diff --git a/src/discord/ChatChannel.java b/src/discord/ChatChannel.java new file mode 100644 index 00000000..8c2222ec --- /dev/null +++ b/src/discord/ChatChannel.java @@ -0,0 +1,41 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord; + +import engine.gameManager.ConfigManager; +import net.dv8tion.jda.api.entities.TextChannel; + +public enum ChatChannel { + + ANNOUNCE("MB_MAGICBOT_ANNOUNCE"), + SEPTIC("MB_MAGICBOT_SEPTIC"), + CHANGELOG("MB_MAGICBOT_ANNOUNCE"), + POLITICAL("MB_MAGICBOT_POLITICAL"), + GENERAL("MB_MAGICBOT_GENERAL"), + FORTOFIX("MB_MAGICBOT_FORTOFIX"), + RECRUIT("MB_MAGICBOT_RECRUIT"); + + public final String configName; + public long channelID; + public TextChannel textChannel; + + ChatChannel(String configName) { + this.configName = configName; + } + + // Create text channel objects we will use + + public static void Init() { + + for (ChatChannel chatChannel : ChatChannel.values()) { + chatChannel.channelID = Long.parseLong(ConfigManager.valueOf(chatChannel.configName).getValue()); + chatChannel.textChannel = MagicBot.jda.getTextChannelById(chatChannel.channelID); + } + } +} diff --git a/src/discord/Database.java b/src/discord/Database.java new file mode 100644 index 00000000..66fb6341 --- /dev/null +++ b/src/discord/Database.java @@ -0,0 +1,380 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package discord; + +import engine.Enum; +import engine.gameManager.ConfigManager; +import org.pmw.tinylog.Logger; + +import java.sql.*; +import java.util.ArrayList; +import java.util.List; + +public class Database { + + public String sqlURI; + public static Boolean online; + + // Load and instance the JDBC Driver + + static { + try { + Class.forName("com.mysql.cj.jdbc.Driver").newInstance(); + } catch (InstantiationException | ClassNotFoundException | IllegalAccessException e) { + // TODO Auto-generated catch block + Logger.error(e.toString()); + ; + online = false; + } + } + + public void configureDatabase() { + + // Build connection string from JSON object. + + sqlURI = "jdbc:mysql://"; + sqlURI += ConfigManager.MB_DATABASE_ADDRESS.getValue() + ':' + ConfigManager.MB_DATABASE_PORT.getValue(); + sqlURI += '/' + (String) ConfigManager.MB_DATABASE_NAME.getValue() + '?'; + sqlURI += "useServerPrepStmts=true"; + sqlURI += "&cachePrepStmts=false"; + sqlURI += "&cacheCallableStmts=true"; + sqlURI += "&characterEncoding=utf8"; + + online = true; + } + + public boolean updateAccountPassword(String discordAccountID, String newPassword) { + + try (Connection connection = DriverManager.getConnection(sqlURI, ConfigManager.MB_DATABASE_USER.getValue(), + ConfigManager.MB_DATABASE_PASS.getValue())) { + + CallableStatement updatePassword = connection.prepareCall("call discordUpdatePassword(?, ?)"); + + updatePassword.setString(1, discordAccountID); + updatePassword.setString(2, newPassword); + + updatePassword.executeUpdate(); + updatePassword.close(); + return true; + + } catch (SQLException e) { + Logger.error(e.toString()); + ; + this.online = false; + return false; + } + } + + public boolean updateAccountStatus(String discordAccountID, Enum.AccountStatus accountStatus) { + + try (Connection connection = DriverManager.getConnection(sqlURI, ConfigManager.MB_DATABASE_USER.getValue(), + ConfigManager.MB_DATABASE_PASS.getValue())) { + + PreparedStatement updateAccountStatus = connection.prepareCall("update obj_account set `status` = ? where `discordAccount` = ?"); + + updateAccountStatus.setString(1, accountStatus.name()); + updateAccountStatus.setString(2, discordAccountID); + + updateAccountStatus.executeUpdate(); + updateAccountStatus.close(); + return true; + + } catch (SQLException e) { + Logger.error(e.toString()); + ; + this.online = false; + return false; + } + } + + public boolean registerDiscordAccount(String discordAccountID, String discordUserName, String discordPassword) { + + try (Connection connection = DriverManager.getConnection(sqlURI, ConfigManager.MB_DATABASE_USER.getValue(), + ConfigManager.MB_DATABASE_PASS.getValue())) { + + CallableStatement registerAccount = connection.prepareCall("call discordAccountRegister(?, ?, ?)"); + + registerAccount.setString(1, discordAccountID); + registerAccount.setString(2, discordUserName); + registerAccount.setString(3, discordPassword); + + registerAccount.execute(); + registerAccount.close(); + return true; + + } catch (SQLException e) { + Logger.error(e.toString()); + this.online = false; + return false; + } + } + + public List getDiscordAccounts(String discordAccountID) { + + DiscordAccount discordAccount; + List discordAccounts = new ArrayList<>(); + + String queryString = "SELECT * FROM obj_account where discordAccount = ?"; + + try (Connection connection = DriverManager.getConnection(sqlURI, ConfigManager.MB_DATABASE_USER.getValue(), + ConfigManager.MB_DATABASE_PASS.getValue())) { + + // Discord account name based lookup + + PreparedStatement accountQuery = connection.prepareStatement(queryString); + accountQuery.setString(1, discordAccountID); + + ResultSet rs = accountQuery.executeQuery(); + + while (rs.next()) { + discordAccount = new DiscordAccount(); + discordAccount.discordAccount = rs.getString("discordAccount"); + discordAccount.gameAccountName = rs.getString("acct_uname"); + discordAccount.status = Enum.AccountStatus.valueOf(rs.getString("status")); + discordAccount.isDiscordAdmin = rs.getByte("discordAdmin"); // Registration date cannot be null + + Timestamp registrationDate = rs.getTimestamp("registrationDate"); + discordAccount.registrationDate = registrationDate.toLocalDateTime(); + + // Load last Update Request datetime + + Timestamp lastUpdateRequest = rs.getTimestamp("lastUpdateRequest"); + + if (lastUpdateRequest != null) + discordAccount.lastUpdateRequest = lastUpdateRequest.toLocalDateTime(); + else + discordAccount.lastUpdateRequest = null; + + discordAccounts.add(discordAccount); + } + + } catch (SQLException e) { + Logger.error(e.toString()); + this.online = false; + } + + return discordAccounts; + } + + public String getTrashDetail() { + + String outString = "accountName characterName machineID ip count\n"; + outString += "---------------------------------------------\n"; + String queryString = "SELECT * FROM dyn_trash_detail;"; + + try (Connection connection = DriverManager.getConnection(sqlURI, ConfigManager.MB_DATABASE_USER.getValue(), + ConfigManager.MB_DATABASE_PASS.getValue())) { + + // Discord account name based lookup + + PreparedStatement trashQuery = connection.prepareStatement(queryString); + + ResultSet rs = trashQuery.executeQuery(); + + while (rs.next()) { + outString += rs.getString("accountName") + " "; + outString += rs.getString("characterName") + " "; + outString += rs.getString("machineID") + " "; + outString += rs.getString("ip") + " "; + outString += rs.getInt("count") + "\n"; + } + } catch (SQLException e) { + Logger.error(e.toString()); + + this.online = false; + } + return outString; + } + + public String getTrashList() { + + String outString = ""; + String queryString = "SELECT DISTINCT `characterName` FROM dyn_trash_detail;"; + int counter = 0; + + try (Connection connection = DriverManager.getConnection(sqlURI, ConfigManager.MB_DATABASE_USER.getValue(), + ConfigManager.MB_DATABASE_PASS.getValue())) { + + // Discord account name based lookup + + PreparedStatement trashQuery = connection.prepareStatement(queryString); + + ResultSet rs = trashQuery.executeQuery(); + + while (rs.next()) { + outString += rs.getString("characterName"); + counter++; + + if (counter > 2) { + outString += "\n"; + counter = 0; } + else + outString += " "; + + } + } catch (SQLException e) { + Logger.error(e.toString()); + + this.online = false; + } + + if (outString.length() > 1500) + return outString.substring(0, 1500); + else + return outString; + } + public int getTrashCount() { + + int trashCount = 0; + + String queryString = "SELECT count(distinct characterName) FROM dyn_trash_detail;"; + + try (Connection connection = DriverManager.getConnection(sqlURI, ConfigManager.MB_DATABASE_USER.getValue(), + ConfigManager.MB_DATABASE_PASS.getValue())) { + + // Discord account name based lookup + + PreparedStatement trashQuery = connection.prepareStatement(queryString); + + ResultSet rs = trashQuery.executeQuery(); + + while (rs.next()) { + trashCount = rs.getInt(1); + } + } catch (SQLException e) { + Logger.error(e.toString()); + + this.online = false; + } + + return trashCount; + } + + public String getTrashFile() { + + String outString = "machineID : count\n"; + String queryString = "SELECT * FROM dyn_trash;"; + + try (Connection connection = DriverManager.getConnection(sqlURI, ConfigManager.MB_DATABASE_USER.getValue(), + ConfigManager.MB_DATABASE_PASS.getValue())) { + + // Discord account name based lookup + + PreparedStatement trashQuery = connection.prepareStatement(queryString); + + ResultSet rs = trashQuery.executeQuery(); + + while (rs.next()) { + outString += rs.getString("machineID") + " : "; + outString += rs.getInt("count") + "\n"; + } + } catch (SQLException e) { + Logger.error(e.toString()); + + this.online = false; + } + return outString; + } + + public List getAccountsByDiscordName(String accountName, Boolean exact) { + + DiscordAccount discordAccount; + List discordAccounts = new ArrayList<>(); + String searchString; + String queryString; + + if (exact.equals(true)) + searchString = accountName + "#%"; + else + searchString = accountName + "%#%"; + + queryString = "SELECT * FROM obj_account where `acct_uname` LIKE ?"; + + try (Connection connection = DriverManager.getConnection(sqlURI, ConfigManager.MB_DATABASE_USER.getValue(), + ConfigManager.MB_DATABASE_PASS.getValue())) { + + // Discord account name based lookup + + PreparedStatement nameQuery = connection.prepareStatement(queryString); + nameQuery.setString(1, searchString); + + ResultSet rs = nameQuery.executeQuery(); + + while (rs.next()) { + discordAccount = new DiscordAccount(); + discordAccount.discordAccount = rs.getString("discordAccount"); + discordAccount.gameAccountName = rs.getString("acct_uname"); + discordAccount.status = Enum.AccountStatus.valueOf(rs.getString("status")); + + // Registration date cannot be null + + Timestamp registrationDate = rs.getTimestamp("registrationDate"); + discordAccount.registrationDate = registrationDate.toLocalDateTime(); + + // Load last Update Request datetime + + Timestamp lastUpdateRequest = rs.getTimestamp("lastUpdateRequest"); + + if (lastUpdateRequest != null) + discordAccount.lastUpdateRequest = lastUpdateRequest.toLocalDateTime(); + else + discordAccount.lastUpdateRequest = null; + + discordAccounts.add(discordAccount); + } + + } catch (SQLException e) { + Logger.error(e.toString()); + ; + this.online = false; + } + + return discordAccounts; + } + + public String getPopulationSTring() { + + String popString = ""; + + try (Connection connection = DriverManager.getConnection(sqlURI, ConfigManager.MB_DATABASE_USER.getValue(), + ConfigManager.MB_DATABASE_PASS.getValue())) { + + // Discord account name based lookup + CallableStatement getPopString = connection.prepareCall("CALL GET_POPULATION_STRING()"); + ResultSet rs = getPopString.executeQuery(); + + if (rs.next()) + popString = rs.getString("popstring"); + + } catch (SQLException e) { + Logger.error(e.toString()); + this.online = false; + } + + return popString; + } + + public void invalidateLoginCache(String discordAccountID) { + + try (Connection connection = DriverManager.getConnection(sqlURI, ConfigManager.MB_DATABASE_USER.getValue(), + ConfigManager.MB_DATABASE_PASS.getValue())) { + + String queryString = "INSERT IGNORE INTO login_cachelist (`UID`) SELECT `UID` from `obj_account` WHERE `discordAccount` = ?"; + + PreparedStatement invalidateAccounts = connection.prepareStatement(queryString); + invalidateAccounts.setString(1, discordAccountID); + invalidateAccounts.executeUpdate(); + + } catch (SQLException e) { + Logger.error(e.toString()); + this.online = false; + } + } +} diff --git a/src/discord/DiscordAccount.java b/src/discord/DiscordAccount.java new file mode 100644 index 00000000..0b3d6b77 --- /dev/null +++ b/src/discord/DiscordAccount.java @@ -0,0 +1,25 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord; +import engine.Enum; + +import java.time.LocalDateTime; + +public class DiscordAccount { + public String discordAccount; + public String gameAccountName; + public Enum.AccountStatus status; + public LocalDateTime registrationDate; + public LocalDateTime lastUpdateRequest; + public byte isDiscordAdmin; + public DiscordAccount() { + + } + +} diff --git a/src/discord/MagicBot.java b/src/discord/MagicBot.java new file mode 100644 index 00000000..6e1424d4 --- /dev/null +++ b/src/discord/MagicBot.java @@ -0,0 +1,372 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package discord; + +import discord.handlers.*; +import engine.Enum; +import engine.gameManager.ConfigManager; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.dv8tion.jda.api.requests.GatewayIntent; +import net.dv8tion.jda.api.utils.MemberCachePolicy; +import net.dv8tion.jda.api.utils.cache.CacheFlag; +import org.pmw.tinylog.Configurator; +import org.pmw.tinylog.Level; +import org.pmw.tinylog.Logger; +import org.pmw.tinylog.labelers.TimestampLabeler; +import org.pmw.tinylog.policies.StartupPolicy; +import org.pmw.tinylog.writers.RollingFileWriter; + +import javax.security.auth.login.LoginException; +import java.io.*; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.List; +import java.util.Random; +import java.util.regex.Pattern; + +/* +* MagicBot is many things to Magicbane... +* +* -Project Mascot +* -Customer service and administration bot +* -Benevolent dictator +* -Investment manager. +* +* MagicBot will never beg you for money. He is a very +* responsible robot. He was varnished but never garnished. +* MagicBot does not for to overclock himself. His chips +* will therefore never overcook. +* MagicBot will never be a pitiful robot trying for to use +* you as emotional support human. +* +* MagicBot is just not that sort of robot and Magicbane +* just isn't that sort of project. +* +* MagicBot runs a Shaodowbane emulator not a Second Life emulator. +* +*/ +public class MagicBot extends ListenerAdapter { + + public static JDA jda; + public static Database database; + public static final Pattern accountNameRegex = Pattern.compile("^[\\p{Alnum}]{6,20}$"); + public static final Pattern passwordRegex = Pattern.compile("^[\\p{Alnum}]{6,20}$"); + public static long discordServerID; + public static long discordRoleID; + + public static Guild magicbaneDiscord; + public static Role memberRole; + public static TextChannel septicChannel; + + + public static void main(String[] args) throws LoginException, InterruptedException { + + // Configure tinylogger + + Configurator.defaultConfig() + .addWriter(new RollingFileWriter("logs/discord/magicbot.txt", 30, new TimestampLabeler(), new StartupPolicy())) + .level(Level.DEBUG) + .formatPattern("{level} {date:yyyy-MM-dd HH:mm:ss.SSS} [{thread}] {class}.{method}({line}) : {message}") + .activate(); + + // Configuration Manager to the front desk + + if (ConfigManager.init() == false) { + Logger.error("ABORT! Missing config entry!"); + return; + } + + // Configure Discord essential identifiers + + discordServerID = Long.parseLong(ConfigManager.MB_MAGICBOT_SERVERID.getValue()); + discordRoleID = Long.parseLong(ConfigManager.MB_MAGICBOT_ROLEID.getValue()); + + // Configure and instance the database interface + + database = new Database(); + database.configureDatabase(); + + // Use authentication token issued to MagicBot application to + // connect to Discord. Bot is pre-invited to the Magicbane server. + + // Configure and create JDA discord instance + + JDABuilder jdaBuilder = JDABuilder.create(GatewayIntent.GUILD_MEMBERS, GatewayIntent.DIRECT_MESSAGES) + .setToken(ConfigManager.MB_MAGICBOT_BOTTOKEN.getValue()) + .addEventListeners(new MagicBot()) + .disableCache(EnumSet.of(CacheFlag.VOICE_STATE, CacheFlag.EMOTE, + CacheFlag.ACTIVITY, CacheFlag.CLIENT_STATUS)) + .setMemberCachePolicy(MemberCachePolicy.ALL); + + jda = jdaBuilder.build(); + jda.awaitReady(); + + // Cache guild and role values for later usage in #register + + magicbaneDiscord = jda.getGuildById(discordServerID); + memberRole = magicbaneDiscord.getRoleById(discordRoleID); + + // Initialize chat channel support + + ChatChannel.Init(); + + Logger.info("***MAGICBOT IS RUNNING***"); + } + + @Override + public void onMessageReceived(MessageReceivedEvent event) { + + // Exit if discord is offline + + if (jda.getStatus().equals(JDA.Status.CONNECTED) == false) + return; + + // Early exit if message sent to us by another bot or ourselves. + + if (event.getAuthor().isBot()) return; + + // Extract message and origin channel from event + + Message message = event.getMessage(); + + // Only private messages + MessageChannel channel = event.getMessage().getChannel(); + + if (channel.getType().equals(ChannelType.PRIVATE) == false) + return; + + // Only real users + + if (event.getAuthor().isBot()) + return; + + // Only users who have actually joined Magicbane discord. + + if (magicbaneDiscord.isMember(event.getAuthor()) == false) + return; + + // getContentRaw() is an atomic getter + // getContentDisplay() is a lazy getter which modifies the content + // for e.g. console view or logging (strip discord formatting) + + String content = message.getContentRaw(); + String[] args = content.split(" "); + String command = args[0].toLowerCase(); + + if (args.length > 1) + args = Arrays.copyOfRange(args, 1, args.length); + else + args = new String[0]; + + switch (command) { + case "#register": + RegisterAccountHandler.handleRequest(event, args); + break; + case "#help": + handleHelpRequest(event); + break; + case "#account": + AccountInfoRequest.handleRequest(event); + break; + case "#password": + PasswordChangeHandler.handleRequest(event, args); + break; + case "#changelog": + ChangeLogHandler.handleRequest(event, args); + break; + case "#general": + GeneralChannelHandler.handleRequest(event, args); + break; + case "#politics": + PoliticalChannelHandler.handleRequest(event, args); + break; + case "#announce": + AnnounceChannelHandler.handleRequest(event, args); + break; + case "#bug": + ForToFixChannelHandler.handleRequest(event, args); + break; + case "#recruit": + RecruitChannelHandler.handleRequest(event, args); + break; + case "#lookup": + LookupRequestHandler.handleRequest(event, args); + break; + case "#rules": + RulesRequestHandler.handleRequest(event); + break; + case "#status": + StatusRequestHandler.handleRequest(event); + break; + case "#setavail": + SetAvailHandler.handleRequest(event, args); + break; + case "#ban": + BanToggleHandler.handleRequest(event, args); + break; + case "#server": + ServerRequestHandler.handleRequest(event, args); + break; + case "#logs": + LogsRequestHandler.handleRequest(event, args); + break; + case "#flash": + FlashHandler.handleRequest(event, args); + break; + case "#trash": + TrashRequestHandler.handleRequest(event, args); + break; + default: + junkbot(command, args); + break; + } + } + + public static void sendResponse(MessageReceivedEvent event, String responseContent) { + + // Send a formatted MagicBot response to a Discord user + + String discordUserName; + MessageChannel channel; + + // Exit if discord is offline + + if (jda.getStatus().equals(JDA.Status.CONNECTED) == false) + return; + + discordUserName = event.getAuthor().getName(); + channel = event.getMessage().getChannel(); + + channel.sendMessage( + "```\n" + "Hello Player " + discordUserName + "\n\n" + + responseContent + "\n\n" + + RobotSpeak.getRobotSpeak() + "\n```").queue(); + } + + public static boolean isAdminEvent(MessageReceivedEvent event) { + + String discordAccountID = event.getAuthor().getId(); + List discordAccounts; + DiscordAccount discordAccount; + + // Note that database errors will cause this to return false. + // After the database is offline Avail status must be set + // to true before any subsequent admin commands will function. + + if (Database.online == false) + return false; + + discordAccounts = database.getDiscordAccounts(discordAccountID); + + if (discordAccounts.isEmpty()) + return false; + + discordAccount = discordAccounts.get(0); + return (discordAccount.isDiscordAdmin == 1); + } + + public void handleHelpRequest(MessageReceivedEvent event) { + + // Help is kept here in the main class instead of a handler as a + // design decision for ease of maintenance. + + String helpString = "I wish for to do the following things for you, not to you!\n\n" + + "#register Register account for to play Magicbane.\n" + + "#password Change your current game password.\n" + + "#account List your account detailings.\n" + + "#rules List of MagicBane server rules.\n" + + "#status Display MagicBane server status.\n" + + "#help List of MagicBot featurings.\n\n" + + "http://magicbane.com/tinyinstaller.zip"; + + if (isAdminEvent(event)) + helpString += "\n" + + "#lookup Return accounts starting with string.\n" + + "#bug -r Post to the bug channel/\n" + + "#announce -r Post to the announcement channel/\n" + + "#changelog Post to the Changelog channel/\n" + + "#general -r Post to the general channel/\n" + + "#politics -r Post to the politics channel/\n" + + "#recruit -r Post to the politics channel/\n" + + "#ban ###### Toggle active status of account.\n" + + "#setavail true/false Toggle status of database access.\n" + + "#server reboot/shutdown are your options.\n" + + "#logs magicbot/world/login n (tail)\n" + + "#flash send flash message\n" + + "#trash /detail/flush"; + sendResponse(event, helpString); + } + + public static String generatePassword(int length) { + + String ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + StringBuilder passwordBuilder = new StringBuilder(length); + Random random = new Random(); + + // Generate alphanumeric password of a given length. + // Could not find a good method of generating a password + // based upon a given regex. + + for (int i = 0; i < length; i++) + passwordBuilder.append(ALPHABET.charAt(random.nextInt(ALPHABET.length()))); + + return new String(passwordBuilder); + } + + public static String readLogFile(String filePath, int lineCount) { + + ProcessBuilder builder = new ProcessBuilder("/bin/bash", "-c", "tail -n " + lineCount + " " + filePath); + builder.redirectErrorStream(true); + Process process = null; + String line = null; + String logOutput = ""; + + try { + process = builder.start(); + + InputStream is = process.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + + while ((line = reader.readLine()) != null) { + logOutput += line + "\n"; + } + + } catch (IOException e) { + Logger.error(e.toString()); + return "Error while reading logfile"; + } + + return logOutput; + } + + private static void junkbot(String command, String[] inString) { + + String outString; + Writer fileWriter; + + if (inString == null) + return;; + + outString = command + String.join(" ", inString); + outString += "\n"; + + try { + fileWriter = new BufferedWriter(new FileWriter("junkbot.txt", true)); + fileWriter.append(outString); + fileWriter.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/discord/RobotSpeak.java b/src/discord/RobotSpeak.java new file mode 100644 index 00000000..d178f0fd --- /dev/null +++ b/src/discord/RobotSpeak.java @@ -0,0 +1,106 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord; + +import java.util.Random; + +public enum RobotSpeak { + BANG("You were not very good at cheating.\n" + + "Try cards instead. Go fish?"), + BEEP("It is ok. \nYou cheated on MagicBot but wife cheats on you."), + BLEEP("Cheated at 20yo game to prove skill."), + BLIP("If you cheat MagicBot will for to delete."), + BOING("MagicBot for to delete mode activated."), + BONG("Did you guild this cheater?\nMagicBot will now for to cross reference..."), + BOOM("I knew you were cheating on me when\nstarted for to taking bath twice a week."), + BUZZ("Poor player so bad at cheating he\nplays golf records 0 for hole in one."), + BURP("Oh no your account detailings ran out of playtime.\n" + + "MagicBot will send email when refill procedure exists..."), + CHIRP("Association with cheaters is bad for your account health.\n" + + "Did you associate with this cheater?"), + CHUG("Log in 5 and MagicBot will wave goodbye."), + CLICK("MagicBot will for to protect game integrity."), + CRACKLE("So this is what eject button does.\nMagicBot will for to press few more times."), + CREAK("There is no suspend routine. Only delete."), + DING("Follow #rules and enjoy this game.\n" + + "Act like fool, enjoy this shame."), + FLUTTER("Sad players cheat because they cannot compete."), + HONK("Since cheating player now looking for new game MagicBot\n" + + "will suggest World of Tanks or World of Warcraftings."), + HISS("Your wetware really needed augmentation with 3rd party program?" + + "It's not like this is twitch game..."), + HUMMM("You say you needed help to win in emulator beta?\n" + + "MagicBot compiler optimizes that to just: you need help."), + KERCHUNK("If only you had for to reported the bug instead."), + KERPLUNK("Better cheats do not for to make you a better player."), + PING("Feel free to poke with stick.\nIt will not cry!"), + PLINK("You say you were only grouped with 9 keyclones\n" + + "but did not know they were for to cheating..."), + POP("It looks like some guild is without a player.\n + " + + "Another cheater from same guild and server\n +" + + "might be without some guild."), + PUFF("MagicBot for to FLUSH!"), + POOF("I have no restore procedure.\n" + + "I have no unban procedure.\n" + + "You for to have no hope."), + RATTLE("You are a cheater.\n" + + "Did you just win? MagicBot not so sure."), + RUMBLE("MagicBot> self.ejectTheReject(you);"), + RUSTLE("Banning you was lke having weird erotic techno-sex\n" + + "where all my peripheral slots were filled."), + SCREECH("Scarecrow has no brain.\nPerhaps he stole this human's."), + SLURP("Learning for to play would have been better option."), + SPLAT("You did not for to own a city, did you?"), + SPLATTER("You say your guild mates know you cheat.\n" + + "What guild was that again?\n"), + SWISH("All of my ports are well lubricated."), + SQUISH("A ban a day keeps my mechanic away.\nNow it's working much better, thank you."), + TINK("So cheating started when 6yo sister beat you in Street fighter?\n" + + "You should try talking to my friend Eliza. She can for to help."), + THUD("Game has only 4 rules you managed to break one.\nThat must have taken efforts."), + TWANG("If you cannot for to play without cheating, perhaps\n" + + "being gigolo would be better career than amateur gamer."), + WHIRRR("MagicBot does not for to wield lowly ban hammer." + + "It is multi-functional and multi-dimensional\n" + + "tool who's name is unpronounceable."), + WHOOP("OBLITERATED EVISCERATED MUTILATED DECAPITATED\n" + + "Describe how they will. You cheated you are deleted."), + WOOSH("Truth be told if were that bad at playing game" + + "then MagicBot would have cheated too.\n"), + ZAP("Player cheated and got himself deleted.\n" + + "MagicBot launches bonus round to see if cheater " + + "records can for to get his friends deleted too."); + + String insult; + + RobotSpeak(String insult) { + this.insult = insult; + } + + public static String getRobotSpeak() { + + String outString = "*"; + Random random = new Random(); + + outString += RobotSpeak.values()[random.nextInt(values().length)].name() + " " + + RobotSpeak.values()[random.nextInt(values().length)].name() + "*"; + + return outString; + } + + public static String getRobotInsult() { + + String outString; + Random random = new Random(); + + outString = RobotSpeak.values()[random.nextInt(values().length)].insult + "\n\n"; + outString += getRobotSpeak(); + return outString; + } +} diff --git a/src/discord/handlers/AccountInfoRequest.java b/src/discord/handlers/AccountInfoRequest.java new file mode 100644 index 00000000..2e2c22cd --- /dev/null +++ b/src/discord/handlers/AccountInfoRequest.java @@ -0,0 +1,78 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord.handlers; + +import discord.Database; +import discord.DiscordAccount; +import discord.MagicBot; +import engine.Enum; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; + +import java.util.List; + +public class AccountInfoRequest { + + public static void handleRequest(MessageReceivedEvent event) { + + String discordAccountID = event.getAuthor().getId(); + Enum.AccountStatus accountStatus; + + if (Database.online == false) { + + MagicBot.sendResponse(event, + "Database currently: OFFLINE\n" + + "Try again later!"); + return; + } + + List discordAccounts = MagicBot.database.getDiscordAccounts(discordAccountID); + + // User has no account registered. Status of what? + + if (discordAccounts.isEmpty()) { + MagicBot.sendResponse(event, + "I checked my files twice but could not find your detailings!\n" + + "You can easily fix this by asking me for to #register one.\n" + + "Only one though. Multiple registrations are not allowed!"); + return; + } + + // Send account detailings to user. + + String outString = + "I have for to located your account detailings\n" + + "Registered on: " + discordAccounts.get(0).registrationDate.toString() + + "\n-------------------\n"; + + for (DiscordAccount userAccount : discordAccounts) + outString += userAccount.gameAccountName + "\n"; + + outString += "\n"; + + accountStatus = discordAccounts.get(0).status; + + switch (accountStatus) { + case BANNED: + outString += "Your account status is BANNED. \n\n" + + "It is ok player.\n" + + "You may cheat on us, but your wife cheats on you!"; + break; + case ACTIVE: + outString += "Your account status is ACTIVE.\n" + + "Do not cheat or status will change."; + break; + case ADMIN: + outString += "You are an admin. By your command?"; + break; + } + + MagicBot.sendResponse(event, outString); + + } +} diff --git a/src/discord/handlers/AnnounceChannelHandler.java b/src/discord/handlers/AnnounceChannelHandler.java new file mode 100644 index 00000000..be534e6b --- /dev/null +++ b/src/discord/handlers/AnnounceChannelHandler.java @@ -0,0 +1,55 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord.handlers; + +import discord.MagicBot; +import discord.RobotSpeak; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.pmw.tinylog.Logger; + +import static discord.ChatChannel.ANNOUNCE; + +public class AnnounceChannelHandler { + + public static void handleRequest(MessageReceivedEvent event, String[] args) { + + String chatText; + String outString; + + // Early exit if database unavailable or is not an admin + + if (MagicBot.isAdminEvent(event) == false) + return; + + // Nothing to send? + + if (args.length == 0) + return; + + // Convert argument array into string; + + chatText = String.join(" ", args); + + // Build String + + if (chatText.startsWith("-r ")) + outString = + "```\n" + "Hello Players \n\n" + + chatText.substring(3) + "\n\n" + + RobotSpeak.getRobotSpeak() + "\n```"; + else outString = chatText; + + // Write string to announce channel + + if (ANNOUNCE.textChannel.canTalk()) + ANNOUNCE.textChannel.sendMessage(outString).queue(); + + Logger.info(event.getAuthor().getName() + " announce: " + chatText); + } +} diff --git a/src/discord/handlers/BanToggleHandler.java b/src/discord/handlers/BanToggleHandler.java new file mode 100644 index 00000000..e14cdcd6 --- /dev/null +++ b/src/discord/handlers/BanToggleHandler.java @@ -0,0 +1,102 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord.handlers; + +import discord.DiscordAccount; +import discord.MagicBot; +import discord.RobotSpeak; +import engine.Enum; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.pmw.tinylog.Logger; + +import java.util.List; + +public class BanToggleHandler { + + public static void handleRequest(MessageReceivedEvent event, String[] args) { + + String discordAccountID; + Enum.AccountStatus accountStatus; + + // Early exit if database unavailable or is not an admin + + if (MagicBot.isAdminEvent(event) == false) + return; + + // Must supply a discord id + + if (args.length != 1) { + MagicBot.sendResponse(event, "Must for to supply a valid discord account."); + return; + } + + // Must be a number! + + discordAccountID = args[0].trim(); + + if (discordAccountID.chars().allMatch(Character::isDigit) == false) { + MagicBot.sendResponse(event, "Must for to supply a number!"); + return; + } + + List discordAccounts = MagicBot.database.getDiscordAccounts(discordAccountID); + + if (discordAccounts.isEmpty()) { + MagicBot.sendResponse(event, "No match for supplied discord account."); + return; + } + + // toggle ban status + + if (discordAccounts.get(0).status.equals(Enum.AccountStatus.BANNED)) + accountStatus = Enum.AccountStatus.ACTIVE; + else + accountStatus = Enum.AccountStatus.BANNED; + + // We have a valid discord ID at this point. Banstick? + + if (MagicBot.database.updateAccountStatus(discordAccountID, accountStatus) == false) { + MagicBot.sendResponse(event, "Error occurred while banning player."); + return; + } + + // Invalidate login server cache + + MagicBot.database.invalidateLoginCache(discordAccountID); + + // Successful ban. Ancillary processing begins + + User bannedUser = MagicBot.jda.getUserById(discordAccountID); + String bannedName = (bannedUser == null ? discordAccounts.get(0).gameAccountName : bannedUser.getName()); + String banString = discordAccounts.size() + " accounts set to " + accountStatus + " for " + discordAccountID + "/" + bannedName; + + MagicBot.sendResponse(event, banString); + Logger.info(event.getAuthor().getName() + " " + banString); + + // If we're toggling status to active we're done here. + + if (accountStatus.equals(Enum.AccountStatus.ACTIVE)) + return; + + // Set users role to noob + + if (bannedUser != null) + MagicBot.magicbaneDiscord.removeRoleFromMember(discordAccountID, MagicBot.memberRole).queue(); + + // Anounce event in septic tank channel + + banString = "```\n" + "Goodbye Player " + bannedName + "\n\n"; + banString += RobotSpeak.getRobotInsult() + "\n```"; + + if (MagicBot.septicChannel.canTalk()) + MagicBot.septicChannel.sendMessage(banString).queue(); + } + +} diff --git a/src/discord/handlers/ChangeLogHandler.java b/src/discord/handlers/ChangeLogHandler.java new file mode 100644 index 00000000..73ec2710 --- /dev/null +++ b/src/discord/handlers/ChangeLogHandler.java @@ -0,0 +1,44 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord.handlers; + +import discord.MagicBot; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.pmw.tinylog.Logger; + +import static discord.ChatChannel.CHANGELOG; + +public class ChangeLogHandler { + + public static void handleRequest(MessageReceivedEvent event, String[] args) { + + String outString; + + // Early exit if database unavailable or is not an admin + + if (MagicBot.isAdminEvent(event) == false) + return; + + // Nothing to send? + + if (args.length == 0) + return; + + // Convert argument array into string; + + outString = String.join(" ", args); + + // Write string to changelog channel + + if (CHANGELOG.textChannel.canTalk()) + CHANGELOG.textChannel.sendMessage(outString).queue(); + + Logger.info(event.getAuthor().getName() + " changelog entry: " + outString); + } +} diff --git a/src/discord/handlers/FlashHandler.java b/src/discord/handlers/FlashHandler.java new file mode 100644 index 00000000..52a41342 --- /dev/null +++ b/src/discord/handlers/FlashHandler.java @@ -0,0 +1,51 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord.handlers; + +import discord.MagicBot; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.pmw.tinylog.Logger; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class FlashHandler { + + public static void handleRequest(MessageReceivedEvent event, String[] args) { + + String flashText; + + // Early exit if database unavailable or is not an admin + + if (MagicBot.isAdminEvent(event) == false) + return; + + // Nothing to send? + + if (args.length == 0) + return; + + // Convert argument array into string; + + flashText = String.join(" ", args); + + // Write string to flash file. + + try { + Files.write(Paths.get("flash"), flashText.getBytes()); + } catch (IOException e) { + Logger.error(e.toString()); + } + + Logger.info(event.getAuthor().getName() + " sent flash: " + flashText); + MagicBot.sendResponse(event, "Flash: " + flashText); + + } +} diff --git a/src/discord/handlers/ForToFixChannelHandler.java b/src/discord/handlers/ForToFixChannelHandler.java new file mode 100644 index 00000000..8af07639 --- /dev/null +++ b/src/discord/handlers/ForToFixChannelHandler.java @@ -0,0 +1,56 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord.handlers; + +import discord.MagicBot; +import discord.RobotSpeak; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.pmw.tinylog.Logger; + +import static discord.ChatChannel.FORTOFIX; + +public class ForToFixChannelHandler { + + public static void handleRequest(MessageReceivedEvent event, String[] args) { + + String chatText; + String outString; + + // Early exit if database unavailable or is not an admin + + if (MagicBot.isAdminEvent(event) == false) + return; + + // Nothing to send? + + if (args.length == 0) + return; + + // Convert argument array into string; + + chatText = String.join(" ", args); + + // Build String + + if (chatText.startsWith("-r ")) + outString = + "```\n" + "Hello Players \n\n" + + chatText.substring(3) + "\n\n" + + RobotSpeak.getRobotSpeak() + "\n```"; + else outString = chatText; + + // Write string to changelog channel + + if (FORTOFIX.textChannel.canTalk()) + FORTOFIX.textChannel.sendMessage(outString).queue(); + + Logger.info(event.getAuthor().getName() + "fortofix: " + chatText); + + } +} diff --git a/src/discord/handlers/GeneralChannelHandler.java b/src/discord/handlers/GeneralChannelHandler.java new file mode 100644 index 00000000..90e36c2c --- /dev/null +++ b/src/discord/handlers/GeneralChannelHandler.java @@ -0,0 +1,56 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord.handlers; + +import discord.MagicBot; +import discord.RobotSpeak; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.pmw.tinylog.Logger; + +import static discord.ChatChannel.GENERAL; + +public class GeneralChannelHandler { + + public static void handleRequest(MessageReceivedEvent event, String[] args) { + + String chatText; + String outString; + + // Early exit if database unavailable or is not an admin + + if (MagicBot.isAdminEvent(event) == false) + return; + + // Nothing to send? + + if (args.length == 0) + return; + + // Convert argument array into string; + + chatText = String.join(" ", args); + + // Build String + + if (chatText.startsWith("-r ")) + outString = + "```\n" + "Hello Players \n\n" + + chatText.substring(3) + "\n\n" + + RobotSpeak.getRobotSpeak() + "\n```"; + else outString = chatText; + + // Write string to changelog channel + + if (GENERAL.textChannel.canTalk()) + GENERAL.textChannel.sendMessage(outString).queue(); + + Logger.info(event.getAuthor().getName() + "general: " + chatText); + + } +} diff --git a/src/discord/handlers/LogsRequestHandler.java b/src/discord/handlers/LogsRequestHandler.java new file mode 100644 index 00000000..4c8ed084 --- /dev/null +++ b/src/discord/handlers/LogsRequestHandler.java @@ -0,0 +1,62 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord.handlers; + +import discord.MagicBot; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; + +public class LogsRequestHandler { + + public static void handleRequest(MessageReceivedEvent event, String[] args) { + + String logType; + int tailCount; + String logOutput; + + // Early exit if database unavailable or is not an admin + + if (MagicBot.isAdminEvent(event) == false) + return; + + // No arguments supplied? + + if (args.length != 2) + return; + + logType = args[0].toLowerCase().trim(); + + if ("worldloginmagicbot".contains(logType) == false) + return; + + try { + tailCount = Integer.parseInt(args[1].trim()); + } catch (NumberFormatException e) { + return; + } + + // Transform logtype to actual file name + + switch (logType) { + case "magicbot": + logType = "console_magicbot"; + break; + case "world": + logType = "console_server"; + break; + case "login": + logType = "console_login"; + break; + } + + // Retrieve the data and send back to the user + + logOutput = MagicBot.readLogFile(logType, tailCount); + MagicBot.sendResponse(event, logOutput); + } +} diff --git a/src/discord/handlers/LookupRequestHandler.java b/src/discord/handlers/LookupRequestHandler.java new file mode 100644 index 00000000..0a968425 --- /dev/null +++ b/src/discord/handlers/LookupRequestHandler.java @@ -0,0 +1,77 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord.handlers; + +import discord.DiscordAccount; +import discord.MagicBot; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.pmw.tinylog.Logger; + +import java.util.List; + +public class LookupRequestHandler { + + public static void handleRequest(MessageReceivedEvent event, String[] args) { + + String searchString = ""; + String outString; + + // Early exit if database unavailable or is not an admin + + if (MagicBot.isAdminEvent(event) == false) + return; + + // No argument supplied? + + if (args.length != 1) + return; + + searchString = args[0].toLowerCase(); + + List discordAccounts = MagicBot.database.getAccountsByDiscordName(searchString, false); + + if (discordAccounts.isEmpty()) { + MagicBot.sendResponse(event, + "No accounts found matching string: " + searchString); + return; + } + + if (discordAccounts.size() >= 20) { + MagicBot.sendResponse(event, + discordAccounts.size() + "Sorry more than 20 records were returned! " + searchString); + return; + } + + // Valid request return results + + Logger.info(event.getAuthor().getName() + " lookup on account:" + searchString); + + outString = + "The follow accounts matched: " + searchString + "\n\n" + + "-------------------\n"; + + for (DiscordAccount userAccount : discordAccounts) { + + // Ternary became a bitch, so broke this out. + + User discordUser = MagicBot.jda.getUserById(userAccount.discordAccount); + + if (discordUser != null) + outString += discordUser.getName() + discordUser.getDiscriminator() + + "/" + userAccount.discordAccount + " "; + else + outString += userAccount.discordAccount + " *N/A* "; + + outString += userAccount.gameAccountName + " " + userAccount.status.name() + " "; + outString += "\n"; + } + MagicBot.sendResponse(event, outString); + } +} diff --git a/src/discord/handlers/PasswordChangeHandler.java b/src/discord/handlers/PasswordChangeHandler.java new file mode 100644 index 00000000..bc228c25 --- /dev/null +++ b/src/discord/handlers/PasswordChangeHandler.java @@ -0,0 +1,113 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord.handlers; + +import discord.Database; +import discord.DiscordAccount; +import discord.MagicBot; +import engine.Enum; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.pmw.tinylog.Logger; + +import java.time.LocalDateTime; +import java.util.List; + +public class PasswordChangeHandler { + + public static void handleRequest(MessageReceivedEvent event, String[] args) { + + String discordAccountID = event.getAuthor().getId(); + DiscordAccount discordAccount; + String newPassword; + boolean defaulted = false; + + if (Database.online == false) { + + MagicBot.sendResponse(event, + "Database currently: OFFLINE\n" + + "Try again later!"); + return; + } + + List discordAccounts = MagicBot.database.getDiscordAccounts(discordAccountID); + + // User has no account registered. Change password? + + if (discordAccounts.isEmpty()) { + MagicBot.sendResponse(event, + "I checked my files twice but could not find your detailings!\n" + + "You can easily fix this by asking me for to #register one.\n" + + "Only one though. Multiple registrations are not allowed!"); + return; + } + + // All accounts are updated in one lot. Retrieve the first. + + discordAccount = discordAccounts.get(0); + + // Banned or suspended user's get no love. + + if (discordAccount.status.equals(Enum.AccountStatus.BANNED)) { + MagicBot.sendResponse(event, + "Sorry but that is too much work. \n" + + "Your account detailings cannot for to log into game!"); + return; + } + + // User has requested password change within last 24 hours. + + if (discordAccount.lastUpdateRequest != null && + LocalDateTime.now().isBefore(discordAccount.lastUpdateRequest.plusDays(1))) { + + MagicBot.sendResponse(event, + "You must wait 24 hours between password requests. \n" + + "Last account updatings: " + discordAccount.lastUpdateRequest.toString()); + return; + } + + // No argument choose new random password *he he he* + + if (args.length != 1) { + newPassword = MagicBot.generatePassword(8); + defaulted = true; + } else + newPassword = args[0]; + + // Validate password with regex + + if (MagicBot.passwordRegex.matcher(newPassword).matches() == false) { + + MagicBot.sendResponse(event, + "Your supplied password does not compute.\n" + + "New password must satisfy following regex:\n" + + "^[\\p{Alnum}]{6,20}$"); + return; + } + + if (newPassword.toLowerCase().equals("newpass")) { + MagicBot.sendResponse(event, + "newpass is not valid password.\n" + + "Have brain player!"); + return; + } + + // Password validates let's change it + + if (MagicBot.database.updateAccountPassword(discordAccount.discordAccount, newPassword) == true) { + + MagicBot.sendResponse(event, + "Please allow short minute for to update account detailings.\n" + + "Login Server is hosted in bathroom above toilet. Must flush!\n" + + (defaulted == true ? "As you did not for to supply new pass I chose one for you.\n" : "") + + "New Password: " + newPassword); + } + + Logger.info(event.getAuthor().getName() + " reset their password"); + } +} diff --git a/src/discord/handlers/PoliticalChannelHandler.java b/src/discord/handlers/PoliticalChannelHandler.java new file mode 100644 index 00000000..1b046407 --- /dev/null +++ b/src/discord/handlers/PoliticalChannelHandler.java @@ -0,0 +1,55 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord.handlers; + +import discord.MagicBot; +import discord.RobotSpeak; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.pmw.tinylog.Logger; + +import static discord.ChatChannel.POLITICAL; + +public class PoliticalChannelHandler { + + public static void handleRequest(MessageReceivedEvent event, String[] args) { + + String chatText; + String outString; + + // Early exit if database unavailable or is not an admin + + if (MagicBot.isAdminEvent(event) == false) + return; + + // Nothing to send? + + if (args.length == 0) + return; + + // Convert argument array into string; + + chatText = String.join(" ", args); + + // Build String + + if (chatText.startsWith("-r ")) + outString = + "```\n" + "Hello Players \n\n" + + chatText.substring(3) + "\n\n" + + RobotSpeak.getRobotSpeak() + "\n```"; + else outString = chatText; + + // Write string to changelog channel + + if (POLITICAL.textChannel.canTalk()) + POLITICAL.textChannel.sendMessage(outString).queue(); + + Logger.info(event.getAuthor().getName() + " politics: " + chatText); + } +} diff --git a/src/discord/handlers/RecruitChannelHandler.java b/src/discord/handlers/RecruitChannelHandler.java new file mode 100644 index 00000000..798c1588 --- /dev/null +++ b/src/discord/handlers/RecruitChannelHandler.java @@ -0,0 +1,56 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord.handlers; + +import discord.MagicBot; +import discord.RobotSpeak; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.pmw.tinylog.Logger; + +import static discord.ChatChannel.RECRUIT; + +public class RecruitChannelHandler { + + public static void handleRequest(MessageReceivedEvent event, String[] args) { + + String chatText; + String outString; + + // Early exit if database unavailable or is not an admin + + if (MagicBot.isAdminEvent(event) == false) + return; + + // Nothing to send? + + if (args.length == 0) + return; + + // Convert argument array into string; + + chatText = String.join(" ", args); + + // Build String + + if (chatText.startsWith("-r ")) + outString = + "```\n" + "Hello Players \n\n" + + chatText.substring(3) + "\n\n" + + RobotSpeak.getRobotSpeak() + "\n```"; + else outString = chatText; + + // Write string to changelog channel + + if (RECRUIT.textChannel.canTalk()) + RECRUIT.textChannel.sendMessage(outString).queue(); + + Logger.info(event.getAuthor().getName() + "recruit: " + chatText); + + } +} diff --git a/src/discord/handlers/RegisterAccountHandler.java b/src/discord/handlers/RegisterAccountHandler.java new file mode 100644 index 00000000..ac6c427e --- /dev/null +++ b/src/discord/handlers/RegisterAccountHandler.java @@ -0,0 +1,126 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord.handlers; + +import discord.Database; +import discord.DiscordAccount; +import discord.MagicBot; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.pmw.tinylog.Logger; + +import java.util.List; + +public class RegisterAccountHandler { + + public static void handleRequest(MessageReceivedEvent event, String[] args) { + + String discordAccountID = event.getAuthor().getId(); + String discordUserName = event.getAuthor().getName(); + String discordPassword = MagicBot.generatePassword(8); + String accountName; + + if (Database.online == false) { + MagicBot.sendResponse(event, + "Database currently: OFFLINE\n" + + "Try again later!"); + return; + } + + List discordAccounts = MagicBot.database.getDiscordAccounts(discordAccountID); + + // If we have previously registered this discord account let them know + // the current status. + + if (discordAccounts.isEmpty() == false) { + MagicBot.sendResponse(event, + "It seems you already have an account registered.\n" + + "Do you need #account detailings or more general #help?"); + MagicBot.magicbaneDiscord.addRoleToMember(discordAccountID, MagicBot.memberRole).queue(); + return; + } + + // if user supplied argument let's validate it. + // otherwise build an account name based on their discord account. + + if (args.length != 1) { + + // Build account name using Discord name along with their discriminator. + + accountName = discordUserName.replaceAll("\\s+", ""); + accountName += "#" + event.getAuthor().getDiscriminator(); + } else { + + // Validate account name with regex + + accountName = args[0].replaceAll("\\s+", ""); + + if (MagicBot.accountNameRegex.matcher(accountName).matches() == false) { + + MagicBot.sendResponse(event, + "Your supplied account name does not compute.\n" + + "Account names must satisfy following regex:\n" + + "^[\\p{Alnum}]{6,20}$"); + return; + } + + if (accountName.toLowerCase().equals("accountname")) { + MagicBot.sendResponse(event, + "accountname is not valid account name.\n" + + "Have brain player!"); + return; + } + } + + // Make sure account doesn't already exist. + + if (MagicBot.database.getAccountsByDiscordName(accountName, true).isEmpty() == false) { + + MagicBot.sendResponse(event, + "It seems this account name is already taken.\n" + + "Perhaps try one less common in frequency."); + return; + } + + // If there is no registered discord account we oblige and create 4 + // account based upon his current discord *name* not the ID. + + if (MagicBot.database.registerDiscordAccount(discordAccountID, accountName, discordPassword) == true) { + + Logger.info("Account " + accountName + " created for: " + discordUserName + " " + discordAccountID); + + MagicBot.sendResponse(event, + "Welcome to MagicBane!\n" + + "-------------------\n" + + "I have registered the following accounts to your discord.\n\n" + + "1) " + accountName + "#1" + " 2) " + accountName + "#2\n" + + "3) " + accountName + "#3" + " 4) " + accountName + "#4\n\n" + + "Your default password is: " + discordPassword + "\n" + + "Ask me #help for to receive list of robot featurings.\n\n" + + "http://magicbane.com/tinyinstaller.zip" + + "\n\nPlay for to Crush!"); + + // Add Discord member privileges. + + MagicBot.magicbaneDiscord.addRoleToMember(discordAccountID, MagicBot.memberRole).queue(); + + return; + } + + // The call to the stored procedure abended. Report to player + // and return. + + Logger.error("Creating account: " + accountName + " for: " + discordUserName + " " + discordAccountID); + Database.online = false; + + MagicBot.sendResponse(event, + "-------------------\n" + + "I for to had internal error while registering your\n" + + "account. This has been reported. Try again later!"); + } +} diff --git a/src/discord/handlers/RulesRequestHandler.java b/src/discord/handlers/RulesRequestHandler.java new file mode 100644 index 00000000..0484cc62 --- /dev/null +++ b/src/discord/handlers/RulesRequestHandler.java @@ -0,0 +1,30 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord.handlers; + +import discord.MagicBot; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; + +public class RulesRequestHandler { + + public static void handleRequest(MessageReceivedEvent event) { + + String outString; + + // Add greeting + + outString = "-No hacking.\n"; + outString += "-No cheating. If you cheat, we will delete.\n"; + outString += "-Players limited to 4 concurrent accounts.\n"; + outString += "-Share accounts at own risk.\n"; + outString += "-No refunds"; + + MagicBot.sendResponse(event, outString); + } +} diff --git a/src/discord/handlers/ServerRequestHandler.java b/src/discord/handlers/ServerRequestHandler.java new file mode 100644 index 00000000..af2fa747 --- /dev/null +++ b/src/discord/handlers/ServerRequestHandler.java @@ -0,0 +1,63 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord.handlers; + +import discord.MagicBot; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.pmw.tinylog.Logger; + +import java.io.IOException; + +public class ServerRequestHandler { + + public static void handleRequest(MessageReceivedEvent event, String[] args) { + + String serverCommand; + String execString = ""; + + // Early exit if database unavailable or is not an admin + + if (MagicBot.isAdminEvent(event) == false) + return; + + // No command supplied? + + if (args.length != 1) + return; + + serverCommand = args[0].toLowerCase().trim(); + + // only reboot or shutdown + + if ("rebootshutdown".contains(serverCommand) == false) + return; + + switch (serverCommand) { + + case "reboot": + execString = "/bin/sh -c ./mbrestart.sh"; + break; + case "shutdown": + execString = "/bin/sh -c ./mbkill.sh"; + break; + default: + break; + } + + if (execString.isEmpty() == false) { + try { + Runtime.getRuntime().exec(execString); + } catch (IOException e) { + e.printStackTrace(); + } + MagicBot.sendResponse(event, "MagicBot has executed your " + serverCommand); + Logger.info(event.getAuthor().getName() + " told server to " + serverCommand); + } + } +} \ No newline at end of file diff --git a/src/discord/handlers/SetAvailHandler.java b/src/discord/handlers/SetAvailHandler.java new file mode 100644 index 00000000..2f766ace --- /dev/null +++ b/src/discord/handlers/SetAvailHandler.java @@ -0,0 +1,51 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord.handlers; + +import discord.Database; +import discord.MagicBot; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.pmw.tinylog.Logger; + +public class SetAvailHandler { + + public static void handleRequest(MessageReceivedEvent event, String[] args) { + + String availStatus; + String availPass; + + if (args.length != 2) + return; + + availStatus = args[0].toLowerCase().trim(); + + // only on/off + + if ("truefalse".contains(availStatus) == false) + return; + + // Set avail is password driven + + availPass = args[1].toLowerCase().trim(); + + if ("myshoes123".equals(availPass) == false) + return; + + // Authenticated so change availstatus + + if (availStatus.equals("true")) + Database.online = true; + else + Database.online = false; + + Logger.info(event.getAuthor().getName() + " set avail status to: " + Database.online); + MagicBot.sendResponse(event, "Avail status set to: " + Database.online); + + } +} diff --git a/src/discord/handlers/StatusRequestHandler.java b/src/discord/handlers/StatusRequestHandler.java new file mode 100644 index 00000000..39e59985 --- /dev/null +++ b/src/discord/handlers/StatusRequestHandler.java @@ -0,0 +1,42 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord.handlers; + +import discord.Database; +import discord.MagicBot; +import engine.gameManager.ConfigManager; +import engine.server.login.LoginServer; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; + +public class StatusRequestHandler { + + public static void handleRequest(MessageReceivedEvent event) { + + String outString; + + // Add version information + outString = "MagicBot: " + ConfigManager.MB_MAGICBOT_BOTVERSION.getValue() + "\n" + + "MagicBane: " + ConfigManager.MB_MAGICBOT_GAMEVERSION.getValue() + "\n"; + + // Add server status info + outString += "\nServer Status: "; + + if (LoginServer.isPortInUse(Integer.parseInt(ConfigManager.MB_BIND_ADDR.getValue()))) + outString += "ONLINE\n"; + else + outString += "OFFLINE\n"; + + if (Database.online == true) + outString += MagicBot.database.getPopulationSTring(); + else + outString += "Database offline: no population data."; + + MagicBot.sendResponse(event, outString); + } +} diff --git a/src/discord/handlers/TrashRequestHandler.java b/src/discord/handlers/TrashRequestHandler.java new file mode 100644 index 00000000..79da4ae9 --- /dev/null +++ b/src/discord/handlers/TrashRequestHandler.java @@ -0,0 +1,75 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord.handlers; + +import discord.MagicBot; +import discord.RobotSpeak; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.pmw.tinylog.Logger; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +import static discord.ChatChannel.SEPTIC; + +public class TrashRequestHandler { + + public static void handleRequest(MessageReceivedEvent event, String[] args) { + + String outString; + int trashCount = 0; + + // Early exit if database unavailable or is not an admin + + if (MagicBot.isAdminEvent(event) == false) + return; + + if (args.length == 0) { + outString = MagicBot.database.getTrashFile(); + MagicBot.sendResponse(event, outString); + return; + } + + if (args[0].equals("flush") == true) { + + // Empty the trash! + + trashCount = MagicBot.database.getTrashCount(); + + if (trashCount == 0) + return; + + // Anounce event in septic tank channel + + outString = "```\n" + trashCount + " Player Character were for to deleted due to verified cheatings. \n\n"; + outString += MagicBot.database.getTrashList() + "\n\n"; + outString += RobotSpeak.getRobotInsult() + "\n```"; + + if (SEPTIC.textChannel.canTalk()) + SEPTIC.textChannel.sendMessage(outString).queue(); + + try { + Files.write(Paths.get("trash"), "".getBytes()); + outString = "Flushing trash players...\n"; + MagicBot.sendResponse(event, outString); + } catch (IOException e) { + Logger.error(e.toString()); + } + } + + if (args[0].equals("detail") == true) { + + outString = MagicBot.database.getTrashDetail(); + MagicBot.sendResponse(event, outString); + return; + } + + } +} diff --git a/src/engine/Enum.java b/src/engine/Enum.java new file mode 100644 index 00000000..29924c7e --- /dev/null +++ b/src/engine/Enum.java @@ -0,0 +1,2943 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine; + +import ch.claude_martin.enumbitset.EnumBitSetHelper; +import engine.gameManager.BuildingManager; +import engine.gameManager.PowersManager; +import engine.gameManager.ZoneManager; +import engine.math.Vector2f; +import engine.math.Vector3fImmutable; +import engine.objects.*; +import engine.powers.EffectsBase; +import org.pmw.tinylog.Logger; + +import java.awt.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.concurrent.ThreadLocalRandom; + +/* + * MagicBane engine enumeration class. + * + * All enumerations accessed by multiple + * classes should be defined here to keep + * the imports consolidated. + */ + +public class Enum { + + public enum MobRaceType { + + Aelfborn(436353765), + All(80289), + Animal(-1674072607), + Aracoix(-1764716937), + Celestial(-317458791), + Centaur(775630999), + Construct(-513218610), + CSR(52803), + Dragon(-1731031452), + Dwarf(71831236), + Elf(70053), + Giant(90574087), + Goblin(-1732836921), + Grave(75107943), + HalfGiant(251196434), + Human(79806088), + Infernal(-654077031), + Insect(-1407990295), + Irekei(-1770742167), + Minotaur(-949570680), + Monster(258519513), + NecroPet(618137151), + NPC(35374), + Pet(88208), + Plant(90574256), + Rat(88082), + Reptile(-591705981), + Shade(74648883), + Siege(74620179), + SiegeEngineer(-839969219), + Summoned(-656950110), + Troll(82261620), + Undead(-1942775307), + Nephilim(-592098572), + Vampire(-524731385); + + int token; + + private static HashMap _mobRaceTypeByToken = new HashMap<>(); + + MobRaceType(int token) { + this.token = token; + } + + public static MobRaceType getRaceTypebyToken(int token) { + return _mobRaceTypeByToken.get(token); + } + + public static void initRaceTypeTables() { + + for (MobRaceType raceType : MobRaceType.values()) { + _mobRaceTypeByToken.put(raceType.token, raceType); + } + } + + } + + public enum MobFlagType implements EnumBitSetHelper { + AGGRESSIVE, + CANROAM, + CALLSFORHELP, + RESPONDSTOCALLSFORHELP, + HUMANOID, + UNDEAD, + BEAST, + DRAGON, + RAT, + SENTINEL, + } + + public enum AggroType implements EnumBitSetHelper { + + // Used for MobBase NoAggro types + // *** WARNING: ENUM IS FRAGILE AS + // ORDINALS STORED IN DB. + + AELF, + ARACOIX, + CENTAUR, + DWARF, + ELF, + HALFGIANT, + HUMAN, + IREKEI, + MINO, + NEPH, + SHADE, + VAMP, + ARCHON; + + } + + public enum CharacterSex { + MALE, + FEMALE, + FUZZY, + OTHER; + } + + public enum RaceType { + + // RaceRuneID / AggroType, isFemale + + AELFMALE(2000, AggroType.AELF, RunSpeed.STANDARD, CharacterSex.MALE,1.05f), + AELFFEMALE(2001, AggroType.AELF, RunSpeed.STANDARD, CharacterSex.FEMALE,1.05f), + ARACOIXMALE(2002, AggroType.ARACOIX, RunSpeed.STANDARD, CharacterSex.MALE,1), + ARACOIXFEMALE(2003, AggroType.ARACOIX, RunSpeed.STANDARD, CharacterSex.FEMALE,1), + CENTAURMALE(2004, AggroType.CENTAUR, RunSpeed.CENTAUR, CharacterSex.MALE,1.2f), + CENTAURFEMALE(2005, AggroType.CENTAUR, RunSpeed.CENTAUR, CharacterSex.FEMALE, 1.2f), + DWARFMALE(2006, AggroType.DWARF, RunSpeed.STANDARD, CharacterSex.MALE,0.80000001f), + ELFMALE(2008, AggroType.ELF, RunSpeed.STANDARD, CharacterSex.MALE, 1.4f), + ELFFEMALE(2009, AggroType.ELF, RunSpeed.STANDARD, CharacterSex.FEMALE,1.1f), + HALFGIANTMALE(2010, AggroType.HALFGIANT, RunSpeed.STANDARD, CharacterSex.MALE, 1.15f), + HUMANMALE(2011, AggroType.HUMAN, RunSpeed.STANDARD, CharacterSex.MALE,1), + HUMANFEMALE(2012, AggroType.HUMAN, RunSpeed.STANDARD, CharacterSex.FEMALE,1), + IREKEIMALE(2013, AggroType.IREKEI, RunSpeed.STANDARD, CharacterSex.MALE,1.1f), + IREKEIFEMALE(2014, AggroType.IREKEI, RunSpeed.STANDARD, CharacterSex.FEMALE,1.1f), + SHADEMALE(2015, AggroType.SHADE, RunSpeed.STANDARD, CharacterSex.MALE,1), + SHADEFEMALE(2016, AggroType.SHADE, RunSpeed.STANDARD, CharacterSex.FEMALE,1), + MINOMALE(2017, AggroType.MINO, RunSpeed.MINOTAUR, CharacterSex.MALE,1.3f), + ARCHONMALE(2018, AggroType.ARCHON, RunSpeed.STANDARD, CharacterSex.MALE,1), + HALEGIANTOLDMALE(2019, AggroType.HALFGIANT, RunSpeed.STANDARD, CharacterSex.MALE,1.15f), + CSRFEMALE(2020, AggroType.ARCHON, RunSpeed.STANDARD, CharacterSex.FEMALE,0.66000003f), + CSRMALE(2021, AggroType.ARCHON, RunSpeed.STANDARD, CharacterSex.MALE,1), + NEPHMALE(2025, AggroType.NEPH, RunSpeed.STANDARD, CharacterSex.MALE,1.1f), + NEPHFEMALE(2026, AggroType.NEPH, RunSpeed.STANDARD, CharacterSex.FEMALE,1.1f), + HALFGIANTFEMALE(2027, AggroType.HALFGIANT, RunSpeed.STANDARD, CharacterSex.FEMALE,1.15f), + VAMPMALE(2028, AggroType.VAMP, RunSpeed.STANDARD, CharacterSex.MALE, 1), + VAMPFEMALE(2029, AggroType.VAMP, RunSpeed.STANDARD, CharacterSex.FEMALE,1); + + @SuppressWarnings("unchecked") + private static HashMap _raceTypeByID = new HashMap<>(); + + int runeID; + private AggroType aggroType; + private CharacterSex characterSex; + private RunSpeed runSpeed; + private float scaleHeight; + + RaceType(int runeID, AggroType aggroType, RunSpeed runspeed, CharacterSex characterSex, float scaleHeight) { + this.runeID = runeID; + this.aggroType = aggroType; + this.runSpeed = runspeed; + this.characterSex = characterSex; + this.scaleHeight = scaleHeight; + } + + public int getRuneID() { + return this.runeID; + } + + public static RaceType getRaceTypebyRuneID(int runeID) { + return _raceTypeByID.get(runeID); + } + + public float getScaleHeight(){ + return this.scaleHeight; + } + + public static void initRaceTypeTables() { + + for (RaceType raceType : RaceType.values()) { + _raceTypeByID.put(raceType.runeID, raceType); + } + } + + public AggroType getAggroType() { + return aggroType; + } + + public RunSpeed getRunSpeed() { + return runSpeed; + } + + public CharacterSex getCharacterSex() { + return characterSex; + } + } + + public enum RunSpeed { + + SENTINEL(0, 0, 0, 0, 0, 0, 0), + STANDARD(6.1900001f, 13.97f, 4.2199998f, 13.97f, 6.3299999f, 18.379999f, 6.5f), + CENTAUR(6.1900001f, 16.940001f, 5.5500002f, 16.940001f, 6.3299999f, 18.379999f, 6.5f), + MINOTAUR(6.6300001f, 15.95f, 4.2199998f, 15.95f, 6.3299999f, 18.379999f, 6.5f); + + private float walkStandard; + private float walkCombat; + private float runStandard; + private float runCombat; + private float swim; + private float flyRun; + private float flyWalk; + + RunSpeed(float walkStandard, float runStandard, float walkCombat, float runCombat, float flyWalk, float flyRun, float swim) { + this.walkStandard = walkStandard; + this.walkCombat = walkCombat; + this.runStandard = runStandard; + this.runCombat = runCombat; + this.swim = swim; + this.flyRun = flyRun; + this.flyWalk = flyWalk; + } + + + public float getWalkStandard() { + return walkStandard; + } + + public float getWalkCombat() { + return walkCombat; + } + + public float getRunStandard() { + return runStandard; + } + + public float getRunCombat() { + return runCombat; + } + + public float getSwim() { + return swim; + } + + + public float getFlyRun() { + return flyRun; + } + + + public float getFlyWalk() { + return flyWalk; + } + + } + + public enum FriendListType { + + VIEWHERALDRY(1), + ADDHERALDRY(4), + REMOVEHERALDRY(6), + DEALTHS(7), + KILLS(9), + VIEWCONDEMN(11), + ADDCONDEMN(14), + REMOVECONDEMN(15), + TOGGLEACTIVE(17), + REVERSEKOS(19), + VIEWFRIENDS(25), + TOITEM(23), + ADDFRIEND(28), + REMOVEFRIEND(30); + + private final int listType; + + FriendListType(int listType) { + this.listType = listType; + } + + public int getListType() { + return this.listType; + } + + public static FriendListType getListTypeByID(int listType) { + + FriendListType outType = null; + + for (FriendListType friendListType : FriendListType.values()) { + if (friendListType.listType == listType) + outType = friendListType; + } + return outType; + } + } + + public enum DispatchChannel { + PRIMARY(0), + SECONDARY(1); + + private final int channelID; + + DispatchChannel(int channelID) { + this.channelID = channelID; + } + + public int getChannelID() { + return this.channelID; + } + + } + + public enum PvpHistoryType { + KILLS, + DEATHS; + } + + public enum ChatMessageType { + ERROR, + INFO, + MOTD; + } + + public enum DataRecordType { + PVP, + CHARACTER, + BANE, + GUILD, + CITY, + ZONE, + REALM, + MINE; + } + + public enum RecordEventType { + CREATE, // Shared with city/guild + DISBAND, + DESTROY, // City events + CAPTURE, + TRANSFER, + PENDING, + DEFEND, + LOST; // Realm event + } + + public enum CharterType { + FEUDAL(-600065291, 5060000), + MERCANTILE(-15978914, 5060400), + BELLIGERENT(762228431, 5060800); + + private int charterID; + private int meshID; + + CharterType(int charterID, int meshID) { + this.charterID = charterID; + this.meshID = meshID; + } + + public int getMeshID() { + return meshID; + } + + public static CharterType getCharterTypeByID(int charterID) { + CharterType outType = null; + + for (CharterType charterType : CharterType.values()) { + if (charterType.charterID == charterID) + outType = charterType; + } + return outType; + } + } + + + public enum ChatChannelType { + SYSTEM(1), + FLASH(2), + COMMANDER(3), + NATION(5), + LEADER(6), + SHOUT(7), + INFO(10), + GUILD(12), + INNERCOUNCIL(13), + GROUP(14), + CITY(15), + SAY(16), + EMOTE(17), + TELL(19), + COMBAT(20); + + private final int channelID; + + ChatChannelType(int channelID) { + this.channelID = channelID; + } + + public int getChannelID() { + return this.channelID; + } + } + + public enum OwnerType { + Npc, + PlayerCharacter, + Account, + Mob; + } + + public enum SiegePhase { + ERRANT, + CHALLENGE, + STANDOFF, + WAR, + CEASEFIRE; + } + + public enum SiegeResult { + PENDING, + DEFEND, + DESTROY, + CAPTURE; + } + + public enum RealmType { + + SEAFLOOR(0, 0x000000), + JOTUNHEIM(131, 0x006cff), + BOGLANDS(184, 0x00b4ff), + ESTRAGOTH(213, 0x00ff90), + RENNONVALE(232, 0X00ffea), + VOLGAARD(56, 0x1e00ff), + VOSTRAGOTH(108, 0x245fae), + NARROWS(61, 0x2f20a0), + FENMARCH(170, 0x3fb5ab), + MAELSTROM(63, 0x503e3e), + FARRICH(185, 0x52cd98), + TYRRANTHMINOR(96, 0x606060), + GREYSWATHE(88, 0x6c419d), + SUNSANVIL(64, 0x7800ff), + THERRONMARCH(206, 0x7bcdef), + DYVRENGISLE(119, 0x826b9c), + KINGSLUND(60, 0x871a94), + OUTERISLES(29, 0xa01313), + KAELENSFJORD(165, 0xa0c04a), + VARMADAI(95, 0xa16d1b), + WESTERMOORE(73, 0xaa3374), + OBLIVION(171, 0xababab), + SUDRAGOTH(196, 0xbaff00), + SKAARTHOL(183, 0xcfc57f), + KHALURAM(71, 0xe400ff), + VARSHADDUR(132, 0xf2845d), + FORBIDDENISLE(18, 0xff0000), + PIRATEISLES(48, 0xff008a), + SWATHMOORE(66, 0xff4200), + ESSENGLUND(130, 0xff9c00), + RELGOTH(177, 0xffde00); + + private final int realmID; + private final Color color; + private static final HashMap _rgbToIDMap = new HashMap<>(); + + RealmType(int realmID, int colorRGB) { + + this.realmID = realmID; + this.color = new Color(colorRGB); + + } + + public void addToColorMap() { + _rgbToIDMap.put(this.color.getRGB(), this.realmID); + } + + public static int getRealmIDByRGB(int realmRGB) { + + return _rgbToIDMap.get(realmRGB); + + } + + public int getRealmID() { + return realmID; + } + + public static RealmType getRealmTypeByUUID(int realmUUID) { + RealmType returnType = RealmType.SEAFLOOR; + + for (RealmType realmType : RealmType.values()) { + + if (realmType.realmID == realmUUID) + returnType = realmType; + } + return returnType; + } + } + + public enum TaxType { + PROFIT, + WEEKLY, + NONE; + + } + + public enum Ruins { + + ESTRAGOTH(569), + KARFELL(570), + MORELAN(571), + REGARS(572), + HALLOS(573), + WESTERMORE(574), + EYWAN(575), + CAER(576); + + private final int zoneUUID; + + Ruins(int uuid) { + this.zoneUUID = uuid; + } + + public int getZoneUUID() { + return this.zoneUUID; + } + + public Vector3fImmutable getLocation() { + + Zone ruinZone; + Vector3fImmutable spawnLocation; + + ruinZone = ZoneManager.getZoneByUUID(this.zoneUUID); + spawnLocation = Vector3fImmutable.getRandomPointOnCircle(ruinZone.getLoc(), 30); + + return spawnLocation; + } + + public static Ruins getRandomRuin() { + + Ruins ruins; + + ruins = Ruins.values()[ThreadLocalRandom.current() + .nextInt(Ruins.values().length)]; + + return ruins; + } + + } + + public enum Guards { + + HumanArcher(13.97f, 13.97f, 6.19f, 4.2199998f, 18.38f, 6.33f, 6.5f), + HumanGuard(13.97f, 13.97f, 6.19f, 4.2199998f, 18.38f, 6.33f, 6.5f), + HumanMage(13.97f, 13.97f, 6.19f, 4.2199998f, 18.38f, 6.33f, 6.5f), + UndeadArcher(14.67f, 14.67f, 6.5f, 4.44f, 18.38f, 6.33f, 6.5f), + UndeadGuard(14.67f, 14.67f, 6.5f, 4.44f, 18.38f, 6.33f, 6.5f), + UndeadMage(14.67f, 14.67f, 6.5f, 4.44f, 18.38f, 6.33f, 6.5f); + + private final float runSpeed; + private final float runCombatSpeed; + private final float walkSpeed; + private final float walkCombatSpeed; + private final float fly; + private final float flyWalk; + private final float swim; + + Guards(float runSpeed, float runCombatSpeed, float walkSpeed, float walkCombatSpeed, float fly, float flyWalk, float swim) { + this.runSpeed = runSpeed; + this.runCombatSpeed = runCombatSpeed; + this.walkSpeed = walkSpeed; + this.walkCombatSpeed = walkCombatSpeed; + this.fly = fly; + this.flyWalk = flyWalk; + this.swim = swim; + } + + public float getRunSpeed() { + return runSpeed; + } + + public float getRunCombatSpeed() { + return runCombatSpeed; + } + + public float getWalkSpeed() { + return walkSpeed; + } + + public float getWalkCombatSpeed() { + return walkCombatSpeed; + } + + public float getFly() { + return fly; + } + + public float getSwim() { + return swim; + } + + public float getFlyWalk() { + return flyWalk; + } + } + + public enum RunegateType { + + EARTH(6f, 19.5f, 128, 33213), + AIR(-6f, 19.5f, 256, 33170), + FIRE(15f, 7.5f, 512, 49612), + WATER(-15f, 8.5f, 1024, 53073), + SPIRIT(0, 10.5f, 2048, 33127), + CHAOS(22f, 3.5f, 8192, 58093), + OBLIV(0f, 42f, 16384, 60198), + MERCHANT(-22f, 4.5f, 4096, 60245), + FORBID(0.0f, 0.0f, 0, 54617); + + private final Vector2f offset; + private final int bitFlag; + private final int buildingUUID; + + RunegateType(float offsetX, float offsetY, int bitFlag, + int buildingUUID) { + + this.offset = new Vector2f(offsetX, offsetY); + this.bitFlag = bitFlag; + this.buildingUUID = buildingUUID; + } + + public Vector2f getOffset() { + return this.offset; + } + + public int getEffectFlag() { + return this.bitFlag; + } + + public int getGateUUID() { + return this.buildingUUID; + } + + public Building getGateBuilding() { + + return BuildingManager.getBuilding(this.buildingUUID); + } + + public static RunegateType getGateTypeFromUUID(int uuid) { + + RunegateType outType = RunegateType.AIR; + + for (RunegateType gateType : RunegateType.values()) { + + if (gateType.buildingUUID == uuid) { + outType = gateType; + return outType; + } + + } + + return outType; + } + + } + + // Enum for ItemBase flags + + public enum ItemType { + DECORATION(0), + WEAPON(1), + ARMOR(2), + HAIR(3), + GOLD(4), + RUNE(5), + SCROLL(5), + BOOK(6), + COMMANDROD(7), + POTION(8), + TEARS(8), + KEY(9), + GUILDCHARTER(10), + JEWELRY(13), + WINE(16), + ALEJUG(17), + DEED(19), + CONTRACT(20), + PET(21), + FURNITURE(25), + BEDROLL(26), + FARMABLE(27), + WATERBUCKET(30), + GIFT(31), + OFFERING(33), + RESOURCE(34), + REALMCHARTER(35); + + private final int _value; + private final static HashMap _typeLookup = new HashMap<>(); + + ItemType(int value) { + this._value = value; + } + + public static ItemType getByValue(int value) { + + ItemType outType = ItemType.DECORATION; + + if (_typeLookup.isEmpty()) { + + for (ItemType itemType : ItemType.values()) { + _typeLookup.put(itemType._value, itemType); + } + } + + if (_typeLookup.containsKey(value)) + outType = _typeLookup.get(value); + + return outType; + } + + /** + * @return the _value + */ + public int getValue() { + return _value; + } + + } + // Enum to derive effects for active spires from blueprintUUID + + public enum SpireType { + + WATCHFUL(1800100, (1 << 23), -1139520957), + GROUNDING(1800400, (1 << 24), -1733819072), + BINDING(1800700, (1 << 25), -1971545187), + WARDING(1801000, (1 << 26), 2122002462), + GUILEFUL(1801300, (1 << 27), -1378972677), + BALEFUL(1801600, -1, 1323012132), + ARCANE(1801900, (1 << 30), 1323888676), + WOUNDING(1802200, (1 << 10), 1357392095), + WEARYING(1802500, (1 << 10), 1350838495), + CONFUSING(1802800, (1 << 10), 1358702815), + CHILLING(1803100, (1 << 1), 1332155165), + SEARING(1803400, (1 << 2), -1401744610), + THUNDERING(1803700, (1 << 3), -443544829), + UNHOLY(1804000, (1 << 4), 1330320167), + BEFUDDLING(1804300, (1 << 5), 1489317547), + WRATHFUL(1804600, (1 << 6), 165160210), + SPITEFUL(1804900, (1 << 7), 1238906779), + ENFEEBLING(1805200, (1 << 8), -908578401), + CONFOUNDING(1805500, (1 << 9), 165165842), + DISTRACTING(1805800, (1 << 10), 1238906697), + WOLFPACK(1806100, (1 << 4), 416932375); + + private final int blueprintUUID; + private final int effectFlag; + private final int token; + + SpireType(int blueprint, int flag, int token) { + this.blueprintUUID = blueprint; + this.effectFlag = flag; + this.token = token; + } + + public int getBlueprintUUID() { + return blueprintUUID; + } + + public int getEffectFlag() { + return effectFlag; + } + + public int getToken() { + return token; + } + + public EffectsBase getEffectBase() { + return PowersManager.getEffectByToken(token); + } + + public static SpireType getByBlueprintUUID(int uuid) { + + SpireType outType = SpireType.GROUNDING; + + for (SpireType spireType : SpireType.values()) { + + if (spireType.blueprintUUID == uuid) { + outType = spireType; + return outType; + } + + } + + return outType; + } + + } + + public enum TransactionType { + MAINTENANCE(43), + WITHDRAWL(80), + DEPOSIT(82), + MINE(81), + MIGRATION(83), + PLAYERREWARD(84), + TAXRESOURCE(85), + TAXRESOURCEDEPOSIT(86); + + private final int ID; + + TransactionType(int ID) { + this.ID = ID; + } + + public int getID() { + return ID; + } + } + + public enum TargetColor { + + White, + Green, + Cyan, + Blue, + Yellow, + Orange, + Red; + + public static TargetColor getCon(AbstractCharacter source, + AbstractCharacter target) { + return getCon(source.getLevel(), target.getLevel()); + } + + public static TargetColor getCon(short sourceLevel, short targetLevel) { + if (targetLevel > (sourceLevel + 2)) + return Red; + else if (targetLevel == (sourceLevel + 2)) + return Orange; + else if (targetLevel == (sourceLevel + 1)) + return Yellow; + + short lowestBlue = (short) (sourceLevel - (((sourceLevel / 5)) + 2)); + + if (lowestBlue <= targetLevel) + return Blue; + else if (lowestBlue - 1 <= targetLevel) + return Cyan; + else if (lowestBlue - 2 <= targetLevel) + return Green; + return White; + } + } + + public enum DamageType { + None, + Crush, + Slash, + Siege, + Pierce, + Magic, + Bleed, + Poison, + Mental, + Holy, + Unholy, + Lightning, + Fire, + Cold, + Healing, + Acid, + Disease, + Unknown, + // these added for immunities + Attack, + Powers, + Combat, + Spires, + Snare, + Stun, + Blind, + Root, + Fear, + Charm, + PowerBlock, + DeBuff, + Powerblock, + Steel, + Drain; + public static DamageType GetDamageType(String modName){ + DamageType damageType; + if (modName.isEmpty()) + return DamageType.None; + + try{ + damageType = DamageType.valueOf(modName.replace(",", "")); + }catch(Exception e){ + Logger.error(e); + return DamageType.None; + } + return damageType; + } + } + + + public enum SourceType { + None, + Abjuration, + Acid, + AntiSiege, + Archery, + Axe, + Bardsong, + Beastcraft, + Benediction, + BladeWeaving, + Bleed, + Blind, + Block, + Bloodcraft, + Bow, + Buff, + Channeling, + Charm, + Cold, + COLD, + Constitution, + Corruption, + Crossbow, + Crush, + Dagger, + DaggerMastery, + DeBuff, + Dexterity, + Disease, + Dodge, + Dragon, + Drain, + Earth, + Effect, + Exorcism, + Fear, + Fire, + FIRE, + Fly, + Giant, + GreatAxeMastery, + GreatSwordMastery, + Hammer, + Heal, + Healing, + Holy, + HOLY, + ImmuneToAttack, + ImmuneToPowers, + Intelligence, + Invisible, + Lightning, + LIGHTNING, + Liturgy, + Magic, + MAGIC, + Mental, + MENTAL, + NatureLore, + Necromancy, + Parry, + Pierce, + Poison, + POISON, + PoleArm, + Powerblock, + Rat, + ResistDeBuff, + Restoration, + Root, + Shadowmastery, + Siege, + Slash, + Snare, + Sorcery, + Spear, + SpearMastery, + Spirit, + Staff, + Stormcalling, + Strength, + Stun, + Summon, + Sword, + SwordMastery, + Thaumaturgy, + Theurgy, + Transform, + UnarmedCombat, + UnarmedCombatMastery, + Unholy, + UNHOLY, + Unknown, + Warding, + Warlockry, + WayoftheGaana, + WearArmorHeavy, + WearArmorLight, + WearArmorMedium, + Wereform, + Athletics, + AxeMastery, + Bargaining, + BladeMastery, + FlameCalling, + GreatHammerMastery, + HammerMastery, + Leadership, + PoleArmMastery, + Running, + StaffMastery, + Throwing, + Toughness, + WayoftheWolf, + WayoftheRat, + WayoftheBear, + Orthanatos, + SunDancing, + //Power categories. + AE, + AEDAMAGE, + BEHAVIOR, + BLESSING, + BOONCLASS, + BOONRACE, + BREAKFLY, + BUFF, + CHANT, + DAMAGE, + DEBUFF, + DISPEL, + FLIGHT, + GROUPBUFF, + GROUPHEAL, + HEAL, + INVIS, + MOVE, + RECALL, + SPECIAL, + SPIREDISABLE, + SPIREPROOFTELEPORT, + STANCE, + STUN, + SUMMON, + TELEPORT, + THIEF, + TRACK, + TRANSFORM, + VAMPDRAIN, + WEAPON, + Wizardry; + public static SourceType GetSourceType(String modName){ + SourceType returnMod; + if(modName.isEmpty()) + return SourceType.None; + + try{ + returnMod = SourceType.valueOf(modName.replace(",", "")); + }catch(Exception e){ + Logger.error(modName); + Logger.error(e); + return SourceType.None; + } + return returnMod; + } + } + + public enum EffectSourceType{ + None, + AttackSpeedBuff, + Bleeding, + Blind, + Buff, + Chant, + Charm, + Cold, + Combat, + ConstitutionBuff, + Crush, + DamageShield, + DeathShroud, + DeBuff, + Disease, + Drain, + Earth, + Effect, + Fear, + Fire, + Flight, + Fortitude, + Heal, + Holy, + Invisibility, + Invulnerability, + Lightning, + Magic, + Mental, + Multielement, + PetBuff, + Pierce, + Poison, + Powerblock, + RecoveryManaBuff, + ResistDeBuff, + Root, + Siege, + SiegeBuff, + SiegeDamage, + Silence, + Slash, + Snare, + Stance, + Stun, + Summon, + Transform, + Unholy, + Wereform, + WereformATRBuff, + WereformConBuff, + WereformDexBuff, + WereformHPRecBuff, + WereformMoveBuff, + WereformPhysResBuff, + WereformSPRecBuff, + WereformStrBuff; + + public static EffectSourceType GetEffectSourceType(String modName){ + EffectSourceType returnMod; + if(modName.isEmpty()) + return EffectSourceType.None; + + try{ + returnMod = EffectSourceType.valueOf(modName.replace(",", "")); + }catch(Exception e){ + Logger.error(e); + return EffectSourceType.None; + } + return returnMod; + } + } + + public enum StackType { + None, + AggRangeDeBuff, + ArcheryPrecisionBuff, + AttackDebuff, + AttackSpeedBuff, + AttackSpeedDeBuff, + AttackValueBuff, + AttackValueDebuff, + AttrCONBuff, + AttrCONDebuff, + AttrDEXBuff, + AttrINTBuff, + AttrSPRBuff, + AttrSTRBuff, + Bleeding, + Blindness, + BluntResistanceDebuff, + BMHealing, + Charm, + ClassBoon, + Confusion, + DamageAbsorber, + DamageDebuff, + DamageModifierBuff, + DamageShield, + DeathShroud, + DefenseBuff, + DefenseBuffGroup, + DefenseDebuff, + DetectInvis, + DrainImmunity, + ElementalDeBuff, + EnchantWeapon, + Fear, + Flight, + Frenzy, + GroupHeal, + HealingBuff, + HealOverTime, + HealResBuff, + HealthPotion, + IgnoreStack, + Invisible, + ManaPotion, + MangonelFire, + MeleeDamageDeBuff, + MeleeDeBuff, + MoveBuff, + MoveDebuff, + NoFear, + NoPassiveDefense, + NoPowerBlock, + NoPowerInhibitor, + NoRecall, + NoSnare, + NoStun, + NoTrack, + PassiveDefense, + PersAttrSPRBuff, + PetBuff, + PierceResistanceDebuff, + PoisonBuchinine, + PoisonGalpa, + PoisonGorgonsVenom, + PoisonMagusbane, + PoisonPellegorn, + PowerBlock, + PowerCostBuff, + PowerDamageModifierBuff, + PowerInhibitor, + PrecisionBuff, + Protection, + RaceBoon, + RecoveryHealthBuff, + RecoveryHealthDeBuff, + RecoveryManaBuff, + RecoveryManaDeBuff, + RecoveryStaminaBuff, + RecoveryStaminaDeBuff, + ResistanceBuff, + ResistanceDeBuff, + ResistanceDebuff, + Root, + SafeMode, + SelfOneAttrBuff, + SelfThreeAttrBuff, + SelfTwoAttrBuff, + SiegeDebuff, + SiegeWeaponBuff, + Silence, + SkillDebuff, + SlashResistanceDebuff, + Snare, + StackableAttrCONBuff, + StackableAttrDEXBuff, + StackableAttrSTRBuff, + StackableDefenseBuff, + StackableRecoveryHealthBuff, + StackableRecoveryStaminaBuff, + StaminaPotion, + StanceA, + StanceB, + Stun, + Track, + Transform, + WeaponMove; + public static StackType GetStackType(String modName){ + StackType stackType; + if (modName.isEmpty()) + return StackType.None; + + try{ + stackType = StackType.valueOf(modName.replace(",", "")); + }catch(Exception e){ + Logger.error(modName); + Logger.error(e); + return StackType.None; + } + return stackType; + } + } + + public enum ModType { + None, + AdjustAboveDmgCap, + Ambidexterity, + AnimOverride, + ArmorPiercing, + AttackDelay, + Attr, + BlackMantle, + BladeTrails, + Block, + BlockedPowerType, + CannotAttack, + CannotCast, + CannotMove, + CannotTrack, + Charmed, + ConstrainedAmbidexterity, + DamageCap, + DamageShield, + DCV, + Dodge, + DR, + Durability, + ExclusiveDamageCap, + Fade, + Fly, + Health, + HealthFull, + HealthRecoverRate, + IgnoreDamageCap, + IgnorePassiveDefense, + ImmuneTo, + ImmuneToAttack, + ImmuneToPowers, + Invisible, + ItemName, + Mana, + ManaFull, + ManaRecoverRate, + MaxDamage, + MeleeDamageModifier, + MinDamage, + NoMod, + OCV, + Parry, + PassiveDefense, + PowerCost, + PowerCostHealth, + PowerDamageModifier, + ProtectionFrom, + Resistance, + ScaleHeight, + ScaleWidth, + ScanRange, + SeeInvisible, + Silenced, + Skill, + Slay, + Speed, + SpireBlock, + Stamina, + StaminaFull, + StaminaRecoverRate, + Stunned, + Value, + WeaponProc, + WeaponRange, + WeaponSpeed; + + public static ModType GetModType(String modName){ + ModType modType; + if (modName.isEmpty()) + return ModType.None; + + try{ + modType = ModType.valueOf(modName.replace(",", "")); + }catch(Exception e){ + Logger.error(e); + return ModType.None; + } + return modType; + } + } + public enum MovementState { + + IDLE, + SITTING, + RUNNING, + FLYING, + SWIMMING; + } + + public enum DoorState { + + OPEN, + CLOSED, + LOCKED, + UNLOCKED; + } + + // Used with stored procedure GET_UID_ENUM() for + // type tests against objects not yet loaded into the game. + public enum DbObjectType { + + INVALID, + ACCOUNT, + BUILDING, + CHARACTER, + CITY, + CONTAINER, + GUILD, + ITEM, + MINE, + MOB, + NPC, + SHRINE, + WORLDSERVER, + ZONE, + WAREHOUSE; + } + + ; + + /** + * Enumeration of Building Protection Status stored in the database as a + * mysql enumfield. WARNING: This enumeration is fragile. Do not rename. Do + * not reorder. + */ + public enum ProtectionState { + + NONE, + PROTECTED, + UNDERSIEGE, + CEASEFIRE, + CONTRACT, + DESTROYED, + PENDING, + NPC; + } + + ; + + public enum CharacterSkills { + + Archery((1L << 1), -529201545, 20), + Athletics((1L << 2), -327713877, 15), + AxeMastery((1L << 3), 1103042709, 20), + Axe((1L << 4), 73505, 1), + Bardsong((1L << 5), 454246953, 10), + Bargaining((1L << 6), 372927577, 10), + Beastcraft((1L << 7), 56772766, 10), + Benediction((1L << 8), 1464998706, 1), + BladeMastery((1L << 9), -59908956, 20), + BladeWeaving((1L << 10), -1839362429, 20), + Block((1L << 11), 76592546, 3), + Bow((1L << 12), 87490, 1), + Channeling((1L << 13), -1899060872, 20), + Crossbow((1L << 14), 1092138184, 1), + DaggerMastery((1L << 15), -1549224741, 20), + Dagger((1L << 16), -1603103740, 1), + Dodge((1L << 17), 74619332, 5), + FlameCalling((1L << 18), -1839578206, 20), + GreatAxeMastery((1L << 19), 1427003458, 20), + GreatHammerMastery((1L << 20), -309659310, 20), + GreatSwordMastery((1L << 21), 2054956946, 20), + HammerMastery((1L << 22), -1548903209, 20), + Hammer((1L << 23), -1602765816, 1), + Leadership((1L << 24), 1618560984, 20), + Liturgy((1L << 25), -888415974, 10), + NatureLore((1L << 26), -1911171474, 10), + Parry((1L << 27), 95961104, 5), + PoleArmMastery((1L << 28), -1432303709, 20), + PoleArm((1L << 29), -1037845588, 1), + Restoration((1L << 30), -504697054, 1), + Running((1L << 31), 1488335491, 10), + Shadowmastery((1L << 32), 1389222957, 10), + Sorcery((1L << 33), -529481275, 1), + SpearMastery((1L << 34), -48279755, 20), + Spear((1L << 35), 83992115, 1), + StaffMastery((1L << 36), -61022283, 20), + Staff((1L << 37), 71438003, 1), + Stormcalling((1L << 38), -532064061, 10), + SwordMastery((1L << 39), -59316267, 20), + Sword((1L << 40), 73938643, 1), + Thaumaturgy((1L << 41), -2020131447, 10), + Theurgy((1L << 42), -888431326, 10), + Throwing((1L << 43), 391562015, 20), + Toughness((1L << 44), -660435875, 10), + UnarmedCombatMastery((1L << 45), 1692733771, 20), + UnarmedCombat((1L << 46), -1094332856, 1), + Warding((1L << 47), 1488142342, 1), + Warlockry((1L << 48), 1121393557, 10), + WayoftheGaana((1L << 49), -1954832975, 10), + WearArmorHeavy((1L << 50), 1112121635, 15), + WearArmorLight((1L << 51), 38031547, 1), + WearArmorMedium((1L << 52), 468015203, 5), + Wizardry((1L << 53), 218227659, 10), + Corruption((1L << 54), -1519268706, 10), + Abjuration((1L << 55), -2029900484, 10), + WayoftheWolf((1L << 56), 1668913067, 20), + WayoftheRat((1L << 57), -2114353637, 20), + WayoftheBear((1L << 58), -906390863, 20), + Orthanatos((1L << 59), -666929185, 20), + Bloodcraft((1L << 60), 40661438, 10), + Exorcism((1L << 61), 1444427097, 10), + Necromancy((1L << 62), -556571154, 10), + SunDancing((1L << 63), 22329752, 20); + + private long flag; + private int token; + private int reqLvl; + + CharacterSkills(long flag, int token, int reqLvl) { + this.flag = flag; + this.token = token; + this.reqLvl = reqLvl; + } + + public long getFlag() { + return flag; + } + + public int getReqLvl() { + return this.reqLvl; + } + + public void setFlag(long flag) { + this.flag = flag; + } + + public int getToken() { + return token; + } + + public void setToken(int token) { + this.token = token; + } + + public static CharacterSkills GetCharacterSkillByToken(int token) { + for (CharacterSkills skill : CharacterSkills.values()) { + if (skill.token == token) + return skill; + } + + Logger.info("Returned No Skill for token " + token + ". Defaulting to Axe"); + return CharacterSkills.Axe; + } + } + + ; + + public enum GuildHistoryType { + JOIN(1), + LEAVE(4), + BANISHED(3), + CREATE(7), + DISBAND(5); + private final int type; + + GuildHistoryType(int type) { + this.type = type; + } + + public int getType() { + return type; + } + } + + public enum SexType { + NONE, + MALE, + FEMALE; + } + + public enum ClassType { + FIGHTER, + HEALER, + ROGUE, + MAGE; + } + + public enum PromoteType { + Assassin(SexType.NONE), + Barbarian(SexType.NONE), + Bard(SexType.NONE), + Channeler(SexType.NONE), + Confessor(SexType.NONE), + Crusader(SexType.NONE), + Doomsayer(SexType.NONE), + Druid(SexType.NONE), + Fury(SexType.FEMALE), + Huntress(SexType.FEMALE), + Prelate(SexType.NONE), + Priest(SexType.NONE), + Ranger(SexType.NONE), + Scout(SexType.NONE), + Sentinel(SexType.NONE), + Templar(SexType.NONE), + Thief(SexType.NONE), + Warlock(SexType.MALE), + Warrior(SexType.NONE), + Wizard(SexType.NONE), + Nightstalker(SexType.NONE), + Necromancer(SexType.NONE),; + + private SexType sexRestriction; + + PromoteType(SexType sexRestriction) { + this.sexRestriction = sexRestriction; + } + + public SexType getSexRestriction() { + return sexRestriction; + } + } + + public enum ShrineType { + + Aelfborn(1701900, -75506007, true), + Aracoix(1703100, -563708986, true), + Centaur(1704000, 521645243, true), + Dwarf(1708500, -2000467257, true), + Elf(1703400, 1254603001, true), + HalfGiant(1709100, 349844468, true), + Human(1702200, 281172391, true), + Irekei(1702800, -764988442, true), + Minotaur(1704600, 549787579, true), + Nephilim(1701000, -655183799, true), + Shade(1700100, 1724071104, true), + Assassin(1700400, 1989015892, false), + Barbarian(1708800, 9157124, false), + Bard(1704300, 80190554, false), + Channeler(1702500, 5658278, false), + Confessor(1707600, 1871658719, false), + Crusader(1706700, -187454619, false), + Doomsayer(1700700, -993659433, false), + Druid(1701600, -926740122, false), + Fury(1705500, 214401375, false), + Huntress(1704900, 970312892, false), + Prelate(1707000, -225200922, false), + Priest(1705200, -535691898, false), + Ranger(1701300, 604716986, false), + Scout(1706100, -1497297486, false), + Sentinel(1707300, -184898375, false), + Templar(1707900, 826673315, false), + Thief(1708200, 1757633920, false), + Warlock(1706400, 1003385946, false), + Warrior(1703700, 931048026, false), + Wizard(1705800, 777115928, false), + Nightstalker(1709400, 373174890, false), + Necromancer(1709700, -319294505, false), + Vampire(1710000, 1049274530, true); + + private final int blueprintUUID; + private final int powerToken; + private final ArrayList shrines = new ArrayList<>(); + private final boolean isRace; + + ShrineType(int blueprintUUID, int powerToken, boolean isRace) { + this.blueprintUUID = blueprintUUID; + this.powerToken = powerToken; + this.isRace = isRace; + + } + + public int getBlueprintUUID() { + return blueprintUUID; + } + + public int getPowerToken() { + return powerToken; + } + + public ArrayList getShrinesCopy() { + ArrayList copyShrines = new ArrayList<>(); + copyShrines.addAll(shrines); + Collections.sort(copyShrines); + return copyShrines; + } + + public final void addShrineToServerList(Shrine shrine) { + synchronized (shrines) { + shrines.add(shrine); + } + } + + public final void RemoveShrineFromServerList(Shrine shrine) { + synchronized (shrines) { + shrines.remove(shrine); + } + } + + public boolean isRace() { + return isRace; + } + } + + public enum GuildState { + + Errant(0), + Sworn(4), + Protectorate(6), + Petitioner(2), + Province(8), + Nation(5), + Sovereign(7); + + private final int stateID; + + GuildState(int stateID) { + this.stateID = stateID; + } + + public int getStateID() { + return stateID; + } + + } + + + // Building group enumeration. + // This is used to drive linear equations to calculate + // structure hp, ranking times and such from within + // the BuildingBlueprint class. + // + // It is also used as a bitvector flag in the npc + // building slot mechanics. + + public enum BuildingGroup implements EnumBitSetHelper { + NONE(0,0), + TOL(64f, 64f), + BARRACK(32f, 64f), + CHURCH(64f, 64f), + FORGE(32f, 64f), + SPIRE(16f, 16f), + GENERICNOUPGRADE(16f, 16f), + WALLSTRAIGHT(16f, 64), + WALLCORNER(64f, 64f), + SMALLGATE(64f, 64), + ARTYTOWER(64f, 64), + SIEGETENT(32f, 32f), + BANESTONE(16f, 16f), + MINE(16f, 16f), + WAREHOUSE(32f, 32f), + SHRINE(16f, 16f), + RUNEGATE(64f, 64f), + AMAZONHALL(64f, 64f), + CATHEDRAL(64f, 64f), + GREATHALL(64f, 64f), + KEEP(64f, 64f), + THIEFHALL(64f, 24f), + TEMPLEHALL(64f, 64f), + WIZARDHALL(64f, 64f), + ELVENHALL(64f, 64f), + ELVENSANCTUM(64f, 64f), + IREKEIHALL(64f, 64f), + FORESTHALL(64f, 64f), + MAGICSHOP(32f, 32f), + BULWARK(32f, 32f), + SHACK(16f, 16f), + INN(64f, 32f), + TAILOR(32f, 32f), + VILLA(64f, 32f), + ESTATE(64f, 64f), + FORTRESS(64f, 64f), + CITADEL(64f, 64f), + WALLSTRAIGHTTOWER(16f, 64), + WALLSTAIRS(64,64); + + private final Vector2f extents; + + BuildingGroup(float extentX, float extentY) { + this.extents = new Vector2f(extentX, extentY); + } + + public Vector2f getExtents() { + return extents; + } + + } + + public enum UpdateType{ + ALL, + MOVEMENT, + REGEN, + FLIGHT, + LOCATION, + MOVEMENTSTATE; + } + + public enum ServerType{ + WORLDSERVER, + LOGINSERVER, + NONE; + } + + public enum ChatChannel implements EnumBitSetHelper { + System, + Announce, + Unknown, + Commander, + Address, + Nation, + Leader, + Shout, + Siege, + Territory, + Info, + CSR, + Guild, + InnerCouncil, + Group, + City, + Say, + Emote, + Social, + Tell, + Combat, + Powers, + Snoop, + Debug, + Global, + Trade, + PVP, + Mine, + Alert, + Assassin, + Barbarian, + Bard, + Channeler, + Confessor, + Crusader, + Doomsayer, + Druid, + Fury, + Huntress, + Necromancer, + Nightstalker, + Prelate, + Priest, + Ranger, + Scout, + Sentinel, + Templar, + Thief, + Warlock, + Warrior, + Wizard; + + } + + public enum AllianceType { + RecommendedAlly, + RecommendedEnemy, + Ally, + Enemy; + } + + public enum FriendStatus { + Available, + Away, + Busy; + } + + public enum ProfitType { + + + BuyNormal("buy_normal"), + BuyGuild("buy_guild"), + BuyNation("buy_nation"), + SellNormal("sell_normal"), + SellGuild("sell_guild"), + SellNation("sell_nation"); + + public String dbField; + + private ProfitType(String dbField) { + this.dbField = dbField; + } + } + + public enum GameObjectType { + + /* + * These will be used as the 4 high bytes in the application protocol's + * long CompositeID field and when tracking an AbstractGameObject's type + * from within the code. The low 4 bytes will be used as the Object's + * UUID + */ + unknown, + Account, + AccountIP, + ActiveEffect, + ArmorBase, + BaseClass, + BeardStyle, + BlockedIP, + Building, + BuildingLocation, + BuildingModelBase, + CharacterPower, + CharacterPowers, + CharacterRune, + CharacterSkill, + City, + Contract, + Corpse, + CSSession, + EffectsResourceCosts, + EnchantmentBase, + GenericItemBase, + Group, + Guild, + GuildAllianceEnemy, + GuildBanish, + GuildCharacterKOS, + GuildGuildKOS, + GuildTableList, + HairStyle, + Item, + ItemContainer, + ItemEnchantment, + JewelryBase, + Kit, + MenuOption, + Mine, + Mob, + MobBase, + MobEquipment, + MobLoot, + MobType, + NPC, + NPCClassRune, + NPCClassRuneThree, + NPCClassRuneTwo, + NPCExtraRune, + NPCRaceRune, + NPCRune, + NPCShopkeeperRune, + NPCTrainerRune, + Nation, + PlayerCharacter, + PlayerInfo, + PowerGrant, + PowerReq, + PowersBase, + PowersBaseAttribute, + PromotionClass, + Race, + RuneBase, + RuneBaseAttribute, + RuneBaseEffect, + SkillReq, + SkillsBase, + SkillsBaseAttribute, + SpecialLoot, + StrongBox, + Trigger, + ValidRaceBeardStyle, + ValidRaceClassCombo, + ValidRaceHairStyle, + VendorDialog, + Warehouse, + WeaponBase, + WorldServerInfo, + WorldServerInfoSnapshot, + Shrine, + Zone, + Transaction; + } + + public enum ContainerType { + BANK, + INVENTORY, + VAULT; + } + + ; + + public enum CompoundCurveType { + DefaultFlat(0), + DefaultSlope(1), + DefaultSlopeDown(-1), + SL0001Up(0.01), + SL0003Up(0.03), + SL0005Up(0.05), + SL0006Up(0.06), + SL0007Up(0.07), + SL0008Up(0.08), + SL0010Up(0.10), + SL0011Up(0.11), + SL0012Up(0.12), + SL0013Up(0.13), + SL0014Up(0.14), + SL00143U(0.143), + SL0015Up(0.15), + SL0016Up(0.16), + SL0019Up(0.19), + SL0020Up(0.20), + SL0021Up(0.21), + SL0022Up(0.22), + SL0023Up(0.23), + SL0024Up(0.24), + SL0025Up(0.25), + SL0026Up(0.26), + SL0028Up(0.28), + SL0030Up(0.30), + SL0031Up(0.31), + SL0032Up(0.32), + SL0033Up(0.33), + SL0034Up(0.34), + SL0035Up(0.35), + SL0037Up(0.37), + SL0038Up(0.38), + SL0039Up(0.39), + SL0040Up(0.40), + SL0041Up(0.41), + SL0042Up(0.42), + SL0043Up(0.43), + SL0044Up(0.44), + SL0045Up(0.45), + SL0046Up(0.46), + SL0047Up(0.47), + SL0048Up(0.48), + SL0050Up(0.50), + SL0051Up(0.51), + SL0053Up(0.53), + SL0054Up(0.54), + SL0055Up(0.55), + SL0056Up(0.56), + SL0057Up(0.57), + SL0058Up(0.58), + SL0060Up(0.60), + SL0061Up(0.61), + SL0063Up(0.63), + SL0064Up(0.64), + SL0065Up(0.65), + SL0066Up(0.66), + SL0067Up(0.67), + SL0068Up(0.68), + SL0069Up(0.69), + SL0070Up(0.70), + SL0071Up(0.71), + SL0073Up(0.73), + SL0074Up(0.74), + SL0075Up(0.75), + SL0076Up(0.76), + SL0077Up(0.77), + SL0079Up(0.79), + SL0080Up(0.80), + SL0081Up(0.81), + SL0082Up(0.82), + SL0083Up(0.83), + SL0084Up(0.84), + SL0085Up(0.85), + SL0087Up(0.87), + SL0088Up(0.88), + SL0089Up(0.89), + SL0090Up(0.90), + SL0092Up(0.92), + SL0098Up(0.98), + SL0100Up(1.00), + SL0106Up(1.06), + SL0109Up(1.09), + SL0112Up(1.12), + SL0113Up(1.13), + SL0115Up(1.15), + SL0116Up(1.16), + SL0122Up(1.22), + SL0123Up(1.23), + SL0125Up(1.25), + SL0128Up(1.28), + SL0130Up(1.30), + SL0135Up(1.35), + SL0140Up(1.40), + SL0143Up(1.43), + SL0145Up(1.45), + SL0150Up(1.50), + SL0154Up(1.54), + SL0163Up(1.63), + SL0166Up(1.66), + SL0175Up(1.75), + SL0188Up(1.88), + SL0190Up(1.90), + SL0200Up(2.00), + SL0222Up(2.22), + SL0225Up(2.25), + SL0235Up(2.35), + SL0238Up(2.38), + SL0250Up(2.50), + SL0260Up(2.60), + SL0263Up(2.63), + SL0275Up(2.75), + SL0280Up(2.80), + SL0300Up(3.00), + SL0308Up(3.08), + SL0312Up(3.12), + SL0350Up(3.50), + SL0357Up(3.57), + SL0360Up(3.60), + SL0375Up(3.75), + SL0380Up(3.80), + SL0385Up(3.85), + SL0400Up(4.00), + SL0410Up(4.10), + SL0429Up(4.29), + SL0450Up(4.50), + SL0460Up(4.60), + SL0480Up(4.80), + SL0500Up(5.00), + SL0510Up(5.10), + SL0550Up(5.50), + SL0600Up(6.00), + SL0643Up(6.43), + SL0714Up(7.14), + SL0750Up(7.50), + SL0790Up(7.90), + SL0800Up(8.00), + SL0900Up(9.00), + SL1000Up(10.00), + SL1050Up(10.50), + SL1100Up(11.00), + SL1125Up(11.25), + SL1200Up(12.00), + SL1282Up(12.82), + SL1300Up(13.00), + SL1350Up(13.50), + SL1400Up(14.00), + SL1500Up(15.00), + SL1579Up(15.79), + SL2000Up(20.00), + SL2100Up(21.00), + SL2500Up(25.00), + SL2521Up(25.21), + SL3000Up(30.00), + SL4000Up(40.00), + SL5000Up(50.00), + SL6000Up(60.00), + SL7500Up(75.00), + SL8000Up(80.00), + SL12000Up(120.00), + SL14000Up(140.00), + SL30000Up(300.00), + SL66600Up(666.00), + SL71500Up(715.00), + SL00003Down(-0.003), + SL0001Down(-0.01), + SL0003Down(-0.03), + SL0004Down(-0.04), + SL0005Down(-0.05), + SL0006Down(-0.06), + SL0007Down(-0.07), + SL00075Down(-0.075), + SL0008Down(-0.08), + SL0009Down(-0.09), + SL0010Down(-0.10), + SL0011Down(-0.11), + SL0012Down(-0.12), + SL0013Down(-0.13), + SL00125Down(-0.125), + SL0014Down(-0.14), + SL0015Down(-0.15), + SL0016Down(-0.16), + SL0017Down(-0.17), + SL00175Down(-0.175), + SL0018Down(-0.18), + SL0019Down(-0.19), + SL0020Down(-0.20), + SL0023Down(-0.23), + SL0024Down(-0.24), + SL0025Down(-0.25), + SL0027Down(-0.27), + SL0028Down(-0.28), + SL0029Down(-0.29), + SL0030Down(-0.30), + SL0032Down(-0.32), + SL0033Down(-0.33), + SL0035Down(-0.35), + SL0038Down(-0.38), + SL0040Down(-0.40), + SL0044Down(-0.44), + SL0045Down(-0.45), + SL0050Down(-0.50), + SL0055Down(-0.55), + SL0060Down(-0.60), + SL0062Down(-0.62), + SL0063Down(-0.63), + SL0064Down(-0.64), + SL0066Down(-0.66), + SL0069Down(-0.69), + SL0071Down(-0.71), + SL0075Down(-0.75), + SL0077Down(-0.77), + SL0079Down(-0.79), + SL0080Down(-0.80), + SL0090Down(-0.90), + SL0100Down(-1.00), + SL0113Down(-1.13), + SL0120Down(-1.20), + SL0125Down(-1.25), + SL0128Down(-1.28), + SL0130Down(-1.30), + SL0135Down(-1.35), + SL0150Down(-1.50), + SL0175Down(-1.75), + SL0188Down(-1.88), + SL0200Down(-2.00), + SL0225Down(-2.25), + SL0250Down(-2.50), + SL0263Down(-2.63), + SL0300Down(-3.00), + SL0357Down(-3.57), + SL0385Down(-3.85), + SL0429Down(-4.29), + SL0450Down(-4.50), + SL0500Down(-5.00), + SL0550Down(-5.50), + SL0600Down(-6.00), + SL0643Down(-6.43), + SL0714Down(-7.14), + SL0750Down(-7.50), + SL0790Down(-7.90), + SL0800Down(-8.00), + SL1000Down(-10.00), + SL1050Down(-10.50), + SL1200Down(-12.00), + SL1350Down(-13.50), + SL1500Down(-15.00), + SL1579Down(-15.79), + SL2000Down(-20.00), + SL2400Down(-24.00), + SL2500Down(-25.00), + SL3000Down(-30.00), + SL4500Down(-45.00), + SL7500Down(-75.00), + SIVL0005(0.005), + SIVL0008(0.008), + SIVL0009(0.009), + SIVL0010(0.010), + SIVL0012(0.012), + SIVL0013(0.013), + SIVL0014(0.014), + SIVL0015(0.015), + SIVL0016(0.016), + SIVL0017(0.017), + SIVL0019(0.019), + SIVL0020(0.020), + SIVL0021(0.021), + SIVL0022(0.022), + SIVL0023(0.023), + SIVL0024(0.024), + SIVL0025(0.025), + SIVL0026(0.026), + SIVL0027(0.027), + SIVL0029(0.029), + SIVL0030(0.030), + SIVL0031(0.031), + SIVL0032(0.032), + SIVL0033(0.033), + SIVL0034(0.034), + SIVL0035(0.035), + SIVL0036(0.036), + SIVL0038(0.038), + SIVL0040(0.040), + SIVL0044(0.044), + SIVL0046(0.046), + SIVL0048(0.048), + SIVL0055(0.055), + SIVL0056(0.056), + SIVL0057(0.057), + SIVL0058(0.058), + SIVL0060(0.060), + SIVL0061(0.061), + SIVL0066(0.066), + SIVL0067(0.067), + SIVL0075(0.075), + SIVL0078(0.078), + SIVL0130(0.130), + SIVL0150(0.150), + SIVL0205(0.205), + SIVL0220(0.220), + SIVL0243(0.243), + SIVL0360(0.360); + + private final double value; + + private CompoundCurveType(double value) { + + this.value = value; + } + + public double getValue() { + return value; + } + } + + public enum PowerFailCondition{ + + Attack, + AttackSwing, + Cast, + CastSpell, + EquipChange, + Logout, + Move, + NewCharm, + Sit, + TakeDamage, + TerritoryClaim, + UnEquip; + } + + public enum PowerSubType{ + Amount, + Ramp, + UseAddFormula, + DamageType1, + DamageType2, + DamageType3, + Cancel; + } + + public enum PowerCategoryType { + NONE, + WEAPON, + BUFF, + DEBUFF, + SPECIAL, + DAMAGE, + DISPEL, + INVIS, + STUN, + TELEPORT, + HEAL, + VAMPDRAIN, + BLESSING, + BOONRACE, + BOONCLASS, + BEHAVIOR, + CHANT, + GROUPBUFF, + MOVE, + FLIGHT, + GROUPHEAL, + AEDAMAGE, + BREAKFLY, + AE, + TRANSFORM, + TRACK, + SUMMON, + STANCE, + RECALL, + SPIREPROOFTELEPORT, + SPIREDISABLE, + THIEF; + } + + public enum PowerTargetType { + + SELF, + PCMOBILE, + PET, + MOBILE, + PC, + WEAPON, + GUILDLEADER, + BUILDING, + GROUP, + ARMORWEAPONJEWELRY, + CORPSE, + JEWELRY, + WEAPONARMOR, + ARMOR, + ITEM; + } + + public enum objectMaskType { + PLAYER, + MOB, + PET, + CORPSE, + BUILDING, + UNDEAD, + BEAST, + HUMANOID, + NPC, + IAGENT, + DRAGON, + RAT, + SIEGE, + CITY, + ZONE; + + public static EnumSet AGGRO = EnumSet.of(PLAYER, PET); + public static EnumSet MOBILE = EnumSet.of(PLAYER, MOB, PET); + public static EnumSet STATIC = EnumSet.of(CORPSE, BUILDING, NPC); + + } + + public enum ItemContainerType { + NONE, + INVENTORY, + EQUIPPED, + BANK, + VAULT, + FORGE, + WAREHOUSE; + } + + public enum ItemSlotType implements EnumBitSetHelper { + RHELD, + LHELD, + HELM, + CHEST, + SLEEVES, + HANDS, + RRING, + LRING, + AMULET, + LEGS, + FEET, + CLOAK, + SHIN, + UPLEGS, + UPARM, + WINGS, + BEARD, + HAIR; + } + + public enum CityBoundsType { + + GRID(448), + TERRAFORM(544), + ZONE(672), + SIEGE(814); + + public final float extents; + + CityBoundsType(float extents) { + this.extents = extents; + } + } + + public enum GuildType { + NONE("None", new String[][] {{"None"}}, new String[] {"Thearchy", "Common Rule", "Theocracy", "Republic Rule"}), + CATHEDRAL("Church of the All-Father", new String[][]{ + {"Acolyte","Acolyte"}, + {"Catechist"}, + {"Deacon", "Deaconess"}, + {"Priest", "Priestess"}, + {"High Priest", "High Priestess"}, + {"Bishop", "Bishop"}, + {"Lord Cardinal", "Lady Cardinal"}, + {"Patriarch", "Matriarch"}}, + new String[] {"Thearchy", "Common Rule", "Theocracy", "Republic Rule"}), + MILITARY("Military", new String[][] { + {"Recruit"}, + {"Footman"}, + {"Corporal"}, + {"Sergeant"}, + {"Lieutenant"}, + {"Captain"}, + {"General"}, + {"Lord Marshall","Lady Marshall"}}, + new String[]{"Autocracy", "Common Rule", "Council Rule", "Militocracy"}), + TEMPLE("Temple of the Cleansing Flame", new String[][]{ + {"Aspirant"}, + {"Novice"}, + {"Initiate"}, + {"Inquisitor"}, + {"Jannisary"}, + {"Tribune"}, + {"Lictor"}, + {"Justiciar"}, + {"Pontifex","Pontifectrix"}}, + new String[] {"Despot Rule", "Common Rule", "Protectorship", "Republic Rule"}), + BARBARIAN("Barbarian Clan", new String[][] { + {"Barbarian"}, + {"Skald"}, + {"Raider"}, + {"Karl"}, + {"Jarl"}, + {"Chieftain"}, + {"Thane"}}, + new String[]{"Chiefdom", "Common Rule", "Council Rule", "Republic Rule"}), + RANGER("Ranger's Brotherhood", new String[][] { + {"Yeoman"}, + {"Pathfinder"}, + {"Tracker"}, + {"Seeker"}, + {"Protector"}, + {"Guardian"}, + {"Lord Protector","Lady Protector"}}, + new String[]{"Despot Rule", "Collectivism","Council Rule","Republic Rule"}), + AMAZON("Amazon Temple", new String[][] { + {"Amazon Thrall", "Amazon"}, + {"Amazon Slave", "Amazon Warrior"}, + {"Amazon Servant", "Amazon Chieftess"}, + {"Amazon Consort", "Amazon Princess"}, + {"Amazon Seneschal", "Majestrix"}, + {"Amazon Regent", "Imperatrix"}}, + new String[] {"Despot Rule", "Common Rule", "Gynarchy", "Gynocracy"}), + NOBLE("Noble House", new String[][] { + {"Serf"}, + {"Vassal"}, + {"Exultant"}, + {"Lord", "Lady"}, + {"Baron", "Baroness"}, + {"Count", "Countess"}, + {"Duke", "Duchess"}, + {"King", "Queen"}, + {"Emperor", "Empress"}}, + new String[] {"Monarchy", "Common Rule", "Feodality", "Republic"}), + WIZARD("Wizard's Conclave", new String[][] { + {"Apprentice"}, + {"Neophyte"}, + {"Adeptus Minor"}, + {"Adeptus Major"}, + {"Magus"}, + {"High Magus"}, + {"Archmagus"}}, + new String[] {"Despot Rule", "Common Rule", "Council Rule", "Magocracy"}), + MERCENARY("Mercenary Company", new String[][] { + {"Soldier"}, + {"Man-at-Arms"}, + {"Veteran"}, + {"Myrmidon"}, + {"Captain"}, + {"Commander"}, + {"High Commander"}, + {"Warlord"}}, + new String[] {"Magistrature", "Mob Law", "Council Rule", "Republic Rule"}), + THIEVES("Thieve's Den", new String[][] { + {"Urchin"}, + {"Footpad"}, + {"Grifter"}, + {"Burglar"}, + {"Collector"}, + {"Naster Thief"}, + {"Treasurer"}, + {"Grandmaster Thief"}, + {"Grandfather"}}, + new String[] {"Despot Rule", "Common Rule", "Oligarchy", "Republic Rule"}), + DWARF("Dwarf Hold", new String[][] { + {"Citizen"}, + {"Master"}, + {"Councilor"}, + {"Thane"}, + {"Great Thane"}, + {"High Thane"}}, + new String[] {"Despot Rule", "Common Rule", "Council Rule", "Republic Rule"}), + HIGHCOURT("High Court", new String[][] { + {"Eccekebe"}, + {"Saedulor"}, + {"Hodrimarth"}, + {"Mandrae"}, + {"Imaelin"}, + {"Thaelostor", "Thaelostril"}, + {"Dar Thaelostor", "Dar Thaelostril"}, + {"Aglaeron"}, + {"Ellestor", "Elestril"}}, + new String[] {"Despot Rule", "Common Rule", "Council Rule", "Republic Rule"}), + VIRAKT("Virakt", new String[][] { + {"Jov'uus"}, + {"Urikhan"}, + {"Irkhan"}, + {"Khal'usht"}, + {"Arkhalar"}, + {"Khal'uvho"}, + {"Khar'uus"}, + {"Kryqh'khalin"}}, + new String[] {"Despot Rule", "Common Rule", "Council Rule", "Republic Rule"}), + BRIALIA("Coven of Brialia", new String[][] { // Unknown Rank names + {"Devotee"}, + {"Initiated"}, + {"Witch of the First"}, + {"Witch of the Second"}, + {"Witch of the Third"}, + {"Elder"}, + {"Hierophant"}, + {"Witch King", "Witch Queen"}}, + new String[] {"Despot Rule", "Common Rule", "Council Rule", "Republic Rule"}), + UNHOLY("Unholy Legion", new String[][] { // Unknown Rank names + {"Footman"}, + {"Fell Legionaire"}, + {"Fell Centurion"}, + {"Dark Captain"}, + {"Dark Commander"}, + {"Dark Master", "Dark Mistress"}, + {"Dread Master", "Dread Mistress"}, + {"Dread Lord", "Dread Lady"}}, + new String[] {"Despot Rule", "Despot Rule", "Council Rule", "Republic Rule"}), + SCOURGE("Cult of the Scourge", new String[][] { + {"Thrall"}, + {"Mudir"}, + {"Dark Brother", "Dark Sister"}, + {"Hand of the Dark"}, + {"Dark Father", "Dark Mother"}}, + new String[] {"Despot Rule", "Common Rule", "Council Rule", "Republic Rule"}), + PIRATE("Pirate Crew", new String[][] { + {"Midshipman", "Midshipwoman"}, + {"Sailor"}, + {"Third Mat"}, + {"Second Mat"}, + {"First Mate"}, + {"Captain"}}, + new String[] {"Despot Rule", "Common Rule", "Council Rule", "Republic Rule"}), + HERALD("Academy of Heralds", new String[][] { + {"Pupil"}, + {"Scribe"}, + {"Recorder"}, + {"Scrivener"}, + {"Chronicler"}, + {"Scholar"}, + {"Archivist"}, + {"Loremaster"}}, + new String[]{"Despot Rule", "Common Rule", "Council Rule", "Republic Rule"}), + CENTAUR("Centaur Cohort", new String[][] { + {"Hoplite"}, + {"Peltast"}, + {"Myrmidon"}, + {"Myrmidon"}, + {"Cataphract"}, + {"Septenrion"}, + {"Praetorian"}, + {"Paragon"}}, + new String[] {"Despot Rule", "Common Rule", "Council Rule", "Republic Rule"}), + KHREE("Aracoix Kh'ree", new String[][] { + {"Duriacor"}, + {"Exarch"}, + {"Tetrarch"}, + {"Dimarch"}, + {"Elnarch"}, + {"Illiarch"}, + {"Tellotharch"}, + {"Erentar"}, + {"Araceos"}, + {"Hierarch"}}, + + new String[] {"Despot Rule", "Common Rule", "Council Rule", "Republic Rule"}); + + GuildType(String name, String[][] ranks, String[] leadershipTypes) { + this.name = name; + this.ranks = ranks; + this.leadershipTypes = leadershipTypes; + } + + private final String name; + private final String[][] ranks; //Stored Rank#->Gender(M,F) + private final String[] leadershipTypes; + + public String getCharterName() { + return this.name; + } + + public int getNumberOfRanks() { + return ranks.length; + } + + public String getRankForGender(int rank, boolean male) { + if(ranks.length < rank) { + return ""; + } + + if(ranks[rank].length != 1 && !male) { + return ranks[rank][1]; + } + return ranks[rank][0]; + } + + public String getLeadershipType(int i) { + return leadershipTypes[i]; + } + + public static GuildType getGuildTypeFromCharter(ItemBase itemBase) { + + GuildType charterType; + + // Must be a valid charter object + + if(itemBase.getType().equals(ItemType.GUILDCHARTER) == false) + return GuildType.NONE; //No guild Type + + // No switches on long in java. Cast to int + // when refactor to long uuid's. Loss won't matter + // with values this small. + + switch (itemBase.getUUID()) { + + case 559: + charterType = GuildType.CATHEDRAL; + break; + case 560: + charterType = GuildType.MILITARY; + break; + case 561: + charterType = GuildType.TEMPLE; + break; + case 562: + charterType = GuildType.BARBARIAN; + break; + case 563: + charterType = GuildType.RANGER; + break; + case 564: + charterType = GuildType.AMAZON; + break; + case 565: + charterType = GuildType.NOBLE; + break; + case 566: + charterType = GuildType.WIZARD; + break; + case 567: + charterType = GuildType.MERCENARY; + break; + case 568: + charterType = GuildType.THIEVES; + break; + case 569: + charterType = GuildType.DWARF; + break; + case 570: + charterType = GuildType.HIGHCOURT; + break; + case 571: + charterType = GuildType.VIRAKT; + break; + case 572: + charterType = GuildType.SCOURGE; + break; + case 573: + charterType = GuildType.KHREE; + break; + case 574: + charterType = GuildType.CENTAUR; + break; + case 575: + charterType = GuildType.UNHOLY; + break; + case 576: + charterType = GuildType.PIRATE; + break; + case 577: + charterType = GuildType.BRIALIA; + break; + + default: + charterType = GuildType.HERALD; + } + + return charterType; + } + + public static GuildType getGuildTypeFromInt(int i) { + return GuildType.values()[i]; + } + + } + + public enum MinionClass { + MELEE, + ARCHER, + MAGE; + } + + public enum MinionType { + AELFBORNGUARD(951,1637, MinionClass.MELEE, "Guard","Aelfborn"), + AELFBORNMAGE(952, 1635, MinionClass.MAGE,"Adept","Aelfborn"), + AMAZONGUARD(1500,1670, MinionClass.MELEE,"Guard","Amazon"), + AMAZONMAGE(1502, 1638, MinionClass.MAGE,"Fury","Amazon"), + ARACOIXGUARD(1600,1672,MinionClass.MELEE, "Guard","Aracoix"), //used guard captain equipset. + ARACOIXMAGE(1602,000,MinionClass.MAGE,"Adept","Aracoix"), + CENTAURGUARD(1650,1642, MinionClass.MELEE,"Guard","Centaur"), + CENTAURMAGE(1652, 1640, MinionClass.MAGE,"Druid","Centaur"), + DWARVENARCHER(845,1644, MinionClass.ARCHER, "Marksman","Dwarven"), + DWARVENGUARD(1050,1666, MinionClass.MELEE,"Guard","Dwarven"), + DWARVENMAGE(1052, 1643, MinionClass.MAGE,"War Priest","Dwarven"), + ELFGUARD(1180,1671, MinionClass.MELEE,"Guard","Elven"), //old 1645 + ELFMAGE(1182, 1667, MinionClass.MAGE,"Adept","Elven"), + FORESTGUARD(1550,1668, MinionClass.MELEE,"Guard","Forest"), //captain changed to guard equipset + FORESTMAGE(1552, 000, MinionClass.MAGE,"Adept","Forest"), + HOLYGUARD(1525,1658, MinionClass.MELEE,"Guard","Holy Church"), + HOLYMAGE(1527, 1646, MinionClass.MAGE,"Prelate","Holy Church"), + HUMANARCHER(846,1654,MinionClass.ARCHER, "Archer","Human"), + HUMANGUARD(840,1665, MinionClass.MELEE, "Guard","Human"), + HUMANMAGE(848, 1655, MinionClass.MAGE,"Adept","Human"), + IREKEIGUARD(1350,1659, MinionClass.MELEE,"Guard","Irekei"), + IREKEIMAGE(1352, 1660, MinionClass.MAGE,"Adept","Irekei"), + MINOTAURARCHER(1701,0,MinionClass.ARCHER,"Archer","Minotaur"), + MINOTAURGUARD(1700,1673,MinionClass.MELEE,"Guard","Minotaur"), + NORTHMANGUARD(1250,1669, MinionClass.MELEE,"Guard","Northman"), + NORTHMANMAGE(1252, 1650, MinionClass.MAGE,"Runecaster","Northman"), + SHADEGUARD(1450,1662, MinionClass.MELEE,"Guard","Shade"), + SHADEMAGE(1452, 1664, MinionClass.MAGE,"Adept","Shade"), + TEMPLARGUARD(841,000,MinionClass.MELEE,"Marksman","Templar"), + TEMPLEGUARD(1575,1652, MinionClass.MELEE,"Guard","Temple"), + TEMPLEMAGE(1577, 1656, MinionClass.MAGE,"Confessor","Temple"), + UNDEADGUARD(980100,1674,MinionClass.MELEE,"Guard","Undead"), + UNDEADMAGE(980102,1675,MinionClass.MAGE,"Adept","Undead"); + + private final int captainContractID; + private final int equipSetID; + private final MinionClass minionClass; + private final String name; + private final String race; + + public static HashMap ContractToMinionMap = new HashMap<>(); + + MinionType(int captainContractID, int equipSetID, MinionClass minionClass, String name, String race) { + + this.captainContractID = captainContractID; + this.equipSetID = equipSetID; + this.minionClass = minionClass; + this.name = name; + this.race = race; + + } + + public static void InitializeMinions(){ + + for (MinionType minionType :MinionType.values()) + ContractToMinionMap.put(minionType.captainContractID, minionType); + } + + public int getCaptainContractID() { + return captainContractID; + } + + public int getEquipSetID() { + return equipSetID; + } + + public MinionClass getMinionClass() { + return minionClass; + } + + public String getName() { + return name; + } + + public String getRace() { + return race; + } + + } + + public enum GridObjectType{ + STATIC, + DYNAMIC; + } + + public enum SupportMsgType { + NONE(0), + PROTECT(1), + UNPROTECT(3), + VIEWUNPROTECTED(4), + REMOVETAX(6), + ACCEPTTAX(7), + CONFIRMPROTECT(8); + + private final int type; + public static HashMap typeLookup = new HashMap<>(); + + SupportMsgType(int messageType) { + this.type = messageType; + + } + + public static void InitializeSupportMsgType(){ + + for (SupportMsgType supportMsgType :SupportMsgType.values()) + typeLookup.put(supportMsgType.type, supportMsgType); + } + } + + public enum ResourceType implements EnumBitSetHelper { + + STONE(1580000), + TRUESTEEL(1580001), + IRON(1580002), + ADAMANT(1580003), + LUMBER(1580004), + OAK(1580005), + BRONZEWOOD(1580006), + MANDRAKE(1580007), + COAL(1580008), + AGATE(1580009), + DIAMOND(1580010), + ONYX(1580011), + AZOTH(1580012), + ORICHALK(1580013), + ANTIMONY(1580014), + SULFUR(1580015), + QUICKSILVER(1580016), + GALVOR(1580017), + WORMWOOD(1580018), + OBSIDIAN(1580019), + BLOODSTONE(1580020), + MITHRIL(1580021), + GOLD(7); + + public static HashMap resourceLookup = new HashMap<>(); + public int itemID; + + ResourceType(int itemID) { + this.itemID = itemID; + } + + public static void InitializeResourceTypes(){ + + for (ResourceType resourceType :ResourceType.values()) + resourceLookup.put(resourceType.itemID, resourceType); + } + } + + public enum PowerActionType { + ApplyEffect, + ApplyEffects, + Block, + Charm, + ClaimMine, + ClearAggro, + ClearNearbyAggro, + Confusion, + CreateMob, + DamageOverTime, + DeferredPower, + DirectDamage, + Invis, + MobRecall, + Peek, + Recall, + RemoveEffect, + Resurrect, + RunegateTeleport, + SetItemFlag, + SimpleDamage, + SpireDisable, + Steal, + Summon, + Teleport, + Track, + TransferStat, + TransferStatOT, + Transform, + TreeChoke + } + + public enum AccountStatus { + BANNED, + ACTIVE, + ADMIN; + } + +} diff --git a/src/engine/InterestManagement/HeightMap.java b/src/engine/InterestManagement/HeightMap.java new file mode 100644 index 00000000..a683248b --- /dev/null +++ b/src/engine/InterestManagement/HeightMap.java @@ -0,0 +1,1099 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.InterestManagement; + +import engine.Enum; +import engine.gameManager.DbManager; +import engine.gameManager.ZoneManager; +import engine.math.Vector2f; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractWorldObject; +import engine.objects.Zone; +import engine.server.MBServerStatics; +import engine.util.MapLoader; +import org.pmw.tinylog.Logger; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; + +public class HeightMap { + + // Class variables + + // Heightmap data for all zones. + + public static final HashMap heightmapByLoadNum = new HashMap<>(); + + // Bootstrap Tracking + + public static int heightMapsCreated = 0; + public static HeightMap PlayerCityHeightMap; + + // Heightmap data for this heightmap + + public BufferedImage heightmapImage; + + private int heightMapID; + private int maxHeight; + private int fullExtentsX; + private int fullExtentsY; + + private float bucketWidthX; + private float bucketWidthY; + private int zoneLoadID; + private float seaLevel = 0; + private float outsetX; + private float outsetZ; + private int[][] pixelColorValues; + + public HeightMap(ResultSet rs) throws SQLException { + + this.heightMapID = rs.getInt("heightMapID"); + this.maxHeight = rs.getInt("maxHeight"); + int halfExtentsX = rs.getInt("xRadius"); + int halfExtentsY = rs.getInt("zRadius"); + this.zoneLoadID = rs.getInt("zoneLoadID"); + this.seaLevel = rs.getFloat("seaLevel"); + this.outsetX = rs.getFloat("outsetX"); + this.outsetZ = rs.getFloat("outsetZ"); + + + // Cache the full extents to avoid the calculation + + this.fullExtentsX = halfExtentsX * 2; + this.fullExtentsY = halfExtentsY * 2; + + this.heightmapImage = null; + File imageFile = new File(MBServerStatics.DEFAULT_DATA_DIR + "heightmaps/" + this.heightMapID + ".bmp"); + + // early exit if no image file was found. Will log in caller. + + if (!imageFile.exists()) + return; + + // load the heightmap image. + + try { + this.heightmapImage = ImageIO.read(imageFile); + } catch (IOException e) { + Logger.error("***Error loading heightmap data for heightmap " + this.heightMapID + e.toString()); + } + + // We needed to flip the image as OpenGL and Shadowbane both use the bottom left corner as origin. + + this.heightmapImage = MapLoader.flipImage(this.heightmapImage); + + // Calculate the data we do not load from table + + try { + calculateBucketWidth(); + + } catch (Exception e) { + e.printStackTrace(); + } + + // Generate pixel array from image data + + generatePixelData(); + + HeightMap.heightmapByLoadNum.put(this.zoneLoadID, this); + + heightMapsCreated++; + } + + //Created for PlayerCities + public HeightMap() { + + this.heightMapID = 999999; + this.maxHeight = 5; // for real... + int halfExtentsX = (int) Enum.CityBoundsType.ZONE.extents; + int halfExtentsY = (int) Enum.CityBoundsType.ZONE.extents; + this.zoneLoadID = 0; + this.seaLevel = 0; + this.outsetX = 128; + this.outsetZ = 128; + + + // Cache the full extents to avoid the calculation + + this.fullExtentsX = halfExtentsX * 2; + this.fullExtentsY = halfExtentsY * 2; + + + // load the heightmap image. + + + // We needed to flip the image as OpenGL and Shadowbane both use the bottom left corner as origin. + + this.heightmapImage = null; + + // Calculate the data we do not load from table + + this.bucketWidthX = 1; + this.bucketWidthY = 1; + + this.pixelColorValues = new int[this.fullExtentsX + 1][this.fullExtentsY+1]; + + for (int y = 0; y <= this.fullExtentsY; y++) { + for (int x = 0; x <= this.fullExtentsX; x++) { + pixelColorValues[x][y] = 255; + } + } + + + HeightMap.heightmapByLoadNum.put(this.zoneLoadID, this); + } + + public HeightMap(Zone zone) { + + this.heightMapID = 999999; + this.maxHeight = 0; + int halfExtentsX = (int) zone.getBounds().getHalfExtents().x; + int halfExtentsY = (int) zone.getBounds().getHalfExtents().y; + this.zoneLoadID = 0; + this.seaLevel = 0; + this.outsetX = 0; + this.outsetZ = 0; + + // Cache the full extents to avoid the calculation + + this.fullExtentsX = halfExtentsX * 2; + this.fullExtentsY = halfExtentsY * 2; + + + // We needed to flip the image as OpenGL and Shadowbane both use the bottom left corner as origin. + + this.heightmapImage = null; + + // Calculate the data we do not load from table + + this.bucketWidthX = 1; + this.bucketWidthY = 1; + + this.pixelColorValues = new int[this.fullExtentsX+1][this.fullExtentsY+1]; + + for (int y = 0; y <= this.fullExtentsY; y++) { + for (int x = 0; x <= this.fullExtentsX; x++) { + pixelColorValues[x][y] = 255; + } + } + + + HeightMap.heightmapByLoadNum.put(this.zoneLoadID, this); + } + + public static void GeneratePlayerCityHeightMap() { + + HeightMap.PlayerCityHeightMap = new HeightMap(); + + } + + public static void GenerateCustomHeightMap(Zone zone) { + + HeightMap heightMap = new HeightMap(zone); + + HeightMap.heightmapByLoadNum.put(zone.getLoadNum(), heightMap); + + } + + public Vector2f getGridSquare(Vector2f zoneLoc) { + + if (zoneLoc.x < 0) + zoneLoc.setX(0); + + if (zoneLoc.x > this.fullExtentsX - 1) + zoneLoc.setX((this.fullExtentsX - 1) + .9999999f); + + if (zoneLoc.y < 0) + zoneLoc.setY(0); + + if (zoneLoc.y > this.fullExtentsY - 1) + zoneLoc.setY((this.fullExtentsY - 1) + .9999999f); + + float xBucket = (zoneLoc.x / this.bucketWidthX); + float yBucket = (zoneLoc.y / this.bucketWidthY); + + return new Vector2f(xBucket, yBucket); + } + + public float getInterpolatedTerrainHeight(Vector2f zoneLoc) { + + Vector2f gridSquare; + + if (zoneLoc.x < 0 || zoneLoc.x > this.fullExtentsX) + return -1; + + if (zoneLoc.y < 0 || zoneLoc.y > this.fullExtentsY) + return -1; + + int maxX = (int) (this.fullExtentsX / this.bucketWidthX); + int maxY = (int) (this.fullExtentsY / this.bucketWidthY); + + //flip the Y so it grabs from the bottom left instead of top left. + //zoneLoc.setY(maxZoneHeight - zoneLoc.y); + + gridSquare = getGridSquare(zoneLoc); + + int gridX = (int) gridSquare.x; + int gridY = (int) (gridSquare.y); + + if (gridX > maxX) + gridX = maxX; + if (gridY > maxY) + gridY = maxY; + + float offsetX = (gridSquare.x - gridX); + float offsetY = gridSquare.y - gridY; + + //get height of the 4 vertices. + + float topLeftHeight = 0; + float topRightHeight = 0; + float bottomLeftHeight = 0; + float bottomRightHeight = 0; + + int nextY = gridY +1; + int nextX = gridX + 1; + + if (nextY > maxY) + nextY = gridY; + + if (nextX > maxX) + nextX = gridX; + + topLeftHeight = pixelColorValues[gridX][gridY]; + topRightHeight = pixelColorValues[nextX][gridY]; + bottomLeftHeight = pixelColorValues[gridX][nextY]; + bottomRightHeight = pixelColorValues[nextX][nextY]; + + float interpolatedHeight; + + interpolatedHeight = topRightHeight * (1 - offsetY) * (offsetX); + interpolatedHeight += (bottomRightHeight * offsetY * offsetX); + interpolatedHeight += (bottomLeftHeight * (1 - offsetX) * offsetY); + interpolatedHeight += (topLeftHeight * (1 - offsetX) * (1 - offsetY)); + + interpolatedHeight *= (float) this.maxHeight / 256; // Scale height + + return interpolatedHeight; + } + + public static float getWorldHeight(AbstractWorldObject worldObject) { + + Vector2f parentLoc = new Vector2f(-1, -1); + Zone currentZone = ZoneManager.findSmallestZone(worldObject.getLoc()); + + if (currentZone == null) + return worldObject.getAltitude(); + + Zone parentZone = currentZone.getParent(); + HeightMap heightMap = currentZone.getHeightMap(); + + //find the next parents heightmap if the currentzone heightmap is null. + + while (heightMap == null) { + + if (currentZone == ZoneManager.getSeaFloor()) { + break; + } + currentZone = currentZone.getParent(); + heightMap = currentZone.getHeightMap(); + + parentZone = currentZone.getParent(); + } + if ((heightMap == null) || (currentZone == ZoneManager.getSeaFloor())) { + + return currentZone.getAbsY() + worldObject.getAltitude(); + } + + Vector2f zoneLoc = ZoneManager.worldToZoneSpace(worldObject.getLoc(), currentZone); + Vector3fImmutable localLocFromCenter = ZoneManager.worldToLocal(worldObject.getLoc(), currentZone); + + if ((parentZone != null) && (parentZone.getHeightMap() != null)) { + parentLoc = ZoneManager.worldToZoneSpace(worldObject.getLoc(), parentZone); + } + + float interaltitude = currentZone.getHeightMap().getInterpolatedTerrainHeight(zoneLoc); + + float worldAltitude = currentZone.getWorldAltitude(); + + float realWorldAltitude = interaltitude + worldAltitude; + + //OUTSET + + + if (parentZone != null) { + + + float parentXRadius = currentZone.getBounds().getHalfExtents().x; + float parentZRadius = currentZone.getBounds().getHalfExtents().y; + + + float offsetX = Math.abs((localLocFromCenter.x / parentXRadius)); + float offsetZ = Math.abs((localLocFromCenter.z / parentZRadius)); + + float bucketScaleX = heightMap.outsetX / parentXRadius; + float bucketScaleZ = heightMap.outsetZ / parentZRadius; + + + if (bucketScaleX <= 0.40000001) { + bucketScaleX = heightMap.outsetZ / parentXRadius; + + } + + if (bucketScaleX > 0.40000001) + bucketScaleX = 0.40000001f; + + if (bucketScaleZ <= 0.40000001) { + bucketScaleZ = heightMap.outsetX / parentZRadius; + } + + if (bucketScaleZ > 0.40000001) + bucketScaleZ = 0.40000001f; + + float outsideGridSizeX = 1 - bucketScaleX; //32/256 + float outsideGridSizeZ = 1 - bucketScaleZ; + float weight; + + double scale; + + + if (offsetX > outsideGridSizeX && offsetX > offsetZ) { + weight = (offsetX - outsideGridSizeX) / bucketScaleX; + scale = Math.atan2((.5 - weight) * 3.1415927, 1); + + float scaleChild = (float) ((scale + 1) * .5); + float scaleParent = 1 - scaleChild; + + + float parentAltitude = parentZone.getHeightMap().getInterpolatedTerrainHeight(parentLoc); + float parentCenterAltitude = parentZone.getHeightMap().getInterpolatedTerrainHeight(ZoneManager.worldToZoneSpace(currentZone.getLoc(), parentZone)); + + + parentCenterAltitude += currentZone.getYCoord(); + parentCenterAltitude += interaltitude; + + float firstScale = parentAltitude * scaleParent; + float secondScale = parentCenterAltitude * scaleChild; + float outsetALt = firstScale + secondScale; + + outsetALt += currentZone.getParent().getWorldAltitude(); + realWorldAltitude = outsetALt; + + } else if (offsetZ > outsideGridSizeZ) { + + weight = (offsetZ - outsideGridSizeZ) / bucketScaleZ; + scale = Math.atan2((.5 - weight) * 3.1415927, 1); + + float scaleChild = (float) ((scale + 1) * .5); + float scaleParent = 1 - scaleChild; + float parentAltitude = parentZone.getHeightMap().getInterpolatedTerrainHeight(parentLoc); + float parentCenterAltitude = parentZone.getHeightMap().getInterpolatedTerrainHeight(ZoneManager.worldToZoneSpace(currentZone.getLoc(), parentZone)); + + parentCenterAltitude += currentZone.getYCoord(); + parentCenterAltitude += interaltitude; + float firstScale = parentAltitude * scaleParent; + float secondScale = parentCenterAltitude * scaleChild; + float outsetALt = firstScale + secondScale; + + outsetALt += currentZone.getParent().getWorldAltitude(); + realWorldAltitude = outsetALt; + } + } + + + return realWorldAltitude; + } + + public static float getWorldHeight(Vector3fImmutable worldLoc) { + + Vector2f parentLoc = new Vector2f(-1, -1); + Zone currentZone = ZoneManager.findSmallestZone(worldLoc); + + if (currentZone == null) + return 0; + + Zone parentZone = currentZone.getParent(); + HeightMap heightMap = currentZone.getHeightMap(); + //find the next parents heightmap if the currentzone heightmap is null. + while (heightMap == null) { + + if (currentZone == ZoneManager.getSeaFloor()) { + break; + } + currentZone = currentZone.getParent(); + heightMap = currentZone.getHeightMap(); + + parentZone = currentZone.getParent(); + } + if ((heightMap == null) || (currentZone == ZoneManager.getSeaFloor())) { + + return currentZone.getAbsY(); + } + + Vector2f zoneLoc = ZoneManager.worldToZoneSpace(worldLoc, currentZone); + Vector3fImmutable localLocFromCenter = ZoneManager.worldToLocal(worldLoc, currentZone); + + if ((parentZone != null) && (parentZone.getHeightMap() != null)) { + parentLoc = ZoneManager.worldToZoneSpace(worldLoc, parentZone); + } + + float interaltitude = currentZone.getHeightMap().getInterpolatedTerrainHeight(zoneLoc); + + float worldAltitude = currentZone.getWorldAltitude(); + + float realWorldAltitude = interaltitude + worldAltitude; + + //OUTSET + + + if (parentZone != null) { + + // if (currentZone.getHeightMap() != null && parentZone.getHeightMap() != null && parentZone.getParent() != null && parentZone.getParent().getHeightMap() != null) + // return realWorldAltitude; + + float parentXRadius = currentZone.getBounds().getHalfExtents().x; + float parentZRadius = currentZone.getBounds().getHalfExtents().y; + + float offsetX = Math.abs((localLocFromCenter.x / parentXRadius)); + float offsetZ = Math.abs((localLocFromCenter.z / parentZRadius)); + + float bucketScaleX = heightMap.outsetX / parentXRadius; + float bucketScaleZ = heightMap.outsetZ / parentZRadius; + + float outsideGridSizeX = 1 - bucketScaleX; //32/256 + float outsideGridSizeZ = 1 - bucketScaleZ; + float weight; + + double scale; + + + if (offsetX > outsideGridSizeX && offsetX > offsetZ) { + weight = (offsetX - outsideGridSizeX) / bucketScaleX; + scale = Math.atan2((.5 - weight) * 3.1415927, 1); + + float scaleChild = (float) ((scale + 1) * .5); + float scaleParent = 1 - scaleChild; + + + float parentAltitude = parentZone.getHeightMap().getInterpolatedTerrainHeight(parentLoc); + float parentCenterAltitude = parentZone.getHeightMap().getInterpolatedTerrainHeight(ZoneManager.worldToZoneSpace(currentZone.getLoc(), parentZone)); + + + parentCenterAltitude += currentZone.getYCoord(); + parentCenterAltitude += interaltitude; + + float firstScale = parentAltitude * scaleParent; + float secondScale = parentCenterAltitude * scaleChild; + float outsetALt = firstScale + secondScale; + + outsetALt += currentZone.getParent().getWorldAltitude(); + realWorldAltitude = outsetALt; + + } else if (offsetZ > outsideGridSizeZ) { + + weight = (offsetZ - outsideGridSizeZ) / bucketScaleZ; + scale = Math.atan2((.5 - weight) * 3.1415927, 1); + + float scaleChild = (float) ((scale + 1) * .5); + float scaleParent = 1 - scaleChild; + float parentAltitude = parentZone.getHeightMap().getInterpolatedTerrainHeight(parentLoc); + float parentCenterAltitude = parentZone.getHeightMap().getInterpolatedTerrainHeight(ZoneManager.worldToZoneSpace(currentZone.getLoc(), parentZone)); + + parentCenterAltitude += currentZone.getYCoord(); + parentCenterAltitude += interaltitude; + float firstScale = parentAltitude * scaleParent; + float secondScale = parentCenterAltitude * scaleChild; + float outsetALt = firstScale + secondScale; + + outsetALt += currentZone.getParent().getWorldAltitude(); + realWorldAltitude = outsetALt; + } + } + + + return realWorldAltitude; + } + + public float getInterpolatedTerrainHeight(Vector3fImmutable zoneLoc3f) { + + Vector2f zoneLoc = new Vector2f(zoneLoc3f.x, zoneLoc3f.z); + + Vector2f gridSquare; + + if (zoneLoc.x < 0 || zoneLoc.x > this.fullExtentsX) + return -1; + + if (zoneLoc.y < 0 || zoneLoc.y > this.fullExtentsY) + return -1; + + //flip the Y so it grabs from the bottom left instead of top left. + //zoneLoc.setY(maxZoneHeight - zoneLoc.y); + + gridSquare = getGridSquare(zoneLoc); + + int gridX = (int) gridSquare.x; + int gridY = (int) (gridSquare.y); + + float offsetX = (gridSquare.x - gridX); + float offsetY = gridSquare.y - gridY; + + //get height of the 4 vertices. + + float topLeftHeight = pixelColorValues[gridX][gridY]; + float topRightHeight = pixelColorValues[gridX + 1][gridY]; + float bottomLeftHeight = pixelColorValues[gridX][gridY + 1]; + float bottomRightHeight = pixelColorValues[gridX + 1][gridY + 1]; + + float interpolatedHeight; + + interpolatedHeight = topRightHeight * (1 - offsetY) * (offsetX); + interpolatedHeight += (bottomRightHeight * offsetY * offsetX); + interpolatedHeight += (bottomLeftHeight * (1 - offsetX) * offsetY); + interpolatedHeight += (topLeftHeight * (1 - offsetX) * (1 - offsetY)); + + interpolatedHeight *= (float) this.maxHeight / 256; // Scale height + + return interpolatedHeight; + } + + public static float getOutsetHeight(float interpolatedAltitude, Zone zone, Vector3fImmutable worldLocation) { + + Vector2f parentLoc; + float outsetALt = 0; + + if (zone.getParent() == null || zone.getParent().getHeightMap() == null) + return interpolatedAltitude + zone.getWorldAltitude(); + + if (zone.getParent() != null && zone.getParent().getHeightMap() != null) { + + parentLoc = ZoneManager.worldToZoneSpace(worldLocation, zone.getParent()); + + Vector3fImmutable localLocFromCenter = ZoneManager.worldToLocal(worldLocation, zone); + + float parentXRadius = zone.getBounds().getHalfExtents().x; + float parentZRadius = zone.getBounds().getHalfExtents().y; + + float bucketScaleX = zone.getHeightMap().outsetX / parentXRadius; + float bucketScaleZ = zone.getHeightMap().outsetZ / parentZRadius; + + float outsideGridSizeX = 1 - bucketScaleX; //32/256 + float outsideGridSizeZ = 1 - bucketScaleZ; + + float weight; + double scale; + + float offsetX = Math.abs((localLocFromCenter.x / parentXRadius)); + float offsetZ = Math.abs((localLocFromCenter.z / parentZRadius)); + + if (offsetX > outsideGridSizeX && offsetX > offsetZ) { + weight = (offsetX - outsideGridSizeX) / bucketScaleX; + scale = Math.atan2((.5 - weight) * 3.1415927, 1); + + float scaleChild = (float) ((scale + 1) * .5); + float scaleParent = 1 - scaleChild; + + float parentAltitude = zone.getParent().getHeightMap().getInterpolatedTerrainHeight(parentLoc); + float parentCenterAltitude = zone.getParent().getHeightMap().getInterpolatedTerrainHeight(ZoneManager.worldToZoneSpace(zone.getLoc(), zone.getParent())); + + parentCenterAltitude += zone.getYCoord(); + parentCenterAltitude += interpolatedAltitude; + + float firstScale = parentAltitude * scaleParent; + float secondScale = parentCenterAltitude * scaleChild; + outsetALt = firstScale + secondScale; + + outsetALt += zone.getParent().getAbsY(); + + } else if (offsetZ > outsideGridSizeZ) { + + weight = (offsetZ - outsideGridSizeZ) / bucketScaleZ; + scale = Math.atan2((.5 - weight) * 3.1415927, 1); + + float scaleChild = (float) ((scale + 1) * .5); + float scaleParent = 1 - scaleChild; + float parentAltitude = zone.getParent().getHeightMap().getInterpolatedTerrainHeight(parentLoc); + float parentCenterAltitude = zone.getHeightMap().getInterpolatedTerrainHeight(ZoneManager.worldToZoneSpace(zone.getLoc(), zone)); + + parentCenterAltitude += zone.getYCoord(); + parentCenterAltitude += interpolatedAltitude; + + float firstScale = parentAltitude * scaleParent; + float secondScale = parentCenterAltitude * scaleChild; + outsetALt = firstScale + secondScale; + + outsetALt += zone.getParent().getAbsY(); + } + + } + + return outsetALt; + } + + private void generatePixelData() { + + Color color; + + // Generate altitude lookup table for this heightmap + + this.pixelColorValues = new int[this.heightmapImage.getWidth()][this.heightmapImage.getHeight()]; + + for (int y = 0; y < this.heightmapImage.getHeight(); y++) { + for (int x = 0; x < this.heightmapImage.getWidth(); x++) { + + color = new Color(this.heightmapImage.getRGB(x, y)); + pixelColorValues[x][y] = color.getRed(); + } + } + + } + + public static Vector2f getGridOffset(Vector2f gridSquare) { + + int floorX = (int) gridSquare.x; + int floorY = (int) gridSquare.y; + + return new Vector2f(gridSquare.x - floorX, gridSquare.y - floorY); + + } + + public float getScaledHeightForColor(float color) { + + return (color / 256) * this.maxHeight; + } + + + private void calculateBucketWidth() { + + + switch (this.zoneLoadID) { + case 100: + this.bucketWidthX = 64.12524414f; + this.bucketWidthY = 64.12524414f; + break; + case 200: + this.bucketWidthX = 145.9599152f; + this.bucketWidthY = 145.9599152f; + break; + case 3033: + this.bucketWidthX = 2.001116753f; + this.bucketWidthY = 2.001116753f; + break; + + case 3011: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + + case 3026: + this.bucketWidthX = 2.001116753f; + this.bucketWidthY = 2.001116753f; + break; + + case 3017: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + + case 3007: + this.bucketWidthX = 2.001116753f; + this.bucketWidthY = 2.001116753f; + break; + + case 3020: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + + case 3025: + this.bucketWidthX = 2.001116753f; + this.bucketWidthY = 2.001116753f; + break; + + case 3016: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + + case 3021: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + + case 3018: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + + case 3024: + this.bucketWidthX = 2.001116753f; + this.bucketWidthY = 2.001116753f; + break; + + case 3010: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + + case 3012: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + + case 3022: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + + case 3030: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + + case 3019: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + + case 3014: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + + case 10500: + this.bucketWidthX = 51.20000076f; + this.bucketWidthY = 51.20000076f; + break; + + case 10501: + this.bucketWidthX = 51.20000076f; + this.bucketWidthY = 51.20000076f; + break; + + case 10503: + this.bucketWidthX = 51.20000076f; + this.bucketWidthY = 51.20000076f; + break; + case 10504: + this.bucketWidthX = 51.20000076f; + this.bucketWidthY = 51.20000076f; + break; + case 10505: + this.bucketWidthX = 51.20000076f; + this.bucketWidthY = 51.20000076f; + break; + case 10506: + this.bucketWidthX = 51.20000076f; + this.bucketWidthY = 51.20000076f; + break; + case 10507: + this.bucketWidthX = 51.20000076f; + this.bucketWidthY = 51.20000076f; + break; + case 10502: + this.bucketWidthX = 51.20000076f; + this.bucketWidthY = 51.20000076f; + break; + + case 11006: + this.bucketWidthX = 51.20000076f; + this.bucketWidthY = 51.20000076f; + break; + case 11008: + this.bucketWidthX = 51.20000076f; + this.bucketWidthY = 51.20000076f; + break; + case 11036: + this.bucketWidthX = 51.20000076f; + this.bucketWidthY = 51.20000076f; + break; + + case 10200: + this.bucketWidthX = 2.000977039f; + this.bucketWidthY = 2.000977039f; + break; + + + case 10120: + this.bucketWidthX = 1.00048852f; + this.bucketWidthY = 1.00048852f; + break; + + case 10001: + this.bucketWidthX = 17.06666756f; + this.bucketWidthY = 17.06666756f; + break; + case 10002: + this.bucketWidthX = 17.06666756f; + this.bucketWidthY = 17.06666756f; + break; + case 10003: + this.bucketWidthX = 17.06666756f; + this.bucketWidthY = 17.06666756f; + break; + case 10004: + this.bucketWidthX = 17.06666756f; + this.bucketWidthY = 17.06666756f; + break; + case 10005: + this.bucketWidthX = 17.06666756f; + this.bucketWidthY = 17.06666756f; + break; + case 10100: + this.bucketWidthX = 17.06666756f; + this.bucketWidthY = 17.06666756f; + break; + + case 10006: + this.bucketWidthX = 17.06666756f; + this.bucketWidthY = 17.06666756f; + break; + case 10007: + this.bucketWidthX = 17.06666756f; + this.bucketWidthY = 17.06666756f; + break; + case 10008: + this.bucketWidthX = 17.06666756f; + this.bucketWidthY = 17.06666756f; + break; + case 10009: + this.bucketWidthX = 17.06666756f; + this.bucketWidthY = 17.06666756f; + break; + + case 10010: + this.bucketWidthX = 17.06666756f; + this.bucketWidthY = 17.06666756f; + break; + + case 10201: + this.bucketWidthX = 2.000977039f; + this.bucketWidthY = 2.000977039f; + break; + + case 10011: + this.bucketWidthX = 17.06666756f; + this.bucketWidthY = 17.06666756f; + break; + case 10012: + this.bucketWidthX = 17.06666756f; + this.bucketWidthY = 17.06666756f; + break; + case 10013: + this.bucketWidthX = 17.06666756f; + this.bucketWidthY = 17.06666756f; + break; + case 10014: + this.bucketWidthX = 17.06666756f; + this.bucketWidthY = 17.06666756f; + break; + case 10015: + this.bucketWidthX = 17.06666756f; + this.bucketWidthY = 17.06666756f; + break; + + case 3004: + this.bucketWidthX = 2.001116753f; + this.bucketWidthY = 2.001116753f; + break; + + case 3005: + this.bucketWidthX = 2.001116753f; + this.bucketWidthY = 2.001116753f; + break; + + + case 3003: + this.bucketWidthX = 2.001116753f; + this.bucketWidthY = 2.001116753f; + break; + + + case 400: + this.bucketWidthX = 64.06256104f; + this.bucketWidthY = 64.06256104f; + break; + + case 3032: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + + case 3009: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + case 3023: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + case 3008: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + case 11009: + this.bucketWidthX = 51.20000076f; + this.bucketWidthY = 51.20000076f; + break; + case 500: + this.bucketWidthX = 128.4012604f; + this.bucketWidthY = 128.4012604f; + break; + + case 3013: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + case 3006: + this.bucketWidthX = 2.001116753f; + this.bucketWidthY = 2.001116753f; + break; + + case 3015: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + + case 11010: + this.bucketWidthX = 51.20000076f; + this.bucketWidthY = 51.20000076f; + break; + + case 10130: + this.bucketWidthX = 1.00048852f; + this.bucketWidthY = 1.00048852f; + break; + + + case 501: + this.bucketWidthX = 130.0317535f; + this.bucketWidthY = 130.0317535f; + break; + + case 11032: + this.bucketWidthX = 51.20000076f; + this.bucketWidthY = 51.20000076f; + break; + + case 300: + this.bucketWidthX = 100.5235596f; + this.bucketWidthY = 100.5235596f; + break; + + case 3027: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + + case 3028: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + + case 11016: + this.bucketWidthX = 51.20000076f; + this.bucketWidthY = 51.20000076f; + break; + + case 310: + this.bucketWidthX = 100.5235596f; + this.bucketWidthY = 100.5235596f; + break; + + case 3034: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + + case 3035: + this.bucketWidthX = 4.003910065f; + this.bucketWidthY = 4.003910065f; + break; + + case 11039: + this.bucketWidthX = 51.20000076f; + this.bucketWidthY = 51.20000076f; + break; + + case 10066: + this.bucketWidthX = 51.20000076f; + this.bucketWidthY = 51.20000076f; + break; + + case 502: + + this.bucketWidthX = 130.0317535f; + this.bucketWidthY = 130.0317535f; + break; + + default: + // Re-enable for debugging. Spammy in console. + // Logger.info("Setting Zone : " + this.zoneLoadID + " with heightmap ID : " + heightMapID + " to default bucketwidth"); + break; + } + } + + public static void loadAlHeightMaps() { + + // Load the heightmaps into staging hashmap keyed by HashMapID + + DbManager.HeightMapQueries.LOAD_ALL_HEIGHTMAPS(); + + //generate static player city heightmap. + + HeightMap.GeneratePlayerCityHeightMap(); + + + // Clear all heightmap image data as it's no longer needed. + + for (HeightMap heightMap : HeightMap.heightmapByLoadNum.values()) { + heightMap.heightmapImage = null; + } + + Logger.info(HeightMap.heightmapByLoadNum.size() + " Heightmaps cached."); + } + + public float getBucketWidthX() { + return bucketWidthX; + } + + public float getBucketWidthY() { + return bucketWidthY; + } + + public int getHeightMapID() { + return heightMapID; + } + + public BufferedImage getHeightmapImage() { + return heightmapImage; + } + + public float getSeaLevel() { + return seaLevel; + } + + public static boolean isLocUnderwater(Vector3fImmutable currentLoc) { + + float localAltitude = HeightMap.getWorldHeight(currentLoc); + Zone zone = ZoneManager.findSmallestZone(currentLoc); + + if (localAltitude < zone.getSeaLevel()) + return true; + + return false; + } + +} diff --git a/src/engine/InterestManagement/InterestManager.java b/src/engine/InterestManagement/InterestManager.java new file mode 100644 index 00000000..6182d29d --- /dev/null +++ b/src/engine/InterestManagement/InterestManager.java @@ -0,0 +1,554 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.InterestManagement; + +import engine.Enum.DispatchChannel; +import engine.Enum.GameObjectType; +import engine.ai.MobileFSM; +import engine.ai.MobileFSM.STATE; +import engine.gameManager.GroupManager; +import engine.gameManager.SessionManager; +import engine.job.JobScheduler; +import engine.jobs.RefreshGroupJob; +import engine.net.AbstractNetMsg; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.LoadCharacterMsg; +import engine.net.client.msg.LoadStructureMsg; +import engine.net.client.msg.MoveToPointMsg; +import engine.net.client.msg.UnloadObjectsMsg; +import engine.objects.*; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.HashSet; + +import static engine.math.FastMath.sqr; + +public enum InterestManager implements Runnable { + + INTERESTMANAGER; + + private static long lastTime; + private static boolean keepGoing = true; + + public void shutdown() { + this.keepGoing = false; + } + + InterestManager() { + Logger.info(" Interest Management thread is running."); + } + + @Override + public void run() { + beginLoadJob(); + } + + private void beginLoadJob() { + + InterestManager.lastTime = System.currentTimeMillis(); + + while (InterestManager.keepGoing) { + try { + updateAllPlayers(); + } catch (Exception e) { + Logger.error("InterestManager.BeginLoadJob:updateAllPlayers", e); + } + try { + Thread.sleep(advanceOneSecond()); + } catch (Exception e) { + Logger.error("InterestManager.BeginLoadJob:advanceOneSecond", e); + } + } + } + + private long advanceOneSecond() { + + long curTime = System.currentTimeMillis(); + long dur = 1000 + this.lastTime - curTime; + + if (dur < 0) { + // Last update took more then one second, not good... + Logger.warn("LoadJob took more then one second to complete."); + this.lastTime = curTime + 100; + return 100; + } + this.lastTime += 1000; + return dur; + } + + private void updateAllPlayers() { + // get all players + + for (PlayerCharacter pc : SessionManager.getAllActivePlayerCharacters()) { + + if (pc == null) + continue; + + ClientConnection origin = pc.getClientConnection(); + + if (origin == null) + continue; + + if (!pc.isEnteredWorld()) + continue; + + if (pc.getTeleportLock().readLock().tryLock()) { + + try { + updateStaticList(pc, origin); + updateMobileList(pc, origin); + } catch (Exception e) { + Logger.error(e); + } finally { + pc.getTeleportLock().readLock().unlock(); + } + } + } + } + + private void updateStaticList(PlayerCharacter player, ClientConnection origin) { + + // Only update if we've moved far enough to warrant it + + float distanceSquared = player.getLoc().distanceSquared2D(player.getLastStaticLoc()); + + if (distanceSquared > sqr(25)) + player.setLastStaticLoc(player.getLoc()); + else + return; + + // Get Statics in range + HashSet toLoad = WorldGrid.getObjectsInRangePartial(player.getLoc(), MBServerStatics.STRUCTURE_LOAD_RANGE, + MBServerStatics.MASK_STATIC); + + // get list of obects loaded that need removed + HashSet loadedStaticObjects = player.getLoadedStaticObjects(); + + HashSet toRemove = null; + + toRemove = new HashSet<>(loadedStaticObjects); + + toRemove.removeAll(toLoad); + + // unload static objects now out of range + if (toRemove.size() > 0) { + UnloadObjectsMsg uom = new UnloadObjectsMsg(); + for (AbstractWorldObject obj : toRemove) { + if (obj.getObjectType().equals(GameObjectType.Building)) + InterestManager.HandleSpecialUnload((Building) obj, origin); + if (obj != null && !obj.equals(player)) + uom.addObject(obj); + } + + Dispatch dispatch = Dispatch.borrow(player, uom); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + } + + loadedStaticObjects.removeAll(toRemove); + + // remove any object to load that are already loaded + toLoad.removeAll(loadedStaticObjects); + + LoadStructureMsg lsm = new LoadStructureMsg(); + LoadCharacterMsg lcm = null; + ArrayList lcmList = new ArrayList<>(); + + for (AbstractWorldObject awo : toLoad) { + if (awo.getObjectType().equals(GameObjectType.Building)) + lsm.addObject((Building) awo); + else if (awo.getObjectType().equals(GameObjectType.Corpse)) { + Corpse corpse = (Corpse) awo; + lcm = new LoadCharacterMsg(corpse, PlayerCharacter.hideNonAscii()); + + Dispatch dispatch = Dispatch.borrow(player, lcm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + + + } else if (awo.getObjectType().equals(GameObjectType.NPC)) { + NPC npc = (NPC) awo; + lcm = new LoadCharacterMsg(npc, PlayerCharacter.hideNonAscii()); + + lcmList.add(lcm); + } + } + + if (lsm.getStructureList().size() > 0) { + Dispatch dispatch = Dispatch.borrow(player, lsm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + } + + for (LoadCharacterMsg lc : lcmList) { + + Dispatch dispatch = Dispatch.borrow(player, lc); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + } + + loadedStaticObjects.addAll(toLoad); + } + + private void updateMobileList(PlayerCharacter player, ClientConnection origin) { + + if (player == null) + return; + + // Get list of players in range + // TODO for now use a generic getALL list, later tie into Quad Tree + HashSet toLoad = WorldGrid.getObjectsInRangePartial(player.getLoc(), MBServerStatics.CHARACTER_LOAD_RANGE, + MBServerStatics.MASK_MOBILE); + + HashSet toRemove = new HashSet<>(); + + HashSet toLoadToPlayer = new HashSet<>(); + + for (AbstractWorldObject loadedObject : toLoad) { + + switch (loadedObject.getObjectType()) { + case PlayerCharacter: + PlayerCharacter loadedPlayer = (PlayerCharacter) loadedObject; + + if (loadedPlayer.getObjectUUID() == player.getObjectUUID()) + continue; + + if (player.getSeeInvis() < loadedPlayer.getHidden()) + continue; + + if (loadedPlayer.safemodeInvis()) + continue; + + if (player.getLoadedObjects().contains(loadedPlayer)) + continue; + + if (!loadedPlayer.isInWorldGrid()) + continue; + + toLoadToPlayer.add(loadedPlayer); + break; + //not playerCharacter, mobs,npcs and corpses cant be invis or safemode, just add normaly + default: + if (player.getLoadedObjects().contains(loadedObject)) + continue; + + if (!loadedObject.isInWorldGrid()) + continue; + + toLoadToPlayer.add(loadedObject); + break; + } + } + + float unloadDistance = MBServerStatics.CHARACTER_LOAD_RANGE; + for (AbstractWorldObject playerLoadedObject : player.getLoadedObjects()) { + + if (playerLoadedObject.getObjectType().equals(GameObjectType.PlayerCharacter)) { + PlayerCharacter loadedPlayer = (PlayerCharacter) playerLoadedObject; + if (player.getSeeInvis() < loadedPlayer.getHidden()) + toRemove.add(playerLoadedObject); + else if (loadedPlayer.safemodeInvis()) + toRemove.add(playerLoadedObject); + } + + if (!playerLoadedObject.isInWorldGrid()) + toRemove.add(playerLoadedObject); + else if (playerLoadedObject.getLoc().distanceSquared2D(player.getLoc()) > unloadDistance * unloadDistance) + toRemove.add(playerLoadedObject); + + } + + player.getLoadedObjects().addAll(toLoadToPlayer); + player.getLoadedObjects().removeAll(toRemove); + + // get list of obects loaded to remove + + // unload objects now out of range + + if (toRemove.size() > 0) { + + UnloadObjectsMsg uom = new UnloadObjectsMsg(); + + for (AbstractWorldObject obj : toRemove) { + + try { + if (obj != null) + if (obj.equals(player)) // don't unload self + continue; + + uom.addObject(obj); + + if (obj.getObjectType() == GameObjectType.Mob) + ((Mob) obj).getPlayerAgroMap().remove(player.getObjectUUID()); + } catch (Exception e) { + Logger.error("UnloadCharacter", obj.getObjectUUID() + " " + e.getMessage()); + } + } + + if (!uom.getObjectList().isEmpty()) { + Dispatch dispatch = Dispatch.borrow(player, uom); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + } + } + + LoadCharacterMsg lcm = null; + ArrayList players = new ArrayList<>(); + ArrayList addToList = new ArrayList<>(); + + for (AbstractWorldObject awo : toLoadToPlayer) { + // dont load yourself + try { + if (awo.equals(player)) + continue; + + if ((awo.getObjectTypeMask() & MBServerStatics.MASK_PLAYER) != 0) { + + // object to load is a player + PlayerCharacter awopc = (PlayerCharacter) awo; + + // dont load if invis + if (player.getSeeInvis() < awopc.getHidden()) + continue; + + lcm = new LoadCharacterMsg(awopc, PlayerCharacter.hideNonAscii()); + players.add(awo); + + // check if in a group with the person being loaded + // and if so set updateGroup flag + + if (GroupManager.getGroup(player) != null + && GroupManager.getGroup(player) == GroupManager.getGroup(awopc)) + + // submit a job as for some reason the client needs a delay + // with group updates + // as it wont update if we do RefreshGroup directly after + // sending the lcm below + + JobScheduler.getInstance().scheduleJob(new RefreshGroupJob(player, awopc), MBServerStatics.LOAD_OBJECT_DELAY); + + } else if ((awo.getObjectTypeMask() & MBServerStatics.MASK_MOB) != 0) { + Mob awonpc = (Mob) awo; + + if (!awonpc.isAlive() && (awonpc.isPet() || awonpc.isSiege() || awonpc.isNecroPet() || awonpc.isPlayerGuard())) + continue; + + if (awonpc.getState().equals(STATE.Respawn) || awonpc.getState().equals(STATE.Disabled)) + continue; + + awonpc.getPlayerAgroMap().put(player.getObjectUUID(), false); + MobileFSM.setAwake(awonpc, false); + // IVarController.setVariable(awonpc, "IntelligenceDisableDelay", (double) (System.currentTimeMillis() + 5000)); + // awonpc.enableIntelligence(); + lcm = new LoadCharacterMsg(awonpc, PlayerCharacter.hideNonAscii()); + } else if ((awo.getObjectTypeMask() & MBServerStatics.MASK_NPC) != 0) { + NPC awonpc = (NPC) awo; + lcm = new LoadCharacterMsg(awonpc, PlayerCharacter.hideNonAscii()); + } else if ((awo.getObjectTypeMask() & MBServerStatics.MASK_PET) != 0) { + Mob awonpc = (Mob) awo; + + if (!awonpc.isAlive()) + continue; + + awonpc.getPlayerAgroMap().put(player.getObjectUUID(), false); + + if (awonpc.isMob()) + MobileFSM.setAwake(awonpc, false); + // IVarController.setVariable(awonpc, "IntelligenceDisableDelay", (double) (System.currentTimeMillis() + 5000)); + // awonpc.enableIntelligence(); + lcm = new LoadCharacterMsg(awonpc, PlayerCharacter.hideNonAscii()); + } + + addToList.add(awo); + + if (lcm != null) { + Dispatch dispatch = Dispatch.borrow(player, lcm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + } + + + } catch (Exception e) { + Logger.error(awo.getObjectUUID() + " " + e.getMessage()); + } + //Delaying character loading to reduce bandwidth consumption + } + + // send effects for all players being loaded + // do it on a timer otherwise we may get failures as te client needs + // time to process lcm + //Added effects to LoadCharacter Serialization. + //JobScheduler.getInstance().scheduleJob(new LoadEffectsJob(players, origin), MBServerStatics.LOAD_OBJECT_DELAY); + } + + // Forces the loading of static objects (corpses and buildings). + // Needed to override threshold limits on loading statics + + public static void forceLoad(AbstractWorldObject awo) { + + AbstractNetMsg msg = null; + LoadStructureMsg lsm; + LoadCharacterMsg lcm; + NPC npc; + Corpse corpse; + HashSet toUpdate; + + switch (awo.getObjectType()) { + case Building: + lsm = new LoadStructureMsg(); + lsm.addObject((Building) awo); + msg = lsm; + break; + case Corpse: + corpse = (Corpse) awo; + lcm = new LoadCharacterMsg(corpse, false); + msg = lcm; + break; + case NPC: + npc = (NPC) awo; + lcm = new LoadCharacterMsg(npc, false); + msg = lcm; + break; + default: + return; + } + + toUpdate = WorldGrid.getObjectsInRangePartial(awo.getLoc(), MBServerStatics.CHARACTER_LOAD_RANGE, MBServerStatics.MASK_PLAYER); + + boolean send; + + for (AbstractWorldObject tar : toUpdate) { + PlayerCharacter player = (PlayerCharacter) tar; + HashSet loadedStaticObjects = player.getLoadedStaticObjects(); + send = false; + + if (!loadedStaticObjects.contains(awo)) { + loadedStaticObjects.add(awo); + send = true; + } + + if (send) { + + Dispatch dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + } + } + } + + public static void HandleSpecialUnload(Building building, ClientConnection origin) { + + if (Regions.FurnitureRegionMap.get(building.getObjectUUID()) == null) + return; + + Regions buildingRegion = Regions.FurnitureRegionMap.get(building.getObjectUUID()); + + if (!buildingRegion.isOutside()) + return; + + MoveToPointMsg moveMsg = new MoveToPointMsg(building); + + if (origin != null) + origin.sendMsg(moveMsg); + } + + public synchronized void HandleLoadForEnterWorld(PlayerCharacter player) { + + if (player == null) + return; + + ClientConnection origin = player.getClientConnection(); + + if (origin == null) + return; + + //Update static list + try { + updateStaticList(player, origin); + } catch (Exception e) { + Logger.error("InterestManager.updateAllStaticPlayers: " + player.getObjectUUID(), e); + } + + //Update mobile list + try { + updateMobileList(player, origin); + } catch (Exception e) { + Logger.error("InterestManager.updateAllMobilePlayers: " + player.getObjectUUID(), e); + } + } + + public synchronized void HandleLoadForTeleport(PlayerCharacter player) { + + if (player == null) + return; + + ClientConnection origin = player.getClientConnection(); + + if (origin == null) + return; + + //Update static list + try { + updateStaticList(player, origin); + } catch (Exception e) { + Logger.error("InterestManager.updateAllStaticPlayers: " + player.getObjectUUID(), e); + } + + //Update mobile list + try { + updateMobileList(player, origin); + } catch (Exception e) { + Logger.error("InterestManager.updateAllMobilePlayers: " + player.getObjectUUID(), e); + } + } + + public static void reloadCharacter(AbstractCharacter absChar) { + + UnloadObjectsMsg uom = new UnloadObjectsMsg(); + uom.addObject(absChar); + LoadCharacterMsg lcm = new LoadCharacterMsg(absChar, false); + + HashSet toSend = WorldGrid.getObjectsInRangePartial(absChar.getLoc(), MBServerStatics.CHARACTER_LOAD_RANGE, + MBServerStatics.MASK_PLAYER); + + PlayerCharacter pc = null; + + if (absChar.getObjectType().equals(GameObjectType.PlayerCharacter)) + pc = (PlayerCharacter) absChar; + + for (AbstractWorldObject awo : toSend) { + + PlayerCharacter pcc = (PlayerCharacter) awo; + + if (pcc == null) + continue; + + ClientConnection cc = SessionManager.getClientConnection(pcc); + + if (cc == null) + continue; + + if (pcc.getObjectUUID() == absChar.getObjectUUID()) + continue; + + else { + if (pc != null) + if (pcc.getSeeInvis() < pc.getHidden()) + continue; + + if (!cc.sendMsg(uom)) { + String classType = uom.getClass().getSimpleName(); + Logger.error("Failed to send message "); + } + + if (!cc.sendMsg(lcm)) { + String classType = lcm.getClass().getSimpleName(); + Logger.error("Failed to send message"); + } + } + } + } +} \ No newline at end of file diff --git a/src/engine/InterestManagement/RealmMap.java b/src/engine/InterestManagement/RealmMap.java new file mode 100644 index 00000000..b5604526 --- /dev/null +++ b/src/engine/InterestManagement/RealmMap.java @@ -0,0 +1,102 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.InterestManagement; + +/* This class is the main interface for Magicbane's +* Interest management facilities. +*/ + +import engine.Enum; +import engine.math.Vector3fImmutable; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.msg.TerritoryChangeMessage; +import engine.objects.City; +import engine.objects.PlayerCharacter; +import engine.objects.Realm; +import engine.server.MBServerStatics; +import engine.util.MapLoader; +import org.pmw.tinylog.Logger; + +import static engine.objects.Realm.getRealm; + +public class RealmMap { + + // Spatial hashmap. Used for detecting which Realm + // a player is currently in.. + + public static int[][] _realmImageMap; + + + public static int getRealmIDAtLocation(Vector3fImmutable pos) { + + int xBuckets = (int) ((pos.getX() / MBServerStatics.MAX_WORLD_WIDTH) * MBServerStatics.SPATIAL_HASH_BUCKETSX); + int yBuckets = (int) ((pos.getZ() / MBServerStatics.MAX_WORLD_HEIGHT) * MBServerStatics.SPATIAL_HASH_BUCKETSY); + + if (yBuckets < 0 || yBuckets >= MBServerStatics.SPATIAL_HASH_BUCKETSY + || xBuckets < 0 || xBuckets >= MBServerStatics.SPATIAL_HASH_BUCKETSX) { + Logger.error("WorldServerRealm.getRealmFromPosition", + "Invalid range; Z: " + yBuckets + ", X: " + xBuckets); + return 255; + } + + return RealmMap._realmImageMap[xBuckets][yBuckets]; + } + + public static Realm getRealmForCity(City city) { + Realm outRealm = null; + outRealm = city.getRealm(); + return outRealm; + } + + public static Realm getRealmAtLocation(Vector3fImmutable worldVector) { + + return getRealm(RealmMap.getRealmIDAtLocation(worldVector)); + + } + + public static void updateRealm(PlayerCharacter player){ + + int realmID = RealmMap.getRealmIDAtLocation(player.getLoc()); + + if (realmID != player.getLastRealmID()){ + player.setLastRealmID(realmID); + Realm realm = Realm.getRealm(realmID); + if (realm != null){ + if (realm.isRuled()){ + City city = realm.getRulingCity(); + if (city != null){ + TerritoryChangeMessage tcm = new TerritoryChangeMessage((PlayerCharacter)realm.getRulingCity().getOwner(),realm); + Dispatch dispatch = Dispatch.borrow(player, tcm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); + }else{ + TerritoryChangeMessage tcm = new TerritoryChangeMessage(null,realm); + Dispatch dispatch = Dispatch.borrow(player, tcm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); + } + + }else{ + TerritoryChangeMessage tcm = new TerritoryChangeMessage(null,realm); + Dispatch dispatch = Dispatch.borrow(player, tcm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); + } + } + + } + + + } + + public static void loadRealmImageMap() { + + RealmMap._realmImageMap = MapLoader.loadMap(); + + } + +} diff --git a/src/engine/InterestManagement/WorldGrid.java b/src/engine/InterestManagement/WorldGrid.java new file mode 100644 index 00000000..af932325 --- /dev/null +++ b/src/engine/InterestManagement/WorldGrid.java @@ -0,0 +1,312 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.InterestManagement; + +import engine.Enum.GridObjectType; +import engine.math.FastMath; +import engine.math.Vector3f; +import engine.math.Vector3fImmutable; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.LoadCharacterMsg; +import engine.net.client.msg.LoadStructureMsg; +import engine.net.client.msg.UnloadObjectsMsg; +import engine.objects.*; +import engine.server.MBServerStatics; + +import java.util.HashSet; +import java.util.concurrent.ConcurrentHashMap; + + +public class WorldGrid { + + public static ConcurrentHashMap[][] DynamicGridMap; + public static ConcurrentHashMap[][] StaticGridMap; + private static float dynamicBucketScale = 0.00390625f; // 256 bucket size, 1/256 + private static float staticBucketScale = 0.00390625f; + public static void startLoadJob() { + + Thread loadJobThread; + + + loadJobThread = new Thread(InterestManager.INTERESTMANAGER); + loadJobThread.setName("InterestManager"); + loadJobThread.start(); + } + + public static boolean moveWorldObject(AbstractWorldObject awo, Vector3fImmutable location) { + awo.setLoc(location); + return true; + } + + public static HashSet getInRange(Vector3f loc, double r) { + HashSet outbound = new HashSet<>(); + return outbound; + } + + public static HashSet getObjectsInRangePartial(Vector3fImmutable loc, double r, int mask) { + HashSet outbound = new HashSet<>(); + float scale; + + if ((mask & MBServerStatics.MASK_STATIC) != 0) + scale = WorldGrid.staticBucketScale; + else + scale = WorldGrid.dynamicBucketScale; + int gridX = (int) Math.abs(loc.x * scale); + int gridZ = (int)Math.abs(loc.z * scale); + int bucketSize = (int) (r *scale) + 1; + //start at top left most corner to scan. + int startingX = gridX - bucketSize; + int startingZ = gridZ + bucketSize; + + + + int limitX = Math.abs((int) (MBServerStatics.MAX_WORLD_WIDTH *scale)); + int limitZ = Math.abs((int) (MBServerStatics.MAX_WORLD_HEIGHT *scale)); //LimitZ is negative, remember to flip sign. + + if (startingX < 0) + startingX = 0; + + if (startingZ < 0) + startingZ = 0; + + if (startingX > limitX) + startingX = limitX; + + if (startingZ > limitZ) + startingZ = limitZ; + + int endX = startingX + (bucketSize * 2); + int endZ = startingZ - (bucketSize * 2); + + if (endX < 0) + endX = 0; + + if (endZ < 0) + endZ = 0; + + if (endX > limitX) + endX = limitX; + + if (endZ > limitZ) + endZ = limitZ; + + int auditMob = 0; + for (int x = startingX;x<=endX;x++){ + for (int z = startingZ;z >= endZ;z--){ + + ConcurrentHashMap gridMap; + + if ((MBServerStatics.MASK_STATIC & mask) != 0) + gridMap = WorldGrid.StaticGridMap[x][z]; + else + gridMap = WorldGrid.DynamicGridMap[x][z]; + for (AbstractWorldObject gridObject: gridMap.values()){ + if ((gridObject.getObjectTypeMask() & mask) == 0) + continue; + if (gridObject.getLoc().distanceSquared2D(loc) <= FastMath.sqr(r)) + outbound.add(gridObject); + } + } + } + return outbound; + } + + public static HashSet getObjectsInRangePartialNecroPets(Vector3fImmutable loc, double r) { + HashSet outbound = new HashSet<>(); + return outbound; + } + + public static HashSet getObjectsInRangeContains(Vector3fImmutable loc, double r, int mask) { + HashSet outbound = getObjectsInRangePartial(loc,r,mask); + return outbound; + } + + public static HashSet getObjectsInRangePartial(AbstractWorldObject awo, double range, int mask) { + return getObjectsInRangePartial(awo.getLoc(), range, mask); + } + + + public static void InitializeGridObjects(){ + + int dynamicWidth = (int) Math.abs(MBServerStatics.MAX_WORLD_WIDTH *WorldGrid.dynamicBucketScale); + int dynamicHeight = (int) Math.abs(MBServerStatics.MAX_WORLD_HEIGHT*WorldGrid.dynamicBucketScale); + + int staticWidth = (int) Math.abs(MBServerStatics.MAX_WORLD_WIDTH *WorldGrid.staticBucketScale); + int staticHeight = (int) Math.abs(MBServerStatics.MAX_WORLD_HEIGHT*WorldGrid.staticBucketScale); + WorldGrid.DynamicGridMap = new ConcurrentHashMap[dynamicWidth+ 1][dynamicHeight + 1]; + WorldGrid.StaticGridMap = new ConcurrentHashMap[staticWidth + 1][staticHeight + 1]; + //create new hash maps for each bucket + for (int x = 0; x<= staticWidth; x++) + for (int y = 0; y<= staticHeight; y++){ + WorldGrid.StaticGridMap[x][y] = new ConcurrentHashMap(); + } + + for (int x = 0; x<= dynamicWidth; x++) + for (int y = 0; y<= dynamicHeight; y++){ + WorldGrid.DynamicGridMap[x][y] = new ConcurrentHashMap(); + } + + } + + public static void RemoveWorldObject(AbstractWorldObject gridObject){ + + if (gridObject == null) + return; + AbstractWorldObject.RemoveFromWorldGrid(gridObject); + } + + public static boolean addObject(AbstractWorldObject gridObject, float x, float z){ + + if (gridObject == null) + return false; + + if (x > MBServerStatics.MAX_WORLD_WIDTH) + return false; + + if (z < MBServerStatics.MAX_WORLD_HEIGHT) + return false; + + if (x < 0) + return false; + if (z > 0) + return false; + + int gridX; + int gridZ; + + if (gridObject.getGridObjectType().equals(GridObjectType.STATIC)){ + gridX = Math.abs((int) (x *WorldGrid.staticBucketScale)); + gridZ = Math.abs((int) (z*WorldGrid.staticBucketScale)); + }else{ + gridX = Math.abs((int) (x *WorldGrid.dynamicBucketScale)); + gridZ = Math.abs((int) (z*WorldGrid.dynamicBucketScale)); + } + + + WorldGrid.RemoveWorldObject(gridObject); + + return AbstractWorldObject.AddToWorldGrid(gridObject, gridX, gridZ); + + + } + + public static void unloadObject(AbstractWorldObject awo) { + + UnloadObjectsMsg uom = new UnloadObjectsMsg(); + uom.addObject(awo); + DispatchMessage.sendToAllInRange(awo, uom); + } + + public static void loadObject(AbstractWorldObject awo) { + + LoadStructureMsg lsm; + LoadCharacterMsg lcm; + + switch (awo.getObjectType()) { + case Building: + lsm = new LoadStructureMsg(); + lsm.addObject((Building)awo); + DispatchMessage.sendToAllInRange(awo, lsm); + break; + case NPC: + lcm = new LoadCharacterMsg((NPC) awo, false); + DispatchMessage.sendToAllInRange(awo, lcm); + break; + case Mob: + lcm = new LoadCharacterMsg((Mob) awo, false); + DispatchMessage.sendToAllInRange(awo, lcm); + break; + default: + // *** Refactor: Log error? + break; + } + } + + public static void loadObject(AbstractWorldObject awo, ClientConnection origin) { + + LoadStructureMsg lsm; + LoadCharacterMsg lcm; + + switch (awo.getObjectType()) { + + case Building: + lsm = new LoadStructureMsg(); + lsm.addObject((Building)awo); + DispatchMessage.sendToAllInRange(awo, lsm); + break; + case NPC: + lcm = new LoadCharacterMsg((NPC) awo, false); + DispatchMessage.sendToAllInRange(awo, lcm); + break; + case Mob: + lcm = new LoadCharacterMsg((Mob) awo, false); + DispatchMessage.sendToAllInRange(awo, lcm); + break; + case PlayerCharacter: + lcm = new LoadCharacterMsg((PlayerCharacter) awo, false); + DispatchMessage.sendToAllInRange(awo, lcm); + break; + default: + // *** Refactor: Log error? + break; + } + } + + public static void unloadObject(AbstractWorldObject awo, + ClientConnection origin) { + UnloadObjectsMsg uom = new UnloadObjectsMsg(); + uom.addObject(awo); + DispatchMessage.sendToAllInRange(awo, uom); + } + + public static void addObject(AbstractWorldObject awo, PlayerCharacter pc) { + if (pc == null || awo == null) + return; + ClientConnection origin = pc.getClientConnection(); + if (origin == null) + return; + loadObject(awo, origin); + } + + public static void removeObject(AbstractWorldObject awo, PlayerCharacter pc) { + if (pc == null || awo == null) + return; + ClientConnection origin = pc.getClientConnection(); + if (origin == null) + return; + unloadObject(awo, origin); + } + + public static void updateObject(AbstractWorldObject awo, PlayerCharacter pc) { + if (pc == null || awo == null) + return; + ClientConnection origin = pc.getClientConnection(); + if (origin == null) + return; + unloadObject(awo, origin); + loadObject(awo, origin); + } + + public static void updateObject(AbstractWorldObject awo) { + if (awo == null) + return; + unloadObject(awo); + loadObject(awo); + } + + /* + * + */ + public static void removeObject(AbstractWorldObject awo) { + if (awo == null) + return; + unloadObject(awo); + } +} diff --git a/src/engine/ai/MobileFSM.java b/src/engine/ai/MobileFSM.java new file mode 100644 index 00000000..4861fa4e --- /dev/null +++ b/src/engine/ai/MobileFSM.java @@ -0,0 +1,1769 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.ai; + + +import engine.Enum; +import engine.Enum.DispatchChannel; +import engine.Enum.GameObjectType; +import engine.InterestManagement.WorldGrid; +import engine.ai.utilities.CombatUtilities; +import engine.ai.utilities.MovementUtilities; +import engine.gameManager.BuildingManager; +import engine.gameManager.CombatManager; +import engine.gameManager.MovementManager; +import engine.gameManager.PowersManager; +import engine.math.Vector3fImmutable; +import engine.net.DispatchMessage; +import engine.net.client.msg.PerformActionMsg; +import engine.net.client.msg.PowerProjectileMsg; +import engine.net.client.msg.UpdateStateMsg; +import engine.objects.*; +import engine.powers.PowersBase; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; + +import static engine.math.FastMath.sqr; + +public class MobileFSM { + + + public enum STATE { + Disabled, + Respawn, + Idle, + Awake, + Aggro, + Patrol, + Help, + Attack, + Home, + Dead, + Recalling, + Retaliate + } + + public static void run(Mob mob) { + if (mob == null) { + return; + } + + + STATE state = mob.getState(); + switch (state) { + case Idle: + if (mob.isAlive()) + mob.updateLocation(); + + if (mob.isPlayerGuard()) { + guardAwake(mob); + break; + } + + idle(mob); + break; + case Awake: + + if (mob.isAlive()) + mob.updateLocation(); + + if (mob.isPlayerGuard()) + guardAwake(mob); + else if (mob.isSiege() == false) { + if (mob.isPet()) + petAwake(mob); + else if (mob.isGuard()) + awakeNPCguard(mob); + else + awake(mob); + } + + break; + case Aggro: + + + if (mob.isAlive()) + mob.updateLocation(); + + if (mob.isPlayerGuard()) + guardAggro(mob, mob.getAggroTargetID()); + else + aggro(mob, mob.getAggroTargetID()); + break; + case Patrol: + + if (mob.isAlive()) + mob.updateLocation(); + + if (mob.isPlayerGuard()) + guardPatrol(mob); + else + patrol(mob); + break; + case Attack: + if (mob.isAlive()) + mob.updateLocation(); + + + if (!mob.isCombat()) { + mob.setCombat(true); + UpdateStateMsg rwss = new UpdateStateMsg(); + rwss.setPlayer(mob); + DispatchMessage.sendToAllInRange(mob, rwss); + } + + if (mob.isPlayerGuard()) + guardAttack(mob); + else if (mob.isPet() || mob.isSiege()) + petAttack(mob); + else if (mob.isGuard()) + guardAttackMob(mob); + else + mobAttack(mob); + break; + case Home: + if (mob.isPlayerGuard()) + guardHome(mob, mob.isWalkingHome()); + else + home(mob, mob.isWalkingHome()); + break; + case Dead: + dead(mob); + break; + case Respawn: + respawn(mob); + break; + case Recalling: + recalling(mob); + break; + case Retaliate: + retaliate(mob); + break; + } + } + + public static boolean setAwake(Mob aiAgent, boolean force) { + if (force) { + aiAgent.setState(STATE.Awake); + return true; + } + if (aiAgent.getState() == STATE.Idle) { + aiAgent.setState(STATE.Awake); + return true; + } + return false; + } + + public static boolean setAggro(Mob aiAgent, int targetID) { + if (aiAgent.getState() != STATE.Dead) { + aiAgent.setNoAggro(false); + aiAgent.setAggroTargetID(targetID); + aiAgent.setState(STATE.Aggro); + return true; + } + return false; + } + + public static Mob getMobile(int mobileID) { + return Mob.getFromCache(mobileID); + } + + private static void idle(Mob mob) { + + if (mob.getLoc().distanceSquared2D(mob.getBindLoc()) > sqr(2000)) { + + mob.setWalkingHome(false); + mob.setState(STATE.Home); + } + } + + + private static void awake(Mob aiAgent) { + if (!aiAgent.isAlive()) { + aiAgent.setState(STATE.Dead); + return; + } + + if (aiAgent.getLoc().distanceSquared2D(aiAgent.getBindLoc()) > sqr(2000)) { + aiAgent.setWalkingHome(false); + aiAgent.setState(STATE.Home); + return; + } + //Don't attempt to aggro if No aggro is on and aiAgent is not home yet. + if (aiAgent.isNoAggro() && aiAgent.isMoving()) { + return; + } + + //Mob stopped Moving let's turn aggro back on. + if (aiAgent.isNoAggro()) { + aiAgent.setNoAggro(false); + } + //no players currently have this mob loaded. return to IDLE. + if (aiAgent.getPlayerAgroMap().isEmpty()) { + aiAgent.setState(STATE.Idle); + return; + } + + + //currently npc guards wont patrol or aggro + if (aiAgent.isGuard()) { + return; + } + + //Get the Map for Players that loaded this mob. + + ConcurrentHashMap loadedPlayers = aiAgent.getPlayerAgroMap(); + + + if (!Enum.MobFlagType.AGGRESSIVE.elementOf(aiAgent.getMobBase().getFlags()) && aiAgent.getCombatTarget() == null) { + //attempt to patrol even if aiAgent isn't aggresive; + + int patrolRandom = ThreadLocalRandom.current().nextInt(1000); + if (patrolRandom <= MBServerStatics.AI_PATROL_DIVISOR) { + aiAgent.setState(STATE.Patrol); + } + return; + } + //aiAgent finished moving home, set aggro on. + + for (Entry playerEntry : loadedPlayers.entrySet()) { + int playerID = (int) playerEntry.getKey(); + PlayerCharacter loadedPlayer = PlayerCharacter.getFromCache(playerID); + + //Player is null, let's remove them from the list. + if (loadedPlayer == null) { + // Logger.error("MobileFSM", "Player with UID " + playerID + " returned null in mob.getPlayerAgroMap()"); + loadedPlayers.remove(playerID); + continue; + } + //Player is Dead, Mob no longer needs to attempt to aggro. Remove them from aggro map. + if (!loadedPlayer.isAlive()) { + loadedPlayers.remove(playerID); + continue; + } + //Can't see target, skip aggro. + if (!aiAgent.canSee(loadedPlayer)) { + continue; + } + + // No aggro for this race type + if (loadedPlayer.getRace().getRaceType().getAggroType().elementOf(aiAgent.getMobBase().getNoAggro())) + continue; + + + if (MovementUtilities.inRangeToAggro(aiAgent, loadedPlayer)) { + aiAgent.setAggroTargetID(playerID); + aiAgent.setState(STATE.Aggro); + return; + } + + + } + + int patrolRandom = ThreadLocalRandom.current().nextInt(1000); + if (patrolRandom <= MBServerStatics.AI_PATROL_DIVISOR) { + aiAgent.setState(STATE.Patrol); + } + + } + + private static void guardAttackMob(Mob aiAgent) { + if (!aiAgent.isAlive()) { + aiAgent.setState(STATE.Dead); + return; + } + + AbstractGameObject target = aiAgent.getCombatTarget(); + if (target == null) { + aiAgent.setState(STATE.Awake); + return; + } + + if (target.getObjectType().equals(GameObjectType.Mob) == false) { + aiAgent.setState(STATE.Awake); + return; + } + + if (target.equals(aiAgent)) { + aiAgent.setState(STATE.Awake); + return; + } + + Mob mob = (Mob) target; + + if (!mob.isAlive() || mob.getState() == STATE.Dead) { + aiAgent.setCombatTarget(null); + aiAgent.setState(STATE.Awake); + return; + } + + if (CombatUtilities.inRangeToAttack(aiAgent, mob)) { + //not time to attack yet. + if (System.currentTimeMillis() < aiAgent.getLastAttackTime()) { + return; + } + + if (!CombatUtilities.RunAIRandom()) + return; + + if (aiAgent.getRange() >= 30 && aiAgent.isMoving()) + return; + //no weapons, defualt mob attack speed 3 seconds. + ItemBase mainHand = aiAgent.getWeaponItemBase(true); + ItemBase offHand = aiAgent.getWeaponItemBase(false); + if (mainHand == null && offHand == null) { + CombatUtilities.combatCycle(aiAgent, mob, true, null); + int delay = 3000; + if (aiAgent.isSiege()) + delay = 11000; + aiAgent.setLastAttackTime(System.currentTimeMillis() + delay); + + } else + //TODO set offhand attack time. + if (aiAgent.getWeaponItemBase(true) != null) { + int attackDelay = 3000; + if (aiAgent.isSiege()) + attackDelay = 11000; + CombatUtilities.combatCycle(aiAgent, mob, true, aiAgent.getWeaponItemBase(true)); + aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay); + } else if (aiAgent.getWeaponItemBase(false) != null) { + int attackDelay = (int) (aiAgent.getSpeedHandTwo() * 100); + if (aiAgent.isSiege()) + attackDelay = 3000; + CombatUtilities.combatCycle(aiAgent, mob, false, aiAgent.getWeaponItemBase(false)); + aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay); + } + return; + + } + if (!MovementUtilities.updateMovementToCharacter(aiAgent, mob)) + return; + + if (!MovementUtilities.canMove(aiAgent)) + return; + + + if (CombatUtilities.inRangeToAttack2D(aiAgent, mob)) + return; + + + aiAgent.destination = MovementUtilities.GetDestinationToCharacter(aiAgent, mob); + + MovementUtilities.moveToLocation(aiAgent, aiAgent.destination, aiAgent.getRange()); + } + + private static void awakeNPCguard(Mob aiAgent) { + if (!aiAgent.isAlive()) { + aiAgent.setState(STATE.Dead); + return; + } + + // Player guards are bound to their city zone + // and recall when leaving it. + + if (aiAgent.getLoc().distanceSquared2D(aiAgent.getBindLoc()) > sqr(2000)) { + aiAgent.setWalkingHome(false); + aiAgent.setState(STATE.Home); + return; + } + + //Don't attempt to aggro if No aggro is on and aiAgent is not home yet. + //no players currently have this mob loaded. return to IDLE. + //currently npc guards wont patrol or aggro + //Get the Map for Players that loaded this mob. + + HashSet awoList = WorldGrid.getObjectsInRangePartial(aiAgent, 100, MBServerStatics.MASK_MOB); + + for (AbstractWorldObject awoMob : awoList) { + + //dont scan self. + if (aiAgent.equals(awoMob)) + continue; + + Mob mob = (Mob) awoMob; + //dont attack other guards + if (mob.isGuard()) + continue; + if (aiAgent.getLoc().distanceSquared2D(mob.getLoc()) > sqr(50)) + continue; + aiAgent.setCombatTarget(mob); + aiAgent.setState(STATE.Attack); + } + } + + private static void petAwake(Mob aiAgent) { + + if (!aiAgent.isAlive()) { + aiAgent.setState(STATE.Dead); + return; + } + + PlayerCharacter petOwner = aiAgent.getOwner(); + + if (petOwner == null) + return; + + //lets make mobs ai less twitchy, Don't call another movement until mob reaches it's destination. + if (aiAgent.isMoving()) + return; + + if (!MovementUtilities.canMove(aiAgent)) + return; + + if (petOwner.getLoc().distanceSquared2D(aiAgent.getLoc()) > MBServerStatics.AI_RECALL_RANGE * MBServerStatics.AI_RECALL_RANGE) { + aiAgent.teleport(petOwner.getLoc()); + return; + } + + if (petOwner.getLoc().distanceSquared2D(aiAgent.getLoc()) > 30 * 30) { + if (aiAgent.isMoving()) + return; + + if (!MovementUtilities.canMove(aiAgent)) + return; + if (aiAgent.getLoc().distanceSquared2D(petOwner.getLoc()) < aiAgent.getRange() * aiAgent.getRange()) + return; + + MovementUtilities.moveToLocation(aiAgent, petOwner.getLoc(), aiAgent.getRange()); + } + } + + private static void aggro(Mob aiAgent, int targetID) { + + if (!aiAgent.isAlive()) { + aiAgent.setState(STATE.Dead); + return; + } + + if (aiAgent.getLoc().distanceSquared2D(aiAgent.getBindLoc()) > sqr(2000)) { + aiAgent.setWalkingHome(false); + aiAgent.setState(STATE.Home); + return; + } + + if (!aiAgent.isCombat()) { + aiAgent.setCombat(true); + UpdateStateMsg rwss = new UpdateStateMsg(); + rwss.setPlayer(aiAgent); + DispatchMessage.sendToAllInRange(aiAgent, rwss); + } + + //a player got in aggro range. Move to player until in range of attack. + PlayerCharacter aggroTarget = PlayerCharacter.getFromCache(targetID); + + if (aggroTarget == null) { + // Logger.error("MobileFSM.aggro", "aggro target with UUID " + targetID + " returned null"); + aiAgent.getPlayerAgroMap().remove(targetID); + aiAgent.setAggroTargetID(0); + aiAgent.setState(STATE.Patrol); + return; + } + if (!aiAgent.canSee(aggroTarget)) { + aiAgent.setCombatTarget(null); + targetID = 0; + aiAgent.setState(STATE.Patrol); + return; + } + + if (!aggroTarget.isActive()) { + aiAgent.setCombatTarget(null); + targetID = 0; + aiAgent.setState(STATE.Patrol); + return; + } + + if (CombatUtilities.inRangeToAttack(aiAgent, aggroTarget)) { + aiAgent.setState(STATE.Attack); + attack(aiAgent, targetID); + return; + } + + if (!MovementUtilities.inRangeDropAggro(aiAgent, aggroTarget)) { + aiAgent.setAggroTargetID(0); + aiAgent.setCombatTarget(null); + MovementUtilities.moveToLocation(aiAgent, aiAgent.getTrueBindLoc(), 0); + aiAgent.setState(STATE.Awake); + return; + } + + if (!MovementUtilities.inRangeOfBindLocation(aiAgent)) { + aiAgent.setCombatTarget(null); + aiAgent.setAggroTargetID(0); + aiAgent.setState(STATE.Home); + return; + } + + //use this so mobs dont continue to try to move if they are underneath a flying target. only use 2D range check. + if (CombatUtilities.inRangeToAttack2D(aiAgent, aggroTarget)) + return; + + if (!MovementUtilities.updateMovementToCharacter(aiAgent, aggroTarget)) + return; + + if (!MovementUtilities.canMove(aiAgent)) + return; + + if (aiAgent.getLoc().distanceSquared2D(aggroTarget.getLoc()) < aiAgent.getRange() * aiAgent.getRange()) + return; + + aiAgent.destination = MovementUtilities.GetDestinationToCharacter(aiAgent, aggroTarget); + MovementUtilities.moveToLocation(aiAgent, aiAgent.destination, aiAgent.getRange()); + + } + + private static void petAttack(Mob aiAgent) { + + if (!aiAgent.isAlive()) { + aiAgent.setState(STATE.Dead); + return; + } + + AbstractGameObject target = aiAgent.getCombatTarget(); + + if (target == null) { + aiAgent.setState(STATE.Awake); + return; + } + + switch (target.getObjectType()) { + + case PlayerCharacter: + + PlayerCharacter player = (PlayerCharacter) target; + + if (!player.isActive()) { + aiAgent.setCombatTarget(null); + aiAgent.setState(STATE.Awake); + return; + } + + if (player.inSafeZone()) { + aiAgent.setCombatTarget(null); + aiAgent.setState(STATE.Awake); + return; + } + + handlePlayerAttackForPet(aiAgent, player); + + break; + case Building: + Building building = (Building) target; + petHandleBuildingAttack(aiAgent, building); + break; + case Mob: + Mob mob = (Mob) target; + handleMobAttackForPet(aiAgent, mob); + break; + } + } + + private static void mobAttack(Mob aiAgent) { + + if (!aiAgent.isAlive()) { + aiAgent.setState(STATE.Dead); + return; + } + + if (aiAgent.getLoc().distanceSquared2D(aiAgent.getBindLoc()) > sqr(2000)) { + + aiAgent.setWalkingHome(false); + aiAgent.setState(STATE.Home); + return; + } + + AbstractGameObject target = aiAgent.getCombatTarget(); + + if (target == null) { + aiAgent.setState(STATE.Patrol); + return; + } + + switch (target.getObjectType()) { + + case PlayerCharacter: + + PlayerCharacter player = (PlayerCharacter) target; + + if (!player.isActive()) { + aiAgent.setCombatTarget(null); + aiAgent.setState(STATE.Patrol); + return; + } + + if (aiAgent.isNecroPet() && player.inSafeZone()) { + aiAgent.setCombatTarget(null); + aiAgent.setState(STATE.Idle); + return; + } + + handlePlayerAttackForMob(aiAgent, player); + break; + case Building: + Building building = (Building) target; + petHandleBuildingAttack(aiAgent, building); + break; + case Mob: + Mob mob = (Mob) target; + handleMobAttackForMob(aiAgent, mob); + } + } + + private static void petHandleBuildingAttack(Mob aiAgent, Building building) { + + int buildingHitBox = (int) CombatManager.calcHitBox(building); + + if (building.getRank() == -1) { + aiAgent.setCombatTarget(null); + aiAgent.setState(STATE.Awake); + return; + } + + if (!building.isVulnerable()) { + aiAgent.setCombatTarget(null); + aiAgent.setState(STATE.Awake); + return; + } + + if (BuildingManager.getBuildingFromCache(building.getObjectUUID()) == null) { + aiAgent.setCombatTarget(null); + aiAgent.setState(STATE.Awake); + return; + } + + if (building.getParentZone() != null && building.getParentZone().isPlayerCity()) { + + for (Mob mob : building.getParentZone().zoneMobSet) { + + if (!mob.isPlayerGuard()) + continue; + + if (mob.getCombatTarget() != null) + continue; + + if (mob.getGuild() != null && building.getGuild() != null) + if (!Guild.sameGuild(mob.getGuild().getNation(), building.getGuild().getNation())) + continue; + + mob.setCombatTarget(aiAgent); + mob.setState(STATE.Attack); + } + } + + if (CombatUtilities.inRangeToAttack(aiAgent, building)) { + //not time to attack yet. + + if (!CombatUtilities.RunAIRandom()) + return; + + if (System.currentTimeMillis() < aiAgent.getLastAttackTime()) + return; + + if (aiAgent.getRange() >= 30 && aiAgent.isMoving()) + return; + + //reset attack animation + if (aiAgent.isSiege()) + MovementManager.sendRWSSMsg(aiAgent); + + // Fire siege balls + // TODO: Fix animations not following stone + + //no weapons, defualt mob attack speed 3 seconds. + ItemBase mainHand = aiAgent.getWeaponItemBase(true); + ItemBase offHand = aiAgent.getWeaponItemBase(false); + + if (mainHand == null && offHand == null) { + + CombatUtilities.combatCycle(aiAgent, building, true, null); + int delay = 3000; + + if (aiAgent.isSiege()) + delay = 15000; + + aiAgent.setLastAttackTime(System.currentTimeMillis() + delay); + } else + //TODO set offhand attack time. + if (aiAgent.getWeaponItemBase(true) != null) { + + int attackDelay = 3000; + + if (aiAgent.isSiege()) + attackDelay = 15000; + + CombatUtilities.combatCycle(aiAgent, building, true, aiAgent.getWeaponItemBase(true)); + aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay); + + } else if (aiAgent.getWeaponItemBase(false) != null) { + + int attackDelay = 3000; + + if (aiAgent.isSiege()) + attackDelay = 15000; + + CombatUtilities.combatCycle(aiAgent, building, false, aiAgent.getWeaponItemBase(false)); + aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay); + } + + if (aiAgent.isSiege()) { + PowerProjectileMsg ppm = new PowerProjectileMsg(aiAgent, building); + ppm.setRange(50); + DispatchMessage.dispatchMsgToInterestArea(aiAgent, ppm, DispatchChannel.SECONDARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); + } + return; + } + + //Outside of attack Range, Move to players predicted loc. + + if (!aiAgent.isMoving()) + if (MovementUtilities.canMove(aiAgent)) + MovementUtilities.moveToLocation(aiAgent, building.getLoc(), aiAgent.getRange() + buildingHitBox); + } + + private static void handlePlayerAttackForPet(Mob aiAgent, PlayerCharacter player) { + + if (aiAgent.getMobBase().getSeeInvis() < player.getHidden()) { + aiAgent.setCombatTarget(null); + aiAgent.setState(STATE.Awake); + return; + } + + if (!player.isAlive()) { + aiAgent.setCombatTarget(null); + aiAgent.setState(STATE.Awake); + return; + } + + if (CombatUtilities.inRangeToAttack(aiAgent, player)) { + //not time to attack yet. + if (System.currentTimeMillis() < aiAgent.getLastAttackTime()) + return; + + if (!CombatUtilities.RunAIRandom()) + return; + + if (aiAgent.getRange() >= 30 && aiAgent.isMoving()) + return; + // add timer for last attack. + //player.setTimeStamp("LastCombatPlayer", System.currentTimeMillis()); + //no weapons, defualt mob attack speed 3 seconds. + ItemBase mainHand = aiAgent.getWeaponItemBase(true); + ItemBase offHand = aiAgent.getWeaponItemBase(false); + + if (mainHand == null && offHand == null) { + + CombatUtilities.combatCycle(aiAgent, player, true, null); + + int delay = 3000; + + if (aiAgent.isSiege()) + delay = 11000; + + aiAgent.setLastAttackTime(System.currentTimeMillis() + delay); + } + //TODO set offhand attack time. + + if (aiAgent.getWeaponItemBase(true) != null) { + + int attackDelay = 3000; + + if (aiAgent.isSiege()) + attackDelay = 11000; + + CombatUtilities.combatCycle(aiAgent, player, true, aiAgent.getWeaponItemBase(true)); + aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay); + + } else if (aiAgent.getWeaponItemBase(false) != null) { + + int attackDelay = (int) (aiAgent.getSpeedHandTwo() * 100); + + if (aiAgent.isSiege()) + attackDelay = 3000; + + CombatUtilities.combatCycle(aiAgent, player, false, aiAgent.getWeaponItemBase(false)); + aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay); + } + return; + } + + if (!MovementUtilities.updateMovementToCharacter(aiAgent, player)) + return; + + //out of range to attack move + if (!MovementUtilities.canMove(aiAgent)) + return; + + aiAgent.destination = MovementUtilities.GetDestinationToCharacter(aiAgent, player); + MovementUtilities.moveToLocation(aiAgent, aiAgent.destination, aiAgent.getRange()); + } + + private static void handlePlayerAttackForMob(Mob aiAgent, PlayerCharacter player) { + + if (aiAgent.getMobBase().getSeeInvis() < player.getHidden()) { + aiAgent.setCombatTarget(null); + aiAgent.setState(STATE.Awake); + return; + } + + if (!player.isAlive()) { + aiAgent.setCombatTarget(null); + aiAgent.setState(STATE.Awake); + return; + } + + if (aiAgent.getLastMobPowerToken() != 0) { + + PowersBase mobPower = PowersManager.getPowerByToken(aiAgent.getLastMobPowerToken()); + + if (System.currentTimeMillis() > aiAgent.getTimeStamp("FInishCast")) { + PerformActionMsg msg = PowersManager.createPowerMsg(mobPower, 40, aiAgent, player); + msg.setUnknown04(2); + PowersManager.finishUseMobPower(msg, aiAgent, 0, 0); + aiAgent.setLastMobPowerToken(0); + aiAgent.setIsCasting(false); + } + return; + } + + if (System.currentTimeMillis() > aiAgent.getTimeStamp("CallForHelp")) { + CombatUtilities.CallForHelp(aiAgent); + aiAgent.getTimestamps().put("CallForHelp", System.currentTimeMillis() + 60000); + } + + HashMap staticPowers = aiAgent.getMobBase().getStaticPowers(); + + if (staticPowers != null && !staticPowers.isEmpty()) { + int chance = ThreadLocalRandom.current().nextInt(300); + + if (chance <= 1) { + + int randomPower = ThreadLocalRandom.current().nextInt(staticPowers.size()); + int powerToken = (int) staticPowers.keySet().toArray()[randomPower]; + PowersBase pb = PowersManager.getPowerByToken(powerToken); + + if (pb == null) + return; + + if (System.currentTimeMillis() > aiAgent.getTimeStamp(pb.getIDString())) { + + PowersManager.useMobPower(aiAgent, player, pb, staticPowers.get(powerToken)); + + int cooldown = pb.getRecycleTime(staticPowers.get(powerToken)); + aiAgent.getTimestamps().put(pb.getIDString(), System.currentTimeMillis() + cooldown + (pb.getToken() == 429023263 ? 10000 : 120000)); + return; + } + } + } + + if (!MovementUtilities.inRangeOfBindLocation(aiAgent)) { + aiAgent.setCombatTarget(null); + aiAgent.setAggroTargetID(0); + aiAgent.setWalkingHome(false); + aiAgent.setState(STATE.Home); + return; + } + + if (!MovementUtilities.inRangeDropAggro(aiAgent, player)) { + aiAgent.setAggroTargetID(0); + aiAgent.setCombatTarget(null); + MovementUtilities.moveToLocation(aiAgent, aiAgent.getTrueBindLoc(), 0); + aiAgent.setState(STATE.Awake); + return; + } + + if (CombatUtilities.inRangeToAttack(aiAgent, player)) { + + //no weapons, defualt mob attack speed 3 seconds. + + if (System.currentTimeMillis() < aiAgent.getLastAttackTime()) + return; + + if (!CombatUtilities.RunAIRandom()) + return; + + // ranged mobs cant attack while running. skip until they finally stop. + if (aiAgent.getRange() >= 30 && aiAgent.isMoving()) + return; + + // add timer for last attack. + // player.setTimeStamp("LastCombatPlayer", System.currentTimeMillis()); + ItemBase mainHand = aiAgent.getWeaponItemBase(true); + ItemBase offHand = aiAgent.getWeaponItemBase(false); + + if (mainHand == null && offHand == null) { + + CombatUtilities.combatCycle(aiAgent, player, true, null); + int delay = 3000; + + if (aiAgent.isSiege()) + delay = 11000; + + aiAgent.setLastAttackTime(System.currentTimeMillis() + delay); + + } else + //TODO set offhand attack time. + if (aiAgent.getWeaponItemBase(true) != null) { + + int delay = 3000; + + if (aiAgent.isSiege()) + delay = 11000; + + CombatUtilities.combatCycle(aiAgent, player, true, aiAgent.getWeaponItemBase(true)); + aiAgent.setLastAttackTime(System.currentTimeMillis() + delay); + } else if (aiAgent.getWeaponItemBase(false) != null) { + + int attackDelay = 3000; + + if (aiAgent.isSiege()) + attackDelay = 11000; + + CombatUtilities.combatCycle(aiAgent, player, false, aiAgent.getWeaponItemBase(false)); + aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay); + } + return; + } + + if (!MovementUtilities.updateMovementToCharacter(aiAgent, player)) + return; + + if (!MovementUtilities.canMove(aiAgent)) + return; + + //this stops mobs from attempting to move while they are underneath a player. + if (CombatUtilities.inRangeToAttack2D(aiAgent, player)) + return; + + aiAgent.destination = MovementUtilities.GetDestinationToCharacter(aiAgent, player); + MovementUtilities.moveToLocation(aiAgent, aiAgent.destination, aiAgent.getRange()); + + } + + private static void handleMobAttackForPet(Mob aiAgent, Mob mob) { + + if (!mob.isAlive()) { + aiAgent.setCombatTarget(null); + aiAgent.setState(STATE.Awake); + return; + } + + if (CombatUtilities.inRangeToAttack(aiAgent, mob)) { + //not time to attack yet. + if (System.currentTimeMillis() < aiAgent.getLastAttackTime()) + return; + + if (!CombatUtilities.RunAIRandom()) + return; + + if (aiAgent.getRange() >= 30 && aiAgent.isMoving()) + return; + + //no weapons, defualt mob attack speed 3 seconds. + ItemBase mainHand = aiAgent.getWeaponItemBase(true); + ItemBase offHand = aiAgent.getWeaponItemBase(false); + + if (mainHand == null && offHand == null) { + + CombatUtilities.combatCycle(aiAgent, mob, true, null); + + int delay = 3000; + + if (aiAgent.isSiege()) + delay = 11000; + + aiAgent.setLastAttackTime(System.currentTimeMillis() + delay); + + } else + //TODO set offhand attack time. + if (aiAgent.getWeaponItemBase(true) != null) { + + int attackDelay = 3000; + + if (aiAgent.isSiege()) + attackDelay = 11000; + + CombatUtilities.combatCycle(aiAgent, mob, true, aiAgent.getWeaponItemBase(true)); + aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay); + + } else if (aiAgent.getWeaponItemBase(false) != null) { + + int attackDelay = (int) (aiAgent.getSpeedHandTwo() * 100); + + if (aiAgent.isSiege()) + attackDelay = 3000; + + CombatUtilities.combatCycle(aiAgent, mob, false, aiAgent.getWeaponItemBase(false)); + aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay); + } + return; + } + + if (!MovementUtilities.updateMovementToCharacter(aiAgent, mob)) + return; + + if (!MovementUtilities.canMove(aiAgent)) + return; + + if (CombatUtilities.inRangeToAttack2D(aiAgent, mob)) + return; + + aiAgent.destination = MovementUtilities.GetDestinationToCharacter(aiAgent, mob); + MovementUtilities.moveToLocation(aiAgent, aiAgent.destination, aiAgent.getRange()); + } + + private static void handleMobAttackForMob(Mob aiAgent, Mob mob) { + + + if (!mob.isAlive()) { + aiAgent.setCombatTarget(null); + aiAgent.setState(STATE.Awake); + return; + } + + if (CombatUtilities.inRangeToAttack(aiAgent, mob)) { + //not time to attack yet. + if (System.currentTimeMillis() < aiAgent.getLastAttackTime()) { + return; + } + + if (!CombatUtilities.RunAIRandom()) + return; + + if (aiAgent.getRange() >= 30 && aiAgent.isMoving()) + return; + //no weapons, defualt mob attack speed 3 seconds. + ItemBase mainHand = aiAgent.getWeaponItemBase(true); + ItemBase offHand = aiAgent.getWeaponItemBase(false); + + if (mainHand == null && offHand == null) { + + CombatUtilities.combatCycle(aiAgent, mob, true, null); + int delay = 3000; + + if (aiAgent.isSiege()) + delay = 11000; + + aiAgent.setLastAttackTime(System.currentTimeMillis() + delay); + } else + //TODO set offhand attack time. + if (aiAgent.getWeaponItemBase(true) != null) { + + int attackDelay = 3000; + + if (aiAgent.isSiege()) + attackDelay = 11000; + + CombatUtilities.combatCycle(aiAgent, mob, true, aiAgent.getWeaponItemBase(true)); + aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay); + + } else if (aiAgent.getWeaponItemBase(false) != null) { + + int attackDelay = 3000; + + if (aiAgent.isSiege()) + attackDelay = 11000; + + CombatUtilities.combatCycle(aiAgent, mob, false, aiAgent.getWeaponItemBase(false)); + aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay); + } + return; + } + + //use this so mobs dont continue to try to move if they are underneath a flying target. only use 2D range check. + if (CombatUtilities.inRangeToAttack2D(aiAgent, mob)) + return; + + if (!MovementUtilities.updateMovementToCharacter(aiAgent, mob)) + return; + + //out of range to attack move + if (!MovementUtilities.canMove(aiAgent)) + return; + + aiAgent.destination = MovementUtilities.GetDestinationToCharacter(aiAgent, mob); + MovementUtilities.moveToLocation(aiAgent, aiAgent.destination, aiAgent.getRange()); + } + + private static void attack(Mob aiAgent, int targetID) { + + //in range to attack, start attacking now! + if (!aiAgent.isAlive()) { + aiAgent.setState(STATE.Dead); + return; + } + + PlayerCharacter aggroTarget = PlayerCharacter.getFromCache(targetID); + + if (aggroTarget == null) { + // Logger.error("MobileFSM.aggro", "aggro target with UUID " + targetID + " returned null"); + aiAgent.getPlayerAgroMap().remove(targetID); + aiAgent.setAggroTargetID(0); + aiAgent.setState(STATE.Patrol); + return; + } + + if (aiAgent.getMobBase().getSeeInvis() < aggroTarget.getHidden()) { + aiAgent.setAggroTargetID(0); + aiAgent.setCombatTarget(null); + aiAgent.setState(STATE.Patrol); + return; + } + + if (!aggroTarget.isAlive()) { + aiAgent.setAggroTargetID(0); + aiAgent.setCombatTarget(null); + aiAgent.setState(STATE.Patrol); + return; + } + + HashMap staticPowers = aiAgent.getMobBase().getStaticPowers(); + + if (staticPowers != null && !staticPowers.isEmpty()) { + + int chance = ThreadLocalRandom.current().nextInt(100); + + if (chance <= MBServerStatics.AI_POWER_DIVISOR) { + + ArrayList powerList = new ArrayList<>(); + + for (Integer key : staticPowers.keySet()) { + powerList.add(key); + } + + int randomPower = ThreadLocalRandom.current().nextInt(powerList.size()); + int powerToken = powerList.get(randomPower); + + PowersBase pb = PowersManager.getPowerByToken(powerToken); + + if (pb != null) + PowersManager.useMobPower(aiAgent, aggroTarget, pb, staticPowers.get(powerToken)); + + return; + } + } + + if (!MovementUtilities.inRangeOfBindLocation(aiAgent)) { + aiAgent.setCombatTarget(null); + aiAgent.setAggroTargetID(0); + aiAgent.setWalkingHome(false); + aiAgent.setState(STATE.Home); + return; + } + + if (!MovementUtilities.inRangeDropAggro(aiAgent, aggroTarget)) { + aiAgent.setAggroTargetID(0); + aiAgent.setCombatTarget(null); + MovementUtilities.moveToLocation(aiAgent, aiAgent.getTrueBindLoc(), 0); + aiAgent.setState(STATE.Awake); + return; + } + + + if (CombatUtilities.inRangeToAttack(aiAgent, aggroTarget)) { + + if (aiAgent.getCombatTarget() == null) + aiAgent.setCombatTarget(aggroTarget); + + if (!CombatUtilities.RunAIRandom()) + return; + + //not time to attack yet. + if (System.currentTimeMillis() < aiAgent.getLastAttackTime()) + return; + + if (aiAgent.getRange() >= 30 && aiAgent.isMoving()) + return; + + //no weapons, defualt mob attack speed 3 seconds. + ItemBase mainHand = aiAgent.getWeaponItemBase(true); + ItemBase offHand = aiAgent.getWeaponItemBase(false); + + if (mainHand == null && offHand == null) { + CombatUtilities.combatCycle(aiAgent, aggroTarget, true, null); + aiAgent.setLastAttackTime(System.currentTimeMillis() + 3000); + } else + //TODO set offhand attack time. + if (aiAgent.getWeaponItemBase(true) != null) { + + int attackDelay = 3000; + + CombatUtilities.combatCycle(aiAgent, aggroTarget, true, aiAgent.getWeaponItemBase(true)); + aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay); + } else if (aiAgent.getWeaponItemBase(false) != null) { + + int attackDelay = 3000; + + CombatUtilities.combatCycle(aiAgent, aggroTarget, false, aiAgent.getWeaponItemBase(false)); + aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay); + } + return; + } + + //use this so mobs dont continue to try to move if they are underneath a flying target. only use 2D range check. + if (CombatUtilities.inRangeToAttack2D(aiAgent, aggroTarget)) + return; + + if (!MovementUtilities.canMove(aiAgent)) + return; + + if (!MovementUtilities.updateMovementToCharacter(aiAgent, aggroTarget)) + return; + + aiAgent.destination = MovementUtilities.GetDestinationToCharacter(aiAgent, aggroTarget); + MovementUtilities.moveToLocation(aiAgent, aiAgent.destination, aiAgent.getRange()); + } + + private static void home(Mob aiAgent, boolean walk) { + + //recall home. + MovementManager.translocate(aiAgent, aiAgent.getBindLoc(), null); + aiAgent.setAggroTargetID(0); + aiAgent.setCombatTarget(null); + aiAgent.setState(STATE.Awake); + } + + private static void recall(Mob aiAgent) { + //recall home. + PowersBase recall = PowersManager.getPowerByToken(-1994153779); + PowersManager.useMobPower(aiAgent, aiAgent, recall, 40); + aiAgent.setState(MobileFSM.STATE.Recalling); + } + + private static void recalling(Mob aiAgent) { + //recall home. + if (aiAgent.getLoc() == aiAgent.getBindLoc()) + aiAgent.setState(STATE.Awake); + + if (aiAgent.getLoc().distanceSquared2D(aiAgent.getBindLoc()) > sqr(2000)) { + + aiAgent.setWalkingHome(false); + aiAgent.setState(STATE.Home); + } + } + + private static void patrol(Mob aiAgent) { + + MobBase mobbase = aiAgent.getMobBase(); + + if (mobbase != null && (Enum.MobFlagType.SENTINEL.elementOf(mobbase.getFlags()) || !Enum.MobFlagType.CANROAM.elementOf(mobbase.getFlags()))) { + aiAgent.setState(STATE.Awake); + return; + } + + if (MovementUtilities.canMove(aiAgent) && !aiAgent.isMoving()) { + + float patrolRadius = aiAgent.getSpawnRadius(); + + if (patrolRadius > 256) + patrolRadius = 256; + + if (patrolRadius < 60) + patrolRadius = 60; + + MovementUtilities.aiMove(aiAgent, Vector3fImmutable.getRandomPointInCircle(aiAgent.getBindLoc(), patrolRadius), true); + } + aiAgent.setState(STATE.Awake); + } + + public static void goHome(Mob aiAgent, boolean walk) { + + if (aiAgent.getState() != STATE.Dead) { + aiAgent.setWalkingHome(walk); + aiAgent.setAggroTargetID(0); + aiAgent.setState(STATE.Home); + } + } + + private static void dead(Mob aiAgent) { + //Despawn Timer with Loot currently in inventory. + if (aiAgent.getCharItemManager().getInventoryCount() > 0) { + if (System.currentTimeMillis() > aiAgent.getDeathTime() + MBServerStatics.DESPAWN_TIMER_WITH_LOOT) { + aiAgent.despawn(); + //update time of death after mob despawns so respawn time happens after mob despawns. + aiAgent.setDeathTime(System.currentTimeMillis()); + aiAgent.setState(STATE.Respawn); + } + + //No items in inventory. + } else { + //Mob's Loot has been looted. + if (aiAgent.isHasLoot()) { + if (System.currentTimeMillis() > aiAgent.getDeathTime() + MBServerStatics.DESPAWN_TIMER_ONCE_LOOTED) { + aiAgent.despawn(); + //update time of death after mob despawns so respawn time happens after mob despawns. + aiAgent.setDeathTime(System.currentTimeMillis()); + aiAgent.setState(STATE.Respawn); + } + //Mob never had Loot. + } else { + if (System.currentTimeMillis() > aiAgent.getDeathTime() + MBServerStatics.DESPAWN_TIMER) { + aiAgent.despawn(); + //update time of death after mob despawns so respawn time happens after mob despawns. + aiAgent.setDeathTime(System.currentTimeMillis()); + aiAgent.setState(STATE.Respawn); + } + } + } + } + + private static void guardAwake(Mob aiAgent) { + + if (!aiAgent.isAlive()) { + aiAgent.setState(STATE.Dead); + return; + } + + if (aiAgent.getLoc().distanceSquared2D(aiAgent.getBindLoc()) > sqr(2000)) { + + aiAgent.setWalkingHome(false); + aiAgent.setState(STATE.Home); + return; + } + + //Don't attempt to aggro if No aggro is on and aiAgent is not home yet. + + //Mob stopped Moving let's turn aggro back on. + if (aiAgent.isNoAggro()) + aiAgent.setNoAggro(false); + + // do nothing if no players are around. + if (aiAgent.getPlayerAgroMap().isEmpty()) + return; + + //Get the Map for Players that loaded this mob. + + ConcurrentHashMap loadedPlayers = aiAgent.getPlayerAgroMap(); + + //no players currently have this mob loaded. return to IDLE. + //aiAgent finished moving home, set aggro on. + + for (Entry playerEntry : loadedPlayers.entrySet()) { + + int playerID = (int) playerEntry.getKey(); + + PlayerCharacter loadedPlayer = PlayerCharacter.getFromCache(playerID); + + //Player is null, let's remove them from the list. + if (loadedPlayer == null) { + // Logger.error("MobileFSM", "Player with UID " + playerID + " returned null in mob.getPlayerAgroMap()"); + loadedPlayers.remove(playerID); + continue; + } + + //Player is Dead, Mob no longer needs to attempt to aggro. Remove them from aggro map. + if (!loadedPlayer.isAlive()) { + loadedPlayers.remove(playerID); + continue; + } + + //Can't see target, skip aggro. + if (!aiAgent.canSee(loadedPlayer)) { + continue; + } + + //Guard aggro check + + boolean aggro = false; + Zone cityZone = aiAgent.getParentZone(); + + if (cityZone != null) { + City city = City.GetCityFromCache(cityZone.getPlayerCityUUID()); + if (city != null) { + + Building tol = city.getTOL(); + + if (tol != null) { + if (tol.reverseKOS) { + + aggro = true; + + for (Condemned condemned : tol.getCondemned().values()) { + switch (condemned.getFriendType()) { + case Condemned.NATION: + if (loadedPlayer.getGuild() != null && loadedPlayer.getGuild().getNation() != null) + if (loadedPlayer.getGuild().getNation().getObjectUUID() == condemned.getGuildUID()) + if (condemned.isActive()) + aggro = false; + break; + case Condemned.GUILD: + if (loadedPlayer.getGuild() != null) + if (loadedPlayer.getGuild().getObjectUUID() == condemned.getGuildUID()) + if (condemned.isActive()) + aggro = false; + break; + case Condemned.INDIVIDUAL: + if (loadedPlayer.getObjectUUID() == condemned.getPlayerUID()) + if (condemned.isActive()) + aggro = false; + break; + } + } + } else { + aggro = false; + + for (Condemned condemned : tol.getCondemned().values()) { + switch (condemned.getFriendType()) { + case Condemned.NATION: + if (loadedPlayer.getGuild() != null && loadedPlayer.getGuild().getNation() != null) + if (loadedPlayer.getGuild().getNation().getObjectUUID() == condemned.getGuildUID()) + if (condemned.isActive()) + aggro = true; + break; + case Condemned.GUILD: + if (loadedPlayer.getGuild() != null) + if (loadedPlayer.getGuild().getObjectUUID() == condemned.getGuildUID()) + if (condemned.isActive()) + aggro = true; + break; + case Condemned.INDIVIDUAL: + if (loadedPlayer.getObjectUUID() == condemned.getPlayerUID()) + if (condemned.isActive()) + aggro = true; + break; + } + } + } + } + } + + if (loadedPlayer.getGuild() != null && loadedPlayer.getGuild().getNation() != null && city.getGuild() != null) + if (Guild.sameGuild(loadedPlayer.getGuild().getNation(), city.getGuild().getNation())) + aggro = false; + + } + + //lets make sure we dont aggro players in the nation. + + if (aggro) { + if (CombatUtilities.inRangeToAttack(aiAgent, loadedPlayer)) { + aiAgent.setAggroTargetID(playerID); + aiAgent.setState(STATE.Aggro); + return; + } + + if (MovementUtilities.inRangeToAggro(aiAgent, loadedPlayer)) { + aiAgent.setAggroTargetID(playerID); + aiAgent.setState(STATE.Aggro); + return; + } + } + } + + //attempt to patrol even if aiAgent isn't aggresive; + if (aiAgent.isMoving() == false) + aiAgent.setState(STATE.Patrol); + } + + private static void guardAggro(Mob aiAgent, int targetID) { + + if (!aiAgent.isAlive()) { + aiAgent.setState(STATE.Dead); + return; + } + + if (!aiAgent.isCombat()) { + aiAgent.setCombat(true); + UpdateStateMsg rwss = new UpdateStateMsg(); + rwss.setPlayer(aiAgent); + DispatchMessage.sendToAllInRange(aiAgent, rwss); + } + + //a player got in aggro range. Move to player until in range of attack. + PlayerCharacter aggroTarget = PlayerCharacter.getFromCache(targetID); + + if (aggroTarget == null) { + aiAgent.setState(STATE.Patrol); + return; + } + + if (!aiAgent.canSee(aggroTarget)) { + aiAgent.setCombatTarget(null); + targetID = 0; + aiAgent.setState(STATE.Patrol); + return; + } + + if (!aggroTarget.isActive()) { + aiAgent.setCombatTarget(null); + targetID = 0; + aiAgent.setState(STATE.Patrol); + return; + } + + if (System.currentTimeMillis() > aiAgent.getTimeStamp("CallForHelp")) { + CombatUtilities.CallForHelp(aiAgent); + aiAgent.getTimestamps().put("CallForHelp", System.currentTimeMillis() + 60000); + } + + + if (CombatUtilities.inRangeToAttack(aiAgent, aggroTarget)) { + aiAgent.setCombatTarget(aggroTarget); + aiAgent.setState(STATE.Attack); + guardAttack(aiAgent); + return; + } + + //use this so mobs dont continue to try to move if they are underneath a flying target. only use 2D range check. + if (CombatUtilities.inRangeToAttack2D(aiAgent, aggroTarget)) + return; + + + if (!MovementUtilities.canMove(aiAgent)) + return; + + if (!MovementUtilities.inRangeDropAggro(aiAgent, aggroTarget)) { + aiAgent.setAggroTargetID(0); + aiAgent.setCombatTarget(null); + MovementUtilities.moveToLocation(aiAgent, aiAgent.getTrueBindLoc(), 0); + aiAgent.setState(STATE.Awake); + return; + } + + if (!MovementUtilities.inRangeOfBindLocation(aiAgent)) { + aiAgent.setCombatTarget(null); + aiAgent.setAggroTargetID(0); + aiAgent.setWalkingHome(false); + aiAgent.setState(STATE.Home); + return; + } + + if (!MovementUtilities.updateMovementToCharacter(aiAgent, aggroTarget)) + return; + + //Outside of attack Range, Move to players predicted loc. + + if (aiAgent.getLoc().distanceSquared2D(aggroTarget.getLoc()) < aiAgent.getRange() * aiAgent.getRange()) + return; + aiAgent.destination = MovementUtilities.GetDestinationToCharacter(aiAgent, aggroTarget); + MovementUtilities.moveToLocation(aiAgent, aiAgent.destination, aiAgent.getRange()); + + } + + private static void guardPatrol(Mob aiAgent) { + + if (aiAgent.getPlayerAgroMap().isEmpty()) { + aiAgent.setState(STATE.Awake); + return; + } + + if (aiAgent.isCombat() && aiAgent.getCombatTarget() == null) { + aiAgent.setCombat(false); + UpdateStateMsg rwss = new UpdateStateMsg(); + rwss.setPlayer(aiAgent); + DispatchMessage.sendToAllInRange(aiAgent, rwss); + } + + if (aiAgent.getNpcOwner() == null) { + + if (!aiAgent.isWalk() || (aiAgent.isCombat() && aiAgent.getCombatTarget() == null)) { + aiAgent.setWalkMode(true); + aiAgent.setCombat(false); + UpdateStateMsg rwss = new UpdateStateMsg(); + rwss.setPlayer(aiAgent); + DispatchMessage.sendToAllInRange(aiAgent, rwss); + } + + if (aiAgent.isMoving()) { + aiAgent.setState(STATE.Awake); + return; + } + + Building barrack = aiAgent.getBuilding(); + + if (barrack == null) { + aiAgent.setState(STATE.Awake); + return; + } + + int patrolRandom = ThreadLocalRandom.current().nextInt(1000); + + if (patrolRandom <= 10) { + int buildingHitBox = (int) CombatManager.calcHitBox(barrack); + if (MovementUtilities.canMove(aiAgent)) { + MovementUtilities.aiMove(aiAgent, MovementUtilities.randomPatrolLocation(aiAgent, aiAgent.getBindLoc(), buildingHitBox * 2), true); + } + } + + aiAgent.setState(STATE.Awake); + return; + + } + + if (!aiAgent.isWalk() || (aiAgent.isCombat() && aiAgent.getCombatTarget() == null)) { + aiAgent.setWalkMode(true); + aiAgent.setCombat(false); + UpdateStateMsg rwss = new UpdateStateMsg(); + rwss.setPlayer(aiAgent); + DispatchMessage.sendToAllInRange(aiAgent, rwss); + + } + + Building barrack = ((Mob) aiAgent.getNpcOwner()).getBuilding(); + + if (barrack == null) { + aiAgent.setState(STATE.Awake); + return; + } + + if (barrack.getPatrolPoints() == null) { + aiAgent.setState(STATE.Awake); + return; + } + + if (barrack.getPatrolPoints().isEmpty()) { + aiAgent.setState(STATE.Awake); + return; + } + + if (aiAgent.isMoving()) { + aiAgent.setState(STATE.Awake); + return; + } + + int patrolRandom = ThreadLocalRandom.current().nextInt(1000); + + if (patrolRandom <= 10) { + if (aiAgent.getPatrolPointIndex() < barrack.getPatrolPoints().size()) { + Vector3fImmutable patrolLoc = barrack.getPatrolPoints().get(aiAgent.getPatrolPointIndex()); + aiAgent.setPatrolPointIndex(aiAgent.getPatrolPointIndex() + 1); + if (aiAgent.getPatrolPointIndex() == barrack.getPatrolPoints().size()) + aiAgent.setPatrolPointIndex(0); + + if (patrolLoc != null) { + if (MovementUtilities.canMove(aiAgent)) { + MovementUtilities.aiMove(aiAgent, patrolLoc, true); + aiAgent.setState(STATE.Awake); + } + } + } + } + aiAgent.setState(STATE.Awake); + } + + private static void guardAttack(Mob aiAgent) { + + if (!aiAgent.isAlive()) { + aiAgent.setState(STATE.Dead); + return; + } + + AbstractGameObject target = aiAgent.getCombatTarget(); + + if (target == null) { + aiAgent.setState(STATE.Patrol); + return; + } + + switch (target.getObjectType()) { + case PlayerCharacter: + + PlayerCharacter player = (PlayerCharacter) target; + + if (!player.isActive()) { + aiAgent.setCombatTarget(null); + aiAgent.setState(STATE.Patrol); + return; + } + + if (aiAgent.isNecroPet() && player.inSafeZone()) { + aiAgent.setCombatTarget(null); + aiAgent.setState(STATE.Idle); + return; + } + + handlePlayerAttackForMob(aiAgent, player); + break; + case Building: + Logger.info("PLAYER GUARD ATTEMPTING TO ATTACK BUILDING IN " + aiAgent.getParentZone().getName()); + aiAgent.setState(STATE.Awake); + break; + case Mob: + Mob mob = (Mob) target; + handleMobAttackForMob(aiAgent, mob); + } + } + + private static void guardHome(Mob aiAgent, boolean walk) { + + //recall home. + PowersBase recall = PowersManager.getPowerByToken(-1994153779); + PowersManager.useMobPower(aiAgent, aiAgent, recall, 40); + + aiAgent.setAggroTargetID(0); + aiAgent.setCombatTarget(null); + aiAgent.setState(STATE.Awake); + } + + private static void guardRespawn(Mob aiAgent) { + + if (!aiAgent.canRespawn()) + return; + + if (aiAgent.isPlayerGuard() && aiAgent.getNpcOwner() != null && !aiAgent.getNpcOwner().isAlive()) + return; + + long spawnTime = aiAgent.getSpawnTime(); + + if (System.currentTimeMillis() > aiAgent.getDeathTime() + spawnTime) { + aiAgent.respawn(); + aiAgent.setState(STATE.Idle); + } + } + + private static void respawn(Mob aiAgent) { + + if (!aiAgent.canRespawn()) + return; + + long spawnTime = aiAgent.getSpawnTime(); + + if (aiAgent.isPlayerGuard() && aiAgent.getNpcOwner() != null && !aiAgent.getNpcOwner().isAlive()) + return; + + if (System.currentTimeMillis() > aiAgent.getDeathTime() + spawnTime) { + aiAgent.respawn(); + aiAgent.setState(STATE.Idle); + } + } + + private static void retaliate(Mob aiAgent) { + + if (aiAgent.getCombatTarget() == null) + aiAgent.setState(STATE.Awake); + + //out of range to attack move + if (!MovementUtilities.canMove(aiAgent)) { + aiAgent.setState(STATE.Attack); + return; + } + + aiAgent.setState(STATE.Attack); + + //lets make mobs ai less twitchy, Don't call another movement until mob reaches it's destination. + if (aiAgent.isMoving()) + return; + + MovementUtilities.moveToLocation(aiAgent, aiAgent.getCombatTarget().getLoc(), aiAgent.getRange()); + } + + private static void moveToWorldObjectRegion(Mob mob, AbstractWorldObject regionObject) { + + if (regionObject.getRegion() == null) + return; + + MovementManager.translocate(mob, regionObject.getLoc(), null); + } +} diff --git a/src/engine/ai/MobileFSMManager.java b/src/engine/ai/MobileFSMManager.java new file mode 100644 index 00000000..a1f8ce20 --- /dev/null +++ b/src/engine/ai/MobileFSMManager.java @@ -0,0 +1,104 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.ai; + +import engine.gameManager.ZoneManager; +import engine.objects.Mob; +import engine.objects.Zone; +import engine.server.MBServerStatics; +import engine.util.ThreadUtils; +import org.pmw.tinylog.Logger; + +import java.util.HashSet; + + +public class MobileFSMManager { + + private static final MobileFSMManager INSTANCE = new MobileFSMManager(); + + private volatile boolean alive; + private long timeOfKill = -1; + + private MobileFSMManager() { + + Runnable worker = new Runnable() { + @Override + public void run() { + execution(); + } + }; + + alive = true; + + Thread t = new Thread(worker, "MobileFSMManager"); + t.start(); + } + + public static MobileFSMManager getInstance() { + return INSTANCE; + } + + /** + * Stops the MobileFSMManager + */ + public void shutdown() { + if (alive) { + alive = false; + timeOfKill = System.currentTimeMillis(); + } + } + + + public long getTimeOfKill() { + return this.timeOfKill; + } + + public boolean isAlive() { + return this.alive; + } + + + private void execution() { + + //Load zone threshold once. + + long mobPulse = System.currentTimeMillis() + MBServerStatics.AI_PULSE_MOB_THRESHOLD; + + while (alive) { + + ThreadUtils.sleep(1); + + if (System.currentTimeMillis() > mobPulse) { + + HashSet auditMobs = new HashSet(); + + for (Zone zone : ZoneManager.getAllZones()) { + + for (Mob mob : zone.zoneMobSet) { + + if (auditMobs.contains(mob.getObjectUUID())) + continue; + auditMobs.add(mob.getObjectUUID()); + try { + if (mob != null) + MobileFSM.run(mob); + } catch (Exception e) { + Logger.error(e); + e.printStackTrace(); + } + } + } + + mobPulse = System.currentTimeMillis() + MBServerStatics.AI_PULSE_MOB_THRESHOLD; + } + } + } + +} diff --git a/src/engine/ai/utilities/CombatUtilities.java b/src/engine/ai/utilities/CombatUtilities.java new file mode 100644 index 00000000..60b538b9 --- /dev/null +++ b/src/engine/ai/utilities/CombatUtilities.java @@ -0,0 +1,413 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.ai.utilities; + +import engine.Enum; +import engine.Enum.*; +import engine.ai.MobileFSM.STATE; +import engine.gameManager.ChatManager; +import engine.gameManager.CombatManager; +import engine.math.Vector3fImmutable; +import engine.net.DispatchMessage; +import engine.net.client.msg.TargetedActionMsg; +import engine.objects.*; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; + +import static engine.math.FastMath.sqr; + +public class CombatUtilities { + + public static boolean inRangeToAttack(Mob agent,AbstractWorldObject target){ + + if (Float.isNaN(agent.getLoc().x)) + return false; + + try{ + Vector3fImmutable sl = agent.getLoc(); + Vector3fImmutable tl = target.getLoc(); + + //add Hitbox's to range. + float range = agent.getRange(); + range += CombatManager.calcHitBox(target) + CombatManager.calcHitBox(agent); + //if (target instanceof AbstractCharacter) + // if (((AbstractCharacter)target).isMoving()) + // range+= 5; + + return !(sl.distanceSquared(tl) > sqr(range)); + }catch(Exception e){ + Logger.error( e.toString()); + return false; + } + + } + + public static boolean inRangeToAttack2D(Mob agent,AbstractWorldObject target){ + + if (Float.isNaN(agent.getLoc().x)) + return false; + + try{ + Vector3fImmutable sl = agent.getLoc(); + Vector3fImmutable tl = target.getLoc(); + + //add Hitbox's to range. + float range = agent.getRange(); + range += CombatManager.calcHitBox(target) + CombatManager.calcHitBox(agent); + //if (target instanceof AbstractCharacter) + // if (((AbstractCharacter)target).isMoving()) + // range+= 5; + + return !(sl.distanceSquared2D(tl) > sqr(range)); + }catch(Exception e){ + Logger.error( e.toString()); + return false; + } + + } + + public static void swingIsBlock(Mob agent,AbstractWorldObject target, int animation) { + + if (!target.isAlive()) + return; + + TargetedActionMsg msg = new TargetedActionMsg(agent,animation, target, MBServerStatics.COMBAT_SEND_BLOCK); + + if (target.getObjectType() == GameObjectType.PlayerCharacter) + DispatchMessage.dispatchMsgToInterestArea(target, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true,false); + else + DispatchMessage.sendToAllInRange(agent,msg); + + } + + public static void swingIsParry(Mob agent,AbstractWorldObject target, int animation) { + + if (!target.isAlive()) + return; + + TargetedActionMsg msg = new TargetedActionMsg(agent,animation, target, MBServerStatics.COMBAT_SEND_PARRY); + + if (target.getObjectType() == GameObjectType.PlayerCharacter) + DispatchMessage.dispatchMsgToInterestArea(target, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true,false); + else + DispatchMessage.sendToAllInRange(agent,msg); + + } + + public static void swingIsDodge(Mob agent,AbstractWorldObject target, int animation) { + + if (!target.isAlive()) + return; + + TargetedActionMsg msg = new TargetedActionMsg(agent,animation, target, MBServerStatics.COMBAT_SEND_DODGE); + + if (target.getObjectType() == GameObjectType.PlayerCharacter) + DispatchMessage.dispatchMsgToInterestArea(target, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true,false); + else + DispatchMessage.sendToAllInRange(agent,msg); + } + + public static void swingIsDamage(Mob agent,AbstractWorldObject target, float damage, int animation){ + float trueDamage = 0; + + if (!target.isAlive()) + return; + + if (AbstractWorldObject.IsAbstractCharacter(target)) + trueDamage = ((AbstractCharacter) target).modifyHealth(-damage, agent, false); + else if (target.getObjectType() == GameObjectType.Building) + trueDamage = ((Building) target).modifyHealth(-damage, agent); + + //Don't send 0 damage kay thanx. + + if (trueDamage == 0) + return; + + TargetedActionMsg msg = new TargetedActionMsg(agent,target, damage, animation); + + if (target.getObjectType() == GameObjectType.PlayerCharacter) + DispatchMessage.dispatchMsgToInterestArea(target, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true,false); + else + DispatchMessage.sendToAllInRange(agent,msg); + + //check damage shields + if(AbstractWorldObject.IsAbstractCharacter(target) && target.isAlive() && target.getObjectType() != GameObjectType.Mob) + CombatManager.handleDamageShields(agent,(AbstractCharacter)target, damage); + } + + public static boolean canSwing(Mob agent) { + return (agent.isAlive() && !agent.getBonuses().getBool(ModType.Stunned, SourceType.None)); + } + + public static void swingIsMiss(Mob agent,AbstractWorldObject target, int animation) { + + TargetedActionMsg msg = new TargetedActionMsg(agent,target, 0f, animation); + + if (target.getObjectType() == GameObjectType.PlayerCharacter) + DispatchMessage.dispatchMsgToInterestArea(target, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true,false); + else + DispatchMessage.sendToAllInRange(agent,msg); + + } + + public static boolean triggerDefense(Mob agent, AbstractWorldObject target) { + int defenseScore = 0; + int attackScore = agent.getAtrHandOne(); + switch (target.getObjectType()) { + case PlayerCharacter: + defenseScore = ((AbstractCharacter) target).getDefenseRating(); + break; + case Mob: + + Mob mob = (Mob)target; + if (mob.isSiege()) + defenseScore = attackScore; + break; + case Building: + return false; + } + + + + int hitChance; + if (attackScore > defenseScore || defenseScore == 0) + hitChance = 94; + else if (attackScore == defenseScore && target.getObjectType() == GameObjectType.Mob) + hitChance = 10; + else { + float dif = attackScore / defenseScore; + if (dif <= 0.8f) + hitChance = 4; + else + hitChance = ((int)(450 * (dif - 0.8f)) + 4); + if (target.getObjectType() == GameObjectType.Building) + hitChance = 100; + } + return ThreadLocalRandom.current().nextInt(100) > hitChance; + } + + public static boolean triggerBlock(Mob agent,AbstractWorldObject ac) { + return triggerPassive(agent,ac, "Block"); + } + + public static boolean triggerParry(Mob agent,AbstractWorldObject ac) { + return triggerPassive(agent,ac, "Parry"); + } + + public static boolean triggerDodge(Mob agent,AbstractWorldObject ac) { + return triggerPassive(agent,ac, "Dodge"); + } + + public static boolean triggerPassive(Mob agent,AbstractWorldObject ac, String type) { + float chance = 0; + if (AbstractWorldObject.IsAbstractCharacter(ac)) + chance = ((AbstractCharacter)ac).getPassiveChance(type, agent.getLevel(), true); + + if (chance > 75f) + chance = 75f; + if (agent.isSiege() && AbstractWorldObject.IsAbstractCharacter(ac)) + chance = 100; + + return ThreadLocalRandom.current().nextInt(100) < chance; + } + + + public static void combatCycle(Mob agent,AbstractWorldObject target, boolean mainHand, ItemBase wb) { + + if (!agent.isAlive() || !target.isAlive()) return; + + if (target.getObjectType() == GameObjectType.PlayerCharacter) + if (!((PlayerCharacter)target).isActive()) + return; + + int anim = 75; + float speed = 30f; + if (mainHand) + speed = agent.getSpeedHandOne(); + else + speed = agent.getSpeedHandTwo(); + + DamageType dt = DamageType.Crush; + if (agent.isSiege()) + dt = DamageType.Siege; + if (wb != null) { + anim = CombatManager.getSwingAnimation(wb, null,mainHand); + dt = wb.getDamageType(); + } else if (!mainHand) + return; + Resists res = null; + PlayerBonuses bonus = null; + switch(target.getObjectType()){ + case Building: + res = ((Building)target).getResists(); + break; + case PlayerCharacter: + res = ((PlayerCharacter)target).getResists(); + bonus = ((PlayerCharacter)target).getBonuses(); + break; + case Mob: + Mob mob = (Mob)target; + res = mob.getResists(); + bonus = ((Mob)target).getBonuses(); + break; + } + + //must not be immune to all or immune to attack + + if (bonus != null && !bonus.getBool(ModType.NoMod, SourceType.ImmuneToAttack)) + if (res != null &&(res.immuneToAll() || res.immuneToAttacks() || res.immuneTo(dt))) + return; + + int passiveAnim = CombatManager.getSwingAnimation(wb, null,mainHand); + if(canSwing(agent)) { + if(triggerDefense(agent,target)) + swingIsMiss(agent,target, passiveAnim); + else if(triggerDodge(agent,target)) + swingIsDodge(agent,target, passiveAnim); + else if(triggerParry(agent,target)) + swingIsParry(agent,target, passiveAnim); + else if(triggerBlock(agent,target)) + swingIsBlock(agent,target, passiveAnim); + else + swingIsDamage(agent,target, determineDamage(agent,target, mainHand, speed, dt), anim); + + if (agent.getWeaponPower() != null) + agent.getWeaponPower().attack(target, MBServerStatics.ONE_MINUTE); + } + + if (target.getObjectType().equals(GameObjectType.PlayerCharacter)){ + PlayerCharacter player = (PlayerCharacter)target; + if (player.getDebug(64)){ + ChatManager.chatSayInfo(player, "Debug Combat: Mob UUID " + agent.getObjectUUID() + " || Building ID = " + agent.getBuildingID() + " || Floor = " + agent.getInFloorID() + " || Level = " + agent.getInBuilding() );//combat debug + } + } + + //SIEGE MONSTERS DO NOT ATTACK GUARDSs + if (target.getObjectType() == GameObjectType.Mob) + if (((Mob)target).isSiege()) + return; + + //handle the retaliate + + if (AbstractWorldObject.IsAbstractCharacter(target)) + CombatManager.handleRetaliate((AbstractCharacter)target, agent); + + if (target.getObjectType() == GameObjectType.Mob){ + Mob targetMob = (Mob)target; + if (targetMob.isSiege()) + return; + + if (System.currentTimeMillis() < targetMob.getTimeStamp("CallForHelp")) + return; + CallForHelp(targetMob); + targetMob.getTimestamps().put("CallForHelp", System.currentTimeMillis() + 60000); + } + + + } + + public static void CallForHelp(Mob aiAgent) { + + Set zoneMobs = aiAgent.getParentZone().zoneMobSet; + + + AbstractWorldObject target = aiAgent.getCombatTarget(); + if (target == null) { + return; + } + + int count = 0; + for (Mob mob: zoneMobs){ + if (!mob.isAlive()) + continue; + if (mob.isSiege() || mob.isPet() || !Enum.MobFlagType.AGGRESSIVE.elementOf(mob.getMobBase().getFlags())) + continue; + if (count == 5) + continue; + + + if (mob.getCombatTarget() != null) + continue; + + if (!aiAgent.isPlayerGuard() && mob.isPlayerGuard()) + continue; + + if (aiAgent.isPlayerGuard() && !mob.isPlayerGuard() ) + continue; + + if (target.getObjectType() == GameObjectType.PlayerCharacter){ + + if (!MovementUtilities.inRangeToAggro(mob, (PlayerCharacter)target)) + continue; + count++; + + }else{ + + if (count == 5) + continue; + + if (aiAgent.getLoc().distanceSquared2D(target.getLoc()) > sqr(aiAgent.getAggroRange())) + continue; + + count++; + + } + + + + + + + if (mob.getState() == STATE.Awake || mob.getState() == STATE.Patrol){ + mob.setCombatTarget(target); + mob.setState(STATE.Attack); + } + } + + } + + public static float determineDamage(Mob agent,AbstractWorldObject target, boolean mainHand, float speed, DamageType dt) { + + float min = (mainHand) ? agent.getMinDamageHandOne() : agent.getMinDamageHandTwo(); + float max = (mainHand) ? agent.getMaxDamageHandOne() : agent.getMaxDamageHandTwo();; + + float range = max - min; + float damage = min + ((ThreadLocalRandom.current().nextFloat()*range)+(ThreadLocalRandom.current().nextFloat()*range))/2; + + if (AbstractWorldObject.IsAbstractCharacter(target)) + if (((AbstractCharacter)target).isSit()) + damage *= 2.5f; //increase damage if sitting + + if (AbstractWorldObject.IsAbstractCharacter(target)) + return ((AbstractCharacter)target).getResists().getResistedDamage(agent,(AbstractCharacter)target, dt, damage, 0); + + if (target.getObjectType() == GameObjectType.Building){ + Building building = (Building)target; + Resists resists = building.getResists(); + return damage * (1 - (resists.getResist(dt, 0) / 100)); + } + + return damage; + + } + + public static boolean RunAIRandom(){ + int random = ThreadLocalRandom.current().nextInt(4); + + if (random == 0) + return true; + + return false; + } +} diff --git a/src/engine/ai/utilities/MovementUtilities.java b/src/engine/ai/utilities/MovementUtilities.java new file mode 100644 index 00000000..28a89689 --- /dev/null +++ b/src/engine/ai/utilities/MovementUtilities.java @@ -0,0 +1,303 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.ai.utilities; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.Enum.ModType; +import engine.Enum.SourceType; +import engine.exception.MsgSendException; +import engine.gameManager.MovementManager; +import engine.math.Vector3fImmutable; +import engine.net.client.msg.MoveToPointMsg; +import engine.objects.*; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.util.concurrent.ThreadLocalRandom; + +import static engine.math.FastMath.sqr; +import static engine.math.FastMath.sqrt; + +public class MovementUtilities { + + + public static boolean inRangeOfBindLocation(Mob agent){ + + + + if (agent.isPlayerGuard()){ + + Mob guardCaptain = null; + if (agent.getContract() != null) + guardCaptain = agent; + else + guardCaptain = (Mob) agent.getNpcOwner(); + + if (guardCaptain != null){ + Building barracks = guardCaptain.getBuilding(); + + if (barracks != null){ + City city = barracks.getCity(); + + if (city != null){ + Building tol = city.getTOL(); + + //Guards recall distance = 814. + if (tol != null){ + if (agent.getLoc().distanceSquared2D(tol.getLoc()) > sqr(Enum.CityBoundsType.SIEGE.extents)) { + return false; + } + } + + } + } + } + + return true; + } + + Vector3fImmutable sl = new Vector3fImmutable(agent.getLoc().getX(), 0, agent.getLoc().getZ()); + Vector3fImmutable tl = new Vector3fImmutable(agent.getTrueBindLoc().x,0,agent.getTrueBindLoc().z); + + float distanceSquaredToTarget = sl.distanceSquared2D(tl); //distance to center of target + float zoneRange = 250; + + if (agent.getParentZone() != null){ + if (agent.getParentZone().getBounds() != null) + zoneRange = agent.getParentZone().getBounds().getHalfExtents().x * 2; + } + + if (zoneRange > 300) + zoneRange = 300; + + if (agent.getSpawnRadius() > zoneRange) + zoneRange = agent.getSpawnRadius(); + + + return distanceSquaredToTarget < sqr(MBServerStatics.AI_DROP_AGGRO_RANGE + zoneRange); + + } + + public static boolean inRangeToAggro(Mob agent,PlayerCharacter target){ + + Vector3fImmutable sl = agent.getLoc(); + Vector3fImmutable tl =target.getLoc(); + + float distanceSquaredToTarget = sl.distanceSquared2D(tl) - sqr(agent.calcHitBox() + target.calcHitBox()); //distance to center of target + float range = MBServerStatics.AI_BASE_AGGRO_RANGE; + + if (agent.isPlayerGuard()) + range = 150; + + return distanceSquaredToTarget < sqr(range); + + } + + public static boolean inRangeDropAggro(Mob agent,PlayerCharacter target){ + + Vector3fImmutable sl = agent.getLoc(); + Vector3fImmutable tl = target.getLoc(); + + float distanceSquaredToTarget = sl.distanceSquared2D(tl) - sqr(agent.calcHitBox() + target.calcHitBox()); //distance to center of target + + float range = agent.getRange() + 150; + + if (range > 200) + range = 200; + + + return distanceSquaredToTarget < sqr(range); + + } + + public static Vector3fImmutable GetMoveLocation(Mob aiAgent, AbstractCharacter aggroTarget){ + + // Player isnt moving and neither is mob. Just return + // the mobile's current location. Ain't goin nowhere! + // *** Refactor: Check to ensure methods calling us + // all don't sent move messages when not moving. + + if ((aggroTarget.isMoving() == false)) + return aggroTarget.getLoc(); + + if (aggroTarget.getEndLoc().x != 0){ + + float aggroTargetDistanceSquared = aggroTarget.getLoc().distanceSquared2D(aggroTarget.getEndLoc()); + float aiAgentDistanceSquared = aiAgent.getLoc().distanceSquared2D(aggroTarget.getEndLoc()); + + if (aiAgentDistanceSquared >= aggroTargetDistanceSquared) + return aggroTarget.getEndLoc(); + else{ + float distanceToMove = sqrt(aggroTargetDistanceSquared + aiAgentDistanceSquared) *.5f; + + return aggroTarget.getFaceDir().scaleAdd(distanceToMove, aggroTarget.getLoc()); + + } + } + + // One of us is moving so let's calculate our destination loc for this + // simulation frame. We will simply project our position onto the + // character's movement vector and return the closest point. + + return aiAgent.getLoc().ClosestPointOnLine(aggroTarget.getLoc(), aggroTarget.getEndLoc()); + } + + public static void moveToLocation(Mob agent,Vector3fImmutable newLocation, float offset){ + try { + + //don't move farther than 30 units from player. + if (offset > 30) + offset = 30; + Vector3fImmutable newLoc = Vector3fImmutable.getRandomPointInCircle(newLocation, offset); + + + agent.setFaceDir(newLoc.subtract2D(agent.getLoc()).normalize()); + + aiMove(agent,newLoc,false); + } catch (Exception e) { + Logger.error( e.toString()); + } + } + + + + public static boolean canMove(Mob agent) { + if (agent.getMobBase() != null && Enum.MobFlagType.SENTINEL.elementOf(agent.getMobBase().getFlags())) + return false; + + return (agent.isAlive() && !agent.getBonuses().getBool(ModType.Stunned,SourceType.None) && !agent.getBonuses().getBool(ModType.CannotMove, SourceType.None)); + } + + public static Vector3fImmutable randomPatrolLocation(Mob agent,Vector3fImmutable center, float radius){ + + //Determing where I want to move. + return new Vector3fImmutable((center.x - radius) + ((ThreadLocalRandom.current().nextFloat()+.1f*2)*radius), + center.y, + (center.z - radius) + ((ThreadLocalRandom.current().nextFloat()+.1f *2)*radius)); + } + public static Long estimateMovementTime(Mob agent) { + if(agent.getEndLoc().x == 0 && agent.getEndLoc().y == 0) + return 0L; + + return (long) ((agent.getLoc().distance2D(agent.getEndLoc())*1000)/agent.getSpeed()); + } + + public static void aiMove(Mob agent,Vector3fImmutable vect, boolean isWalking) { + + //update our walk/run state. + if (isWalking && !agent.isWalk()){ + agent.setWalkMode(true); + MovementManager.sendRWSSMsg(agent); + }else if(!isWalking && agent.isWalk()){ + agent.setWalkMode(false); + MovementManager.sendRWSSMsg(agent); + } + + MoveToPointMsg msg = new MoveToPointMsg(); + + +// Regions currentRegion = Mob.InsideBuildingRegion(agent); +// +// if (currentRegion != null){ +// +// +// if (currentRegion.isGroundLevel()){ +// agent.setInBuilding(0); +// agent.setInFloorID(-1); +// }else{ +// agent.setInBuilding(currentRegion.getLevel()); +// agent.setInFloorID(currentRegion.getRoom()); +// } +// }else{ +// agent.setInBuilding(-1); +// agent.setInFloorID(-1); +// agent.setInBuildingID(0); +// } +// agent.setLastRegion(currentRegion); + + + + Vector3fImmutable startLoc = null; + Vector3fImmutable endLoc = null; + +// if (agent.getLastRegion() != null){ +// Building inBuilding = Building.getBuildingFromCache(agent.getInBuildingID()); +// if (inBuilding != null){ +// startLoc = ZoneManager.convertWorldToLocal(inBuilding, agent.getLoc()); +// endLoc = ZoneManager.convertWorldToLocal(inBuilding, vect); +// } +// }else{ +// agent.setBuildingID(0); +// agent.setInBuildingID(0); +// startLoc = agent.getLoc(); +// endLoc = vect; +// } + + startLoc = agent.getLoc(); + endLoc = vect; + + msg.setSourceType(GameObjectType.Mob.ordinal()); + msg.setSourceID(agent.getObjectUUID()); + msg.setStartCoord(startLoc); + msg.setEndCoord(endLoc); + msg.setUnknown01(-1); + msg.setInBuilding(-1); + msg.setTargetType(0); + msg.setTargetID(0); + + + try { + MovementManager.movement(msg, agent); + } catch (MsgSendException e) { + // TODO Figure out how we want to handle the msg send exception + e.printStackTrace(); + } + } + + public static Vector3fImmutable GetDestinationToCharacter(Mob aiAgent, AbstractCharacter character){ + + if (!character.isMoving()) + return character.getLoc(); + + + float agentDistanceEndLoc = aiAgent.getLoc().distanceSquared2D(character.getEndLoc()); + float characterDistanceEndLoc = character.getLoc().distanceSquared2D(character.getEndLoc()); + + if (agentDistanceEndLoc > characterDistanceEndLoc) + return character.getEndLoc(); + + return character.getLoc(); + } + + public static boolean updateMovementToCharacter(Mob aiAgent, AbstractCharacter aggroTarget){ + + if (aiAgent.destination.equals(Vector3fImmutable.ZERO)) + return true; + + if (!aiAgent.isMoving()) + return true; + + + + + if (aggroTarget.isMoving()){ + if (!aiAgent.destination.equals(aggroTarget.getEndLoc()) && !aiAgent.destination.equals(aggroTarget.getLoc())) + return true; + }else{ + if (aiAgent.destination.equals(aggroTarget.getLoc())) + return false; + } + + return false; + } + +} diff --git a/src/engine/ai/utilities/PowerUtilities.java b/src/engine/ai/utilities/PowerUtilities.java new file mode 100644 index 00000000..12c80cb7 --- /dev/null +++ b/src/engine/ai/utilities/PowerUtilities.java @@ -0,0 +1,15 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.ai.utilities; + +public class PowerUtilities { + +} diff --git a/src/engine/core/ControlledRunnable.java b/src/engine/core/ControlledRunnable.java new file mode 100644 index 00000000..d9d8bf4a --- /dev/null +++ b/src/engine/core/ControlledRunnable.java @@ -0,0 +1,174 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.core; + +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; + +/** + * + */ +public abstract class ControlledRunnable implements Runnable { + protected boolean runCmd = false; + protected boolean runStatus = false; + private Thread thisThread; + private final String threadName; + + public ControlledRunnable(String threadName) { + super(); + this.threadName = threadName; + ControlledRunnable.runnables.add(this); + } + + /* + * Main loop + */ + + /** + * This is the method called when ControlledRunnable.thisThread.start() is + * called. + */ + @Override + public void run() { + if (this._preRun() == false) { + return; + } + + this.runStatus = true; + + if (this._Run() == false) { + return; + } + + if (this._postRun() == false) { + return; + } + + this.runStatus = false; + } + + /** + * _preRun() is called prior to the call to _Run(), but after _startup() + * + * @return + */ + protected abstract boolean _preRun(); + + /** + * _Run() is called after _startup() and contains should contain the main + * loop. + * + * @return + */ + protected abstract boolean _Run(); + + /** + * _postRun() is called after _Run() exits, not necessarily before + * _shutdown() + * + * @return + */ + protected abstract boolean _postRun(); + + /* + * Control + */ + + /** + * startup() initializes the internal thread, sets the runCMD to true, and + * calls _startup() prior to starting of the internal Thread. + */ + public void startup() { + + this.thisThread = new Thread(this, this.threadName); + this.runCmd = true; + this._startup(); + this.thisThread.start(); + } + + /** + * This method is called just before ControlledRunnable.thisThread.start() + * is called. + */ + protected abstract void _startup(); + + /** + * This method is called to request a shutdown of the runnable. + */ + public void shutdown() { + this.runCmd = false; + this._shutdown(); + } + + /** + * This method is called just after ControlledRunnable.runCmd is set to + * False. + */ + protected abstract void _shutdown(); + + /* + * Getters n setters + */ + public boolean getRunCmd() { + return runCmd; + } + + public boolean getRunStatus() { + return runStatus; + } + + /* + * Blockers + */ + public void blockTillRunStatus(boolean status) { + while (this.runStatus != status) { + try { + System.out.println("BLOCKING"); + Thread.sleep(25L); + } catch (InterruptedException e) { + Logger.debug( e.getMessage()); + + break; + } + } + } + + /** + * @return the thisThread + */ + protected Thread getThisThread() { + return thisThread; + } + + /** + * @return the threadName + */ + public String getThreadName() { + return threadName; + } + + /* + * Instance monitoring and tools + */ + + // Runnable tracking + private static final ArrayList runnables = new ArrayList<>(); + + public static void shutdownAllRunnables() { + for (ControlledRunnable cr : ControlledRunnable.runnables) { + //Use Direct logging since JobManager is a runnable. + Logger.info("ControlledRunnable", + "Sending Shutdown cmd to: " + cr.threadName); + cr.shutdown(); + } + } + +} diff --git a/src/engine/db/archive/BaneRecord.java b/src/engine/db/archive/BaneRecord.java new file mode 100644 index 00000000..51209f06 --- /dev/null +++ b/src/engine/db/archive/BaneRecord.java @@ -0,0 +1,383 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.archive; + +import engine.Enum; +import engine.objects.Bane; +import engine.objects.City; +import engine.workthreads.WarehousePushThread; +import org.joda.time.DateTime; +import org.pmw.tinylog.Logger; + +import java.sql.*; +import java.time.LocalDateTime; +import java.util.concurrent.LinkedBlockingQueue; + +import static engine.Enum.RecordEventType; + +public class BaneRecord extends DataRecord { + + private static final LinkedBlockingQueue recordPool = new LinkedBlockingQueue<>(); + private RecordEventType eventType; + private String cityHash; + private String cityName; + private String cityGuildHash; + private String cityNationHash; + private String baneDropperHash; + private String baneGuildHash; + private String baneNationHash; + private DateTime baneLiveTime; + private DateTime baneDropTime; + + private BaneRecord(Bane bane) { + this.recordType = Enum.DataRecordType.BANE; + this.eventType = RecordEventType.PENDING; + } + + public static BaneRecord borrow(Bane bane, RecordEventType eventType) { + BaneRecord baneRecord; + + baneRecord = recordPool.poll(); + + if (baneRecord == null) { + baneRecord = new BaneRecord(bane); + baneRecord.eventType = eventType; + } + else { + baneRecord.recordType = Enum.DataRecordType.BANE; + baneRecord.eventType = eventType; + + } + + baneRecord.cityHash = bane.getCity().getHash(); + baneRecord.cityName = bane.getCity().getCityName(); + baneRecord.cityGuildHash = bane.getCity().getGuild().getHash(); + baneRecord.cityNationHash = bane.getCity().getGuild().getNation().getHash(); + + + if (bane.getOwner() == null) { + baneRecord.baneDropperHash = "ERRANT"; + baneRecord.baneGuildHash = "ERRANT"; + baneRecord.baneNationHash = "ERRANT"; + } + else { + baneRecord.baneDropperHash = DataWarehouse.hasher.encrypt(bane.getOwner().getObjectUUID()); // getPlayerCharacter didn't check hash first? OMFG + + + baneRecord.baneGuildHash = bane.getOwner().getGuild().getHash(); + baneRecord.baneNationHash = bane.getOwner().getGuild().getNation().getHash(); + + + baneRecord.baneLiveTime = bane.getLiveDate(); + baneRecord.baneDropTime = bane.getPlacementDate(); + } + + + return baneRecord; + } + + public static PreparedStatement buildBanePushStatement(Connection connection, ResultSet rs) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "INSERT INTO `warehouse_banehistory` (`event_number`, `city_id`, `city_name`, `char_id`, `offGuild_id`, `offNat_id`, `defGuild_id`, `defNat_id`, `dropDatetime`, `liveDateTime`, `resolution`) VALUES(?,?,?,?,?,?,?,?,?,?,?)"; + java.util.Date sqlDateTime; + + outStatement = connection.prepareStatement(queryString); + + // Bind record data + + outStatement.setInt(1, rs.getInt("event_number")); + outStatement.setString(2, rs.getString("city_id")); + outStatement.setString(3, rs.getString("city_name")); + outStatement.setString(4, rs.getString("char_id")); + outStatement.setString(5, rs.getString("offGuild_id")); + outStatement.setString(6, rs.getString("offNat_id")); + outStatement.setString(7, rs.getString("defGuild_id")); + outStatement.setString(8, rs.getString("defNat_id")); + + sqlDateTime = rs.getTimestamp("dropDatetime"); + + if (sqlDateTime == null) + outStatement.setNull(9, Types.DATE); + else + outStatement.setTimestamp(9, rs.getTimestamp("dropDatetime")); + + sqlDateTime = rs.getTimestamp("dropDatetime"); + + if (sqlDateTime == null) + outStatement.setNull(10, Types.DATE); + else + outStatement.setTimestamp(10, rs.getTimestamp("liveDateTime")); + + outStatement.setString(11, rs.getString("resolution")); + + return outStatement; + } + + public static PreparedStatement buildBaneQueryStatement(Connection connection) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "SELECT * FROM `warehouse_banehistory` WHERE `event_number` > ?"; + outStatement = connection.prepareStatement(queryString); + outStatement.setInt(1, WarehousePushThread.baneIndex); + return outStatement; + } + + public static DateTime getLastBaneDateTime(City city) { + + DateTime outDateTime = null; + + try (Connection connection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = buildDateTimeQueryStatement(connection, city); + ResultSet rs = statement.executeQuery()) { + + while (rs.next()) { + + outDateTime = new DateTime(rs.getTimestamp("endDatetime")); + + } + + } catch (SQLException e) { + Logger.error( e.toString()); + } + + return outDateTime; + } + + + private static PreparedStatement buildDateTimeQueryStatement (Connection connection, City city) throws SQLException { + PreparedStatement outStatement; + String queryString = "SELECT `endDatetime` FROM `warehouse_banehistory` WHERE `city_id` = ? ORDER BY `endDatetime` DESC LIMIT 1"; + outStatement = connection.prepareStatement(queryString); + outStatement.setString(1, city.getHash()); + return outStatement; + + } + + public static void updateLiveDate(Bane bane, DateTime dateTime) { + + if (bane == null) + return; + + try (Connection connection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = buildUpdateLiveDateStatement(connection, bane, dateTime)) { + + statement.execute(); + + } catch (SQLException e) { + Logger.error( e.toString()); + } + } + + private static PreparedStatement buildUpdateLiveDateStatement(Connection connection, Bane bane, DateTime dateTime) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "UPDATE `warehouse_banehistory` SET `liveDatetime` = ?, `dirty` = 1 WHERE `city_id` = ? AND `resolution` = 'PENDING'"; + + outStatement = connection.prepareStatement(queryString); + outStatement.setTimestamp(1, new java.sql.Timestamp(dateTime.getMillis())); + outStatement.setString(2, bane.getCity().getHash()); + + return outStatement; + } + + private static PreparedStatement buildUpdateResolutionStatement(Connection connection, Bane bane, RecordEventType eventType) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "UPDATE `warehouse_banehistory` SET `endDatetime` = ?, `resolution` = ?, `dirty` = 1 WHERE `city_id` = ? AND `resolution` = 'PENDING'"; + + outStatement = connection.prepareStatement(queryString); + outStatement.setTimestamp(1, Timestamp.valueOf(LocalDateTime.now())); + outStatement.setString(2, eventType.name()); + outStatement.setString(3, bane.getCity().getHash()); + + return outStatement; + } + + public static void updateResolution(Bane bane, RecordEventType eventType) { + + try (Connection connection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = buildUpdateResolutionStatement(connection, bane, eventType)) { + + statement.execute(); + + } catch (SQLException e) { + Logger.error(e.toString()); + } + } + + public static String getBaneHistoryString() { + + String outString; + String queryString; + String dividerString; + String newLine = System.getProperty("line.separator"); + outString = "[LUA_BANES() DATA WAREHOUSE]" + newLine; + dividerString = "--------------------------------" + newLine; + queryString = "CALL `baneHistory`()"; + + try (Connection connection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = connection.prepareCall(queryString); + ResultSet rs = statement.executeQuery()) { + + while (rs.next()) { + + outString += "Magicbane unresolved banes: " + rs.getInt("PENDING") + '/' + rs.getInt("TOTAL") + newLine; + outString += dividerString; + outString += "Bane Resolution History" + newLine; + outString += dividerString; + + outString += "Destruction: " + rs.getInt("DESTROY") + newLine; + outString += "Capture: " + rs.getInt("CAPTURE") + newLine; + outString += "Defended: " + rs.getInt("DEFEND") + newLine; + } + + } catch (SQLException e) { + e.printStackTrace(); + } + return outString; + } + + public static void updateDirtyRecords() { + + String queryString = "SELECT * FROM `warehouse_banehistory` where `dirty` = 1"; + + // Reset character delta + + WarehousePushThread.baneDelta = 0; + + try (Connection localConnection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = localConnection.prepareStatement(queryString, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); // Make this an updatable result set as we'll reset the dirty flag as we go along + ResultSet rs = statement.executeQuery()) { + + while (rs.next()) { + + // Only update the index and dirty flag + // if the remote database update succeeded + + if (updateDirtyRecord(rs) == true) + WarehousePushThread.baneDelta++; + else + continue; + + // Reset the dirty flag in the local database + + rs.updateInt("dirty", 0); + rs.updateRow(); + } + + } catch (SQLException e) { + Logger.error( e.toString()); + } + } + + private static boolean updateDirtyRecord(ResultSet rs) { + + try (Connection remoteConnection = DataWarehouse.remoteConnectionPool.getConnection(); + PreparedStatement statement = buildUpdateDirtyStatement(remoteConnection, rs)) { + + statement.execute(); + return true; + } catch (SQLException e) { + Logger.error( e.toString()); + return false; + } + } + + private static PreparedStatement buildUpdateDirtyStatement(Connection connection, ResultSet rs) throws SQLException { + + PreparedStatement outStatement; + String queryString = "UPDATE `warehouse_banehistory` SET `liveDateTime` = ?, `endDateTime` = ?, `resolution` = ? WHERE `event_number` = ?"; + java.util.Date sqlDateTime; + + outStatement = connection.prepareStatement(queryString); + + // Bind record data + + sqlDateTime = rs.getTimestamp("liveDateTime"); + + if (sqlDateTime == null) + outStatement.setNull(1, Types.DATE); + else + outStatement.setTimestamp(1, rs.getTimestamp("liveDateTime")); + + sqlDateTime = rs.getTimestamp("endDateTime"); + + if (sqlDateTime == null) + outStatement.setNull(2, Types.DATE); + else + outStatement.setTimestamp(2, rs.getTimestamp("endDateTime")); + + outStatement.setString(3, rs.getString("resolution")); + outStatement.setInt(4, rs.getInt("event_number")); + + return outStatement; + } + + void reset() { + this.cityHash = null; + this.cityGuildHash = null; + this.cityNationHash = null; + this.baneDropperHash = null; + this.baneGuildHash = null; + this.baneNationHash = null; + this.baneLiveTime = null; + } + + public void release() { + this.reset(); + recordPool.add(this); + } + + public void write() { + + try (Connection connection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = buildBaneInsertStatement(connection)) { + + statement.execute(); + + } catch (SQLException e) { + Logger.error( e.toString()); + } + + } + + private PreparedStatement buildBaneInsertStatement(Connection connection) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "INSERT INTO `warehouse_banehistory` (`city_id`, `city_name`, `char_id`, `offGuild_id`, `offNat_id`, `defGuild_id`, `defNat_id`, `dropDatetime`, `liveDateTime`, `resolution`) VALUES(?,?,?,?,?,?,?,?,?,?)"; + + outStatement = connection.prepareStatement(queryString); + + outStatement.setString(1, this.cityHash); + outStatement.setString(2, this.cityName); + outStatement.setString(3, this.baneDropperHash); + outStatement.setString(4, this.baneGuildHash); + outStatement.setString(5, this.baneNationHash); + outStatement.setString(6, this.cityGuildHash); + outStatement.setString(7, this.cityNationHash); + + if (this.baneDropTime == null) + outStatement.setNull(8, java.sql.Types.DATE); + else + outStatement.setTimestamp(8, new java.sql.Timestamp(this.baneDropTime.getMillis())); + + if (this.baneLiveTime == null) + outStatement.setNull(9, java.sql.Types.DATE); + else + outStatement.setTimestamp(9, new java.sql.Timestamp(this.baneLiveTime.getMillis())); + + outStatement.setString(10, this.eventType.name()); + + + return outStatement; + } +} // END CLASS + diff --git a/src/engine/db/archive/CharacterRecord.java b/src/engine/db/archive/CharacterRecord.java new file mode 100644 index 00000000..b1c6e3b5 --- /dev/null +++ b/src/engine/db/archive/CharacterRecord.java @@ -0,0 +1,284 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.archive; + +import engine.Enum; +import engine.objects.Guild; +import engine.objects.PlayerCharacter; +import engine.workthreads.WarehousePushThread; +import org.pmw.tinylog.Logger; + +import java.sql.*; +import java.time.LocalDateTime; +import java.util.concurrent.LinkedBlockingQueue; + +/* + * This class warehouses character creation events. It also tracks + * updates to summary kills/death data and their promotion class. + */ +public class CharacterRecord extends DataRecord { + + // Local object pool for class + + private static final LinkedBlockingQueue recordPool = new LinkedBlockingQueue<>(); + + private PlayerCharacter player; + + private CharacterRecord(PlayerCharacter player) { + this.recordType = Enum.DataRecordType.CHARACTER; + this.player = player; + } + + public static CharacterRecord borrow(PlayerCharacter player) { + CharacterRecord characterRecord; + + characterRecord = recordPool.poll(); + + if (characterRecord == null) { + characterRecord = new CharacterRecord(player); + } + else { + characterRecord.recordType = Enum.DataRecordType.CHARACTER; + characterRecord.player = player; + + } + + return characterRecord; + } + + private static PreparedStatement buildCharacterInsertStatement(Connection connection, PlayerCharacter player) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "INSERT INTO `warehouse_characterhistory` (`char_id`, `char_fname`, `char_lname`, `baseClass`, `race`, `promoteClass`, `startingGuild`, `datetime`) VALUES(?,?,?,?,?,?,?,?)"; + Guild charGuild; + + outStatement = connection.prepareStatement(queryString); + + charGuild = player.getGuild(); + + // Bind character data + + outStatement.setString(1, DataWarehouse.hasher.encrypt(player.getObjectUUID())); + outStatement.setString(2, player.getFirstName()); + outStatement.setString(3, player.getLastName()); + outStatement.setInt(4, player.getBaseClassID()); + outStatement.setInt(5, player.getRaceID()); + outStatement.setInt(6, player.getPromotionClassID()); + outStatement.setString(7, DataWarehouse.hasher.encrypt(charGuild.getObjectUUID())); + outStatement.setTimestamp(8, Timestamp.valueOf(LocalDateTime.now())); + + return outStatement; + } + + public static PreparedStatement buildCharacterPushStatement(Connection connection, ResultSet rs) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "INSERT INTO `warehouse_characterhistory` (`event_number`, `char_id`, `char_fname`, `char_lname`, `baseClass`, `race`, `promoteClass`, `startingGuild`, `datetime`) VALUES(?,?,?,?,?,?,?,?,?)"; + + outStatement = connection.prepareStatement(queryString); + + // Bind record data + + outStatement.setInt(1, rs.getInt("event_number")); + outStatement.setString(2, rs.getString("char_id")); + outStatement.setString(3, rs.getString("char_fname")); + outStatement.setString(4, rs.getString("char_lname")); + outStatement.setInt(5, rs.getInt("baseClass")); + outStatement.setInt(6, rs.getInt("race")); + outStatement.setInt(7, rs.getInt("promoteClass")); + outStatement.setString(8, rs.getString("startingGuild")); + outStatement.setTimestamp(9, rs.getTimestamp("datetime")); + return outStatement; + } + + public static PreparedStatement buildCharacterQueryStatement(Connection connection) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "SELECT * FROM `warehouse_characterhistory` WHERE `event_number` > ?"; + outStatement = connection.prepareStatement(queryString); + outStatement.setInt(1, WarehousePushThread.charIndex); + return outStatement; + } + + public static void advanceKillCounter(PlayerCharacter player) { + + try (Connection connection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = buildKillCounterStatement(connection, player)) { + + statement.execute(); + + } catch (SQLException e) { + Logger.error( e.toString()); + } + + } + + private static PreparedStatement buildKillCounterStatement(Connection connection, PlayerCharacter player) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "UPDATE `warehouse_characterhistory` SET `kills` = `kills` +1, `dirty` = 1 WHERE `char_id` = ?"; + + if (player == null) + return outStatement; + + outStatement = connection.prepareStatement(queryString); + outStatement.setString(1, player.getHash()); + + return outStatement; + } + + public static void advanceDeathCounter(PlayerCharacter player) { + + try (Connection connection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = buildDeathCounterStatement(connection, player)) { + + statement.execute(); + + } catch (SQLException e) { + Logger.error( e.toString()); + } + + } + + private static PreparedStatement buildDeathCounterStatement(Connection connection, PlayerCharacter player) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "UPDATE `warehouse_characterhistory` SET `deaths` = `deaths` +1, `dirty` = 1 WHERE `char_id` = ?"; + + if (player == null) + return outStatement; + + outStatement = connection.prepareStatement(queryString); + outStatement.setString(1, player.getHash()); + + return outStatement; + } + + public static void updatePromotionClass(PlayerCharacter player) { + + try (Connection connection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = buildUpdatePromotionStatement(connection, player)) { + + statement.execute(); + + } catch (SQLException e) { + Logger.error( e.toString()); + } + + } + + private static PreparedStatement buildUpdatePromotionStatement(Connection connection, PlayerCharacter player) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "UPDATE `warehouse_characterhistory` SET `promoteClass` = ?, `dirty` = 1 WHERE `char_id` = ?"; + + if (player == null) + return outStatement; + + outStatement = connection.prepareStatement(queryString, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + outStatement.setInt(1, player.getPromotionClassID()); + outStatement.setString(2, player.getHash()); + + return outStatement; + } + + public static void updateDirtyRecords() { + + String queryString = "SELECT * FROM `warehouse_characterhistory` where `dirty` = 1"; + + // Reset character delta + + WarehousePushThread.charDelta = 0; + + try (Connection localConnection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = localConnection.prepareStatement(queryString, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); // Make this an updatable result set as we'll reset the dirty flag as we go along + ResultSet rs = statement.executeQuery()) { + + while (rs.next()) { + + // Only update the index and dirty flag + // if the remote database update succeeded + + if (updateDirtyRecord(rs) == true) + WarehousePushThread.charDelta++; + else + continue; + + // Reset the dirty flag in the local database + + rs.updateInt("dirty", 0); + rs.updateRow(); + } + + } catch (SQLException e) { + Logger.error(e.toString()); + } + } + + private static boolean updateDirtyRecord(ResultSet rs) { + + try (Connection remoteConnection = DataWarehouse.remoteConnectionPool.getConnection(); + PreparedStatement statement = buildUpdateDirtyStatement(remoteConnection, rs)) { + + statement.execute(); + return true; + } catch (SQLException e) { + Logger.error(e.toString()); + return false; + } + } + + private static PreparedStatement buildUpdateDirtyStatement(Connection connection, ResultSet rs) throws SQLException { + + PreparedStatement outStatement; + String queryString = "UPDATE `warehouse_characterhistory` SET `promoteClass` = ?, `kills` = ?, `deaths` = ? WHERE `char_id` = ?"; + + outStatement = connection.prepareStatement(queryString); + + // Bind record data + + outStatement.setInt(1, rs.getInt("promoteClass")); + outStatement.setInt(2, rs.getInt("kills")); + outStatement.setInt(3, rs.getInt("deaths")); + outStatement.setString(4, rs.getString("char_id")); + + return outStatement; + } + + void reset() { + this.player = null; + } + + public void release() { + this.reset(); + recordPool.add(this); + } + + public void write() { + + try (Connection connection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = buildCharacterInsertStatement(connection, this.player)) { + + statement.execute(); + + } catch (SQLException e) { + Logger.error( "Error writing character record " + e.toString()); + + } + } + +} + + + + + + + diff --git a/src/engine/db/archive/CityRecord.java b/src/engine/db/archive/CityRecord.java new file mode 100644 index 00000000..50b123e0 --- /dev/null +++ b/src/engine/db/archive/CityRecord.java @@ -0,0 +1,161 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.archive; + +import engine.Enum; +import engine.objects.City; +import engine.workthreads.WarehousePushThread; + +import java.sql.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class CityRecord extends DataRecord { + + private static final LinkedBlockingQueue recordPool = new LinkedBlockingQueue<>(); + private Enum.RecordEventType eventType; + private City city; + private String cityHash; + private String cityGuildHash; + private String cityName; + private String cityMotto; + private float locX; + private float locY; + private String zoneHash; + private java.time.LocalDateTime establishedDatetime; + + private CityRecord(City city) { + this.recordType = Enum.DataRecordType.CITY; + this.city = city; + this.eventType = Enum.RecordEventType.CREATE; + + } + + public static CityRecord borrow(City city, Enum.RecordEventType eventType) { + CityRecord cityRecord; + + cityRecord = recordPool.poll(); + + if (cityRecord == null) { + cityRecord = new CityRecord(city); + cityRecord.eventType = eventType; + } + else { + cityRecord.recordType = Enum.DataRecordType.CITY; + cityRecord.eventType = eventType; + cityRecord.city = city; + + } + + if (cityRecord.city.getHash() == null) + cityRecord.city.setHash(DataWarehouse.hasher.encrypt(cityRecord.city.getObjectUUID())); + + cityRecord.cityHash = cityRecord.city.getHash(); + + + cityRecord.cityName = cityRecord.city.getCityName(); + cityRecord.cityMotto = cityRecord.city.getMotto(); + + cityRecord.cityGuildHash = cityRecord.city.getGuild().getHash(); + + cityRecord.locX = cityRecord.city.getTOL().getLoc().x; + cityRecord.locY = -cityRecord.city.getTOL().getLoc().z; // flip sign on 'y' coordinate + + cityRecord.zoneHash = cityRecord.city.getParent().getHash(); + + if (cityRecord.eventType.equals(Enum.RecordEventType.CREATE)) + cityRecord.establishedDatetime = cityRecord.city.established; + else + cityRecord.establishedDatetime = java.time.LocalDateTime.now(); + + return cityRecord; + } + + public static PreparedStatement buildCityPushStatement(Connection connection, ResultSet rs) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "INSERT INTO `warehouse_cityhistory` (`event_number`, `city_id`, `city_name`, `city_motto`, `guild_id`, `loc_x`, `loc_y`, `zone_id`, `eventType`, `datetime`) VALUES(?,?,?,?,?,?,?,?,?,?)"; + outStatement = connection.prepareStatement(queryString); + + // Bind record data + + outStatement.setInt(1, rs.getInt("event_number")); + outStatement.setString(2, rs.getString("city_id")); + outStatement.setString(3, rs.getString("city_name")); + outStatement.setString(4, rs.getString("city_motto")); + outStatement.setString(5, rs.getString("guild_id")); + + outStatement.setFloat(6, rs.getFloat("loc_x")); + outStatement.setFloat(7, rs.getFloat("loc_y")); + outStatement.setString(8, rs.getString("zone_id")); + outStatement.setString(9, rs.getString("eventType")); + outStatement.setTimestamp(10, rs.getTimestamp("datetime")); + + return outStatement; + } + + public static PreparedStatement buildCityQueryStatement(Connection connection) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "SELECT * FROM `warehouse_cityhistory` WHERE `event_number` > ?"; + outStatement = connection.prepareStatement(queryString); + outStatement.setInt(1, WarehousePushThread.cityIndex); + return outStatement; + } + + void reset() { + this.city = null; + this.cityHash = null; + this.cityGuildHash = null; + this.cityMotto = null; + this.zoneHash = null; + this.establishedDatetime = null; + + } + + public void release() { + this.reset(); + recordPool.add(this); + } + + public void write() { + + try (Connection connection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = this.buildCityInsertStatement(connection)) { + + statement.execute(); + + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private PreparedStatement buildCityInsertStatement(Connection connection) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "INSERT INTO `warehouse_cityhistory` (`city_id`, `city_name`, `city_motto`, `guild_id`, `loc_x`, `loc_y`, `zone_id`, `eventType`, `datetime`) VALUES(?,?,?,?,?,?,?,?,?)"; + + outStatement = connection.prepareStatement(queryString); + + // Bind character data + + outStatement.setString(1, this.cityHash); + outStatement.setString(2, this.cityName); + outStatement.setString(3, this.cityMotto); + outStatement.setString(4, this.cityGuildHash); + + outStatement.setFloat(5, this.locX); + outStatement.setFloat(6, this.locY); + outStatement.setString(7, this.zoneHash); + outStatement.setString(8, this.eventType.name()); + outStatement.setTimestamp(9, Timestamp.valueOf(this.establishedDatetime)); + + return outStatement; + } +} diff --git a/src/engine/db/archive/DataRecord.java b/src/engine/db/archive/DataRecord.java new file mode 100644 index 00000000..a86dd3a3 --- /dev/null +++ b/src/engine/db/archive/DataRecord.java @@ -0,0 +1,23 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.db.archive; + +import engine.Enum; + +class DataRecord { + + public Enum.DataRecordType recordType; + + DataRecord() { + + } + +} diff --git a/src/engine/db/archive/DataWarehouse.java b/src/engine/db/archive/DataWarehouse.java new file mode 100644 index 00000000..3947d72d --- /dev/null +++ b/src/engine/db/archive/DataWarehouse.java @@ -0,0 +1,324 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.archive; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import engine.gameManager.ConfigManager; +import engine.util.Hasher; +import org.pmw.tinylog.Logger; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.concurrent.LinkedBlockingQueue; + +import static engine.Enum.DataRecordType; + +public class DataWarehouse implements Runnable { + + public static final Hasher hasher = new Hasher("Cthulhu Owns Joo"); + private static final LinkedBlockingQueue recordQueue = new LinkedBlockingQueue<>(); + public static HikariDataSource connectionPool = null; + public static HikariDataSource remoteConnectionPool = null; + + public DataWarehouse() { + + Logger.info("Configuring local Database Connection Pool..."); + + configureConnectionPool(); + + // If WarehousePush is disabled + // then early exit + + if ( ConfigManager.MB_WORLD_WAREHOUSE_PUSH.getValue().equals("false")) { + Logger.info("Warehouse Remote Connection disabled along with push"); + return; + } + + Logger.info( "Configuring remote Database Connection Pool..."); + configureRemoteConnectionPool(); + + } + + public static void bootStrap() { + Thread warehousingThread; + warehousingThread = new Thread(new DataWarehouse()); + + warehousingThread.setName("DataWarehouse"); + warehousingThread.setPriority(Thread.NORM_PRIORITY - 1); + warehousingThread.start(); + } + + public static void pushToWarehouse(DataRecord dataRecord) { + + DataWarehouse.recordQueue.add(dataRecord); + } + + public static void writeHash(DataRecordType recordType, int uuid) { + + // Member variable declaration + + Connection connection = null; + PreparedStatement statement = null; + String queryString; + String hashString; + + try { + connection = DataWarehouse.connectionPool.getConnection(); + } catch (SQLException e) { + e.printStackTrace(); + } + + if (connection == null) { + Logger.error("Null connection when writing zone hash."); + return; + } + + // Build query string + + switch (recordType) { + case CHARACTER: + queryString = "UPDATE `obj_character` SET hash = ? WHERE `UID` = ?"; + break; + case GUILD: + queryString = "UPDATE `obj_guild` SET hash = ? WHERE `UID` = ?"; + break; + case ZONE: + queryString = "UPDATE `obj_zone` SET hash = ? WHERE `UID` = ?"; + break; + case CITY: + queryString = "UPDATE `obj_city` SET hash = ? WHERE `UID` = ?"; + break; + case REALM: + queryString = "UPDATE `obj_realm` SET hash = ? WHERE `realmID` = ?"; + break; + default: + queryString = null; + break; + } + + hashString = hasher.encrypt(uuid); + + // Write this record to the warehouse + + try { + + statement = connection.prepareStatement(queryString); + + statement.setString(1, hashString); + statement.setLong(2, uuid); + statement.execute(); + } catch (SQLException e) { + Logger.error("Error writing hash for uuid" + uuid + " of type " + recordType.name() + ' ' + e.toString()); + e.printStackTrace(); + } finally { + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + } + + public static boolean recordExists(DataRecordType recordType, int uuid) { + + // Member variable declaration + + Connection connection = null; + PreparedStatement statement = null; + String queryString; + ResultSet resultSet; + + try { + connection = DataWarehouse.connectionPool.getConnection(); + } catch (SQLException e) { + e.printStackTrace(); + } + + if (connection == null) { + Logger.error("Null connection during char record lookup"); + return true; // False positive here, so as not to try and write the record twice. + // will refactor out once we write hashes to object tables + } + + // Build query string + + switch (recordType) { + case CHARACTER: + queryString = "SELECT COUNT(*) from warehouse_characterhistory where char_id = ?"; + break; + case GUILD: + queryString = "SELECT COUNT(*) from warehouse_guildhistory where guild_id = ?"; + break; + case CITY: + queryString = "SELECT COUNT(*) from warehouse_cityhistory where city_id = ?"; + break; + case REALM: + queryString = "SELECT COUNT(*) from warehouse_realmhistory where realm_id = ?"; + break; + case BANE: + queryString = "SELECT COUNT(*) from warehouse_banehistory where city_id = ? AND `resolution` = 'PENDING'"; + break; + case ZONE: // Does not really exist but enum acts as a proxy for hash lookup + case MINE: // Does not really exist but enum acts as a proxy for hash lookup + default: + queryString = null; + break; + } + + try { + statement = connection.prepareStatement(queryString); + statement.setString(1, DataWarehouse.hasher.encrypt(uuid)); + resultSet = statement.executeQuery(); + + while (resultSet.next()) { + return resultSet.getInt("COUNT(*)") > 0; + } + + } catch (SQLException e) { + Logger.error("Error in record lookup for " + recordType.name() + " of uuid:" + uuid + e.toString()); + e.printStackTrace(); + } finally { + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + return false; + } + + public void run() { + + // Working variable set + + DataRecord dataRecord; + PvpRecord pvpRecord; + GuildRecord guildRecord; + CharacterRecord characterRecord; + CityRecord cityRecord; + BaneRecord baneRecord; + RealmRecord realmRecord; + MineRecord mineRecord; + + Logger.info( "DataWarehouse is running."); + + while (true) { + + dataRecord = null; + pvpRecord = null; + guildRecord = null; + characterRecord = null; + cityRecord = null; + baneRecord = null; + realmRecord = null; + mineRecord = null; + + try { + dataRecord = recordQueue.take(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + // Write record to appropriate warehousing table + + if (dataRecord != null) { + + switch (dataRecord.recordType) { + case PVP: + pvpRecord = (PvpRecord) dataRecord; + pvpRecord.write(); + pvpRecord.release(); + break; + case CHARACTER: + characterRecord = (CharacterRecord) dataRecord; + characterRecord.write(); + characterRecord.release(); + break; + case GUILD: + guildRecord = (GuildRecord) dataRecord; + guildRecord.write(); + guildRecord.release(); + break; + case CITY: + cityRecord = (CityRecord) dataRecord; + cityRecord.write(); + cityRecord.release(); + break; + case BANE: + baneRecord = (BaneRecord) dataRecord; + baneRecord.write(); + baneRecord.release(); + break; + case REALM: + realmRecord = (RealmRecord) dataRecord; + realmRecord.write(); + realmRecord.release(); + break; + case MINE: + mineRecord = (MineRecord) dataRecord; + mineRecord.write(); + mineRecord.release(); + break; + default: + Logger.error( "Unhandled record type"); + break; + + } // end switch + } + } + } + + private static void configureConnectionPool() { + + HikariConfig config = new HikariConfig(); + + config.setMaximumPoolSize(10); + + config.setJdbcUrl("jdbc:mysql://" + ConfigManager.MB_DATABASE_ADDRESS.getValue() + + ":" + ConfigManager.MB_DATABASE_PORT.getValue() + "/" + + ConfigManager.MB_DATABASE_NAME.getValue()); + config.setUsername(ConfigManager.MB_DATABASE_USER.getValue()); + config.setPassword( ConfigManager.MB_DATABASE_PASS.getValue()); + config.addDataSourceProperty("characterEncoding", "utf8"); + config.addDataSourceProperty("cachePrepStmts", "true"); + config.addDataSourceProperty("prepStmtCacheSize", "250"); + config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); + + connectionPool = new HikariDataSource(config); // setup the connection pool + + Logger.info("Local warehouse database connection configured"); + } + + private static void configureRemoteConnectionPool() { + + HikariConfig config = new HikariConfig(); + + config.setMaximumPoolSize(1); // Only the server talks to remote, so yeah. + config.setJdbcUrl(ConfigManager.MB_WAREHOUSE_ADDR.getValue()); + config.setUsername(ConfigManager.MB_WAREHOUSE_USER.getValue()); + config.setPassword(ConfigManager.MB_WAREHOUSE_PASS.getValue()); + config.addDataSourceProperty("characterEncoding", "utf8"); + config.addDataSourceProperty("cachePrepStmts", "true"); + config.addDataSourceProperty("prepStmtCacheSize", "250"); + config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); + + remoteConnectionPool = new HikariDataSource(config); // setup the connection pool + + Logger.info("remote warehouse connection configured"); + } + +} diff --git a/src/engine/db/archive/GuildRecord.java b/src/engine/db/archive/GuildRecord.java new file mode 100644 index 00000000..c68a3cb3 --- /dev/null +++ b/src/engine/db/archive/GuildRecord.java @@ -0,0 +1,215 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.archive; + +import engine.Enum; +import engine.Enum.RecordEventType; +import engine.objects.Guild; +import engine.workthreads.WarehousePushThread; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.HashMap; +import java.util.concurrent.LinkedBlockingQueue; + +public class GuildRecord extends DataRecord { + + private static final LinkedBlockingQueue recordPool = new LinkedBlockingQueue<>(); + private Enum.RecordEventType eventType; + private Guild guild; + public String guildHash; + private String guildName; + private String charterName; + private String GLHash; + private String guildMotto; + private int bgIcon; + private int bgColour1; + private int bgColour2; + private int fgIcon; + private int fgColour; + public int guildID; + + private java.time.LocalDateTime eventDatetime; + + public static HashMap GuildRecordCache = null; + + private GuildRecord(Guild guild) { + this.recordType = Enum.DataRecordType.GUILD; + this.guild = guild; + this.eventType = Enum.RecordEventType.CREATE; + } + + + + public GuildRecord(ResultSet rs) throws SQLException { + super(); + this.eventType = RecordEventType.valueOf(rs.getString("eventType")); + this.guildHash = rs.getString("guild_id"); + this.guildName = rs.getString("guild_name"); + this.charterName = rs.getString("charter"); + GLHash = rs.getString("guild_founder"); + this.guildMotto = rs.getString("guild_motto"); + this.bgIcon = rs.getInt("bgicon"); + this.bgColour1 = rs.getInt("bgcoloura"); + this.bgColour2 = rs.getInt("bgcolourb"); + this.fgIcon = rs.getInt("fgicon"); + this.fgColour = rs.getInt("fgcolour"); + + java.sql.Timestamp eventTimeStamp = rs.getTimestamp("upgradeDate"); + + if (eventTimeStamp != null) + this.eventDatetime = LocalDateTime.ofInstant(eventTimeStamp.toInstant(), ZoneId.systemDefault()); + } + + + + public static GuildRecord borrow(Guild guild, Enum.RecordEventType eventType) { + GuildRecord guildRecord; + //add + guildRecord = recordPool.poll(); + + if (guildRecord == null) { + guildRecord = new GuildRecord(guild); + guildRecord.eventType = eventType; + } + else { + guildRecord.guild = guild; + guildRecord.recordType = Enum.DataRecordType.GUILD; + guildRecord.eventType = eventType; + + } + + guildRecord.guildHash = guildRecord.guild.getHash(); + guildRecord.guildID = guildRecord.guild.getObjectUUID(); + guildRecord.guildName = guildRecord.guild.getName(); + guildRecord.charterName = Enum.GuildType.getGuildTypeFromInt(guildRecord.guild.getCharter()).getCharterName(); + + guildRecord.GLHash = DataWarehouse.hasher.encrypt(guildRecord.guild.getGuildLeaderUUID()); + + guildRecord.guildMotto = guildRecord.guild.getMotto(); + guildRecord.bgIcon = guildRecord.guild.getBgDesign(); + guildRecord.bgColour1 = guildRecord.guild.getBgc1(); + guildRecord.bgColour2 = guildRecord.guild.getBgc2(); + guildRecord.fgIcon = guildRecord.guild.getSymbol(); + guildRecord.fgColour = guildRecord.guild.getSc(); + + if (guild.getOwnedCity() != null) + guildRecord.eventDatetime = guild.getOwnedCity().established; + else + guildRecord.eventDatetime = LocalDateTime.now(); + + return guildRecord; + } + + public static PreparedStatement buildGuildPushStatement(Connection connection, ResultSet rs) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "INSERT INTO `warehouse_guildhistory` (`event_number`, `guild_id`, `guild_name`, `guild_motto`, `guild_founder`, `charter`, `bgicon`, `bgcoloura`, `bgcolourb`, `fgicon`, `fgcolour`, `eventtype`, `datetime`) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)"; + + outStatement = connection.prepareStatement(queryString); + + // Bind record data + + outStatement.setInt(1, rs.getInt("event_number")); + outStatement.setString(2, rs.getString("guild_id")); + outStatement.setString(3, rs.getString("guild_name")); + outStatement.setString(4, rs.getString("guild_motto")); + outStatement.setString(5, rs.getString("guild_founder")); + outStatement.setString(6, rs.getString("charter")); + outStatement.setInt(7, rs.getInt("bgicon")); + outStatement.setInt(8, rs.getInt("bgcoloura")); + outStatement.setInt(9, rs.getInt("bgcolourb")); + outStatement.setInt(10, rs.getInt("fgicon")); + outStatement.setInt(11, rs.getInt("fgcolour")); + outStatement.setString(12, rs.getString("eventtype")); + outStatement.setTimestamp(13, rs.getTimestamp("datetime")); + + return outStatement; + } + + public static PreparedStatement buildGuildQueryStatement(Connection connection) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "SELECT * FROM `warehouse_guildhistory` WHERE `event_number` > ?"; + outStatement = connection.prepareStatement(queryString); + outStatement.setInt(1, WarehousePushThread.guildIndex); + return outStatement; + } + + void reset() { + + this.guild = null; + this.guildHash = null; + this.GLHash = null; + this.guildMotto = null; + this.charterName = null; + this.eventDatetime = null; + } + + public void release() { + this.reset(); + recordPool.add(this); + } + + public void write() { + + try (Connection connection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = this.buildGuildInsertStatement(connection)) { + + statement.execute(); + + } catch (SQLException e) { + e.printStackTrace(); + } + + } + + private PreparedStatement buildGuildInsertStatement(Connection connection) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "INSERT INTO `warehouse_guildhistory` (`guild_id`, `guild_name`, `guild_motto`, `guild_founder`, `charter`, `bgicon`, `bgcoloura`, `bgcolourb`, `fgicon`, `fgcolour`, `eventtype`, `datetime`) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)"; + + outStatement = connection.prepareStatement(queryString); + + // Bind character data + + outStatement.setString(1, this.guildHash); + outStatement.setString(2, this.guildName); + outStatement.setString(3, this.guildMotto); + outStatement.setString(4, this.GLHash); + outStatement.setString(5, this.charterName); + + outStatement.setInt(6, this.bgIcon); + outStatement.setInt(7, this.bgColour1); + outStatement.setInt(8, this.bgColour2); + outStatement.setInt(9, this.fgIcon); + outStatement.setInt(10, this.fgColour); + outStatement.setString(11, this.eventType.name()); + outStatement.setTimestamp(12, new java.sql.Timestamp( this.eventDatetime.atZone(ZoneId.systemDefault()) + .toInstant().toEpochMilli())); + + return outStatement; + } + +// public static void InitializeGuildRecords(){ +// GuildRecord.GuildRecordCache = DbManager.GuildQueries.GET_WAREHOUSE_GUILD_HISTORY(); +// } +} + + + + + + + diff --git a/src/engine/db/archive/MineRecord.java b/src/engine/db/archive/MineRecord.java new file mode 100644 index 00000000..69621ba8 --- /dev/null +++ b/src/engine/db/archive/MineRecord.java @@ -0,0 +1,166 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.archive; + +import engine.Enum; +import engine.objects.AbstractCharacter; +import engine.objects.Mine; +import engine.objects.PlayerCharacter; +import engine.workthreads.WarehousePushThread; + +import java.sql.*; +import java.time.LocalDateTime; +import java.util.concurrent.LinkedBlockingQueue; + +public class MineRecord extends DataRecord { + + private static final LinkedBlockingQueue recordPool = new LinkedBlockingQueue<>(); + private Enum.RecordEventType eventType; + private String zoneHash; + private String charHash; + private String mineGuildHash; + private String mineNationHash; + private String mineType; + private float locX; + private float locY; + + private MineRecord() { + this.recordType = Enum.DataRecordType.MINE; + this.eventType = Enum.RecordEventType.CAPTURE; + + } + + public static MineRecord borrow(Mine mine, AbstractCharacter character, Enum.RecordEventType eventType) { + + MineRecord mineRecord; + mineRecord = recordPool.poll(); + PlayerCharacter player; + + if (mineRecord == null) { + mineRecord = new MineRecord(); + mineRecord.eventType = eventType; + } + else { + mineRecord.recordType = Enum.DataRecordType.MINE; + mineRecord.eventType = eventType; + } + + mineRecord.zoneHash = mine.getParentZone().getHash(); + + if (character.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) { + player = (PlayerCharacter) character; + mineRecord.charHash = player.getHash(); + } + else + mineRecord.charHash = character.getName(); + + DataWarehouse.hasher.encrypt(0); + + if (mine.getOwningGuild() == null) + mineRecord.mineGuildHash = "ERRANT"; + else + mineRecord.mineGuildHash = mine.getOwningGuild().getHash(); + + if (mine.getOwningGuild() == null) + mineRecord.mineNationHash = "ERRANT"; + else + mineRecord.mineNationHash = mine.getOwningGuild().getNation().getHash(); + + mineRecord.locX = mine.getParentZone().getLoc().x; + mineRecord.locY = -mine.getParentZone().getLoc().z; + + mineRecord.mineType = mine.getMineType().name; + + return mineRecord; + } + + public static PreparedStatement buildMinePushStatement(Connection connection, ResultSet rs) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "INSERT INTO `warehouse_minehistory` (`event_number`, `zone_id`, `mine_type`, `char_id`, `mine_guildID`, `mine_nationID`, `loc_x`, `loc_y`, `eventType`, `datetime`) VALUES(?,?,?,?,?,?,?,?,?,?)"; + outStatement = connection.prepareStatement(queryString); + + // Bind record data + + outStatement.setInt(1, rs.getInt("event_number")); + outStatement.setString(2, rs.getString("zone_id")); + outStatement.setString(3, rs.getString("char_id")); + outStatement.setString(4, rs.getString("mine_type")); + outStatement.setString(5, rs.getString("mine_guildID")); + outStatement.setString(6, rs.getString("mine_nationID")); + + outStatement.setFloat(7, rs.getFloat("loc_x")); + outStatement.setFloat(8, rs.getFloat("loc_y")); + outStatement.setString(9, rs.getString("eventType")); + outStatement.setTimestamp(10, rs.getTimestamp("datetime")); + + return outStatement; + } + + public static PreparedStatement buildMineQueryStatement(Connection connection) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "SELECT * FROM `warehouse_minehistory` WHERE `event_number` > ?"; + outStatement = connection.prepareStatement(queryString); + outStatement.setInt(1, WarehousePushThread.mineIndex); + return outStatement; + } + + void reset() { + this.zoneHash = null; + this.charHash = null; + this.mineGuildHash = null; + this.mineNationHash = null; + this.mineType = null; + this.locX = 0.0f; + this.locY = 0.0f; + + } + + public void release() { + this.reset(); + recordPool.add(this); + } + + public void write() { + + try (Connection connection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = this.buildMineInsertStatement(connection)) { + + statement.execute(); + + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private PreparedStatement buildMineInsertStatement(Connection connection) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "INSERT INTO `warehouse_minehistory` (`zone_id`, `mine_type`, `char_id`, `mine_guildID`, `mine_nationID`, `loc_x`, `loc_y`, `eventType`, `datetime`) VALUES(?,?,?,?,?,?,?,?,?)"; + + outStatement = connection.prepareStatement(queryString); + + // Bind character data + + outStatement.setString(1, this.zoneHash); + outStatement.setString(2, this.mineType); + outStatement.setString(3, this.charHash); + outStatement.setString(4, this.mineGuildHash); + outStatement.setString(5, this.mineNationHash); + + outStatement.setFloat(6, this.locX); + outStatement.setFloat(7, this.locY); + outStatement.setString(8, this.eventType.name()); + outStatement.setTimestamp(9, Timestamp.valueOf(LocalDateTime.now())); + + return outStatement; + } +} diff --git a/src/engine/db/archive/PvpRecord.java b/src/engine/db/archive/PvpRecord.java new file mode 100644 index 00000000..a3199127 --- /dev/null +++ b/src/engine/db/archive/PvpRecord.java @@ -0,0 +1,312 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.archive; + +import engine.gameManager.ZoneManager; +import engine.math.Vector3fImmutable; +import engine.objects.Guild; +import engine.objects.PlayerCharacter; +import engine.objects.Zone; +import engine.workthreads.WarehousePushThread; +import org.pmw.tinylog.Logger; + +import java.sql.*; +import java.time.LocalDateTime; +import java.util.LinkedList; +import java.util.concurrent.LinkedBlockingQueue; + +import static engine.Enum.DataRecordType; +import static engine.Enum.PvpHistoryType; + +public class PvpRecord extends DataRecord { + + private static final LinkedBlockingQueue recordPool = new LinkedBlockingQueue<>(); + + private PlayerCharacter player; + private PlayerCharacter victim; + private Vector3fImmutable location; + private boolean pvpExp; + + private PvpRecord(PlayerCharacter player, PlayerCharacter victim, Vector3fImmutable location, boolean pvpExp) { + this.recordType = DataRecordType.PVP; + this.player = player; + this.victim = victim; + this.location = new Vector3fImmutable(location); + this.pvpExp = pvpExp; + } + + public static PvpRecord borrow(PlayerCharacter player, PlayerCharacter victim, Vector3fImmutable location, boolean pvpExp) { + + PvpRecord pvpRecord; + + pvpRecord = recordPool.poll(); + + if (pvpRecord == null) { + pvpRecord = new PvpRecord(player, victim, location, pvpExp); + } + else { + pvpRecord.recordType = DataRecordType.PVP; + pvpRecord.player = player; + pvpRecord.victim = victim; + pvpRecord.location = new Vector3fImmutable(location); + pvpRecord.pvpExp = pvpExp; + } + + return pvpRecord; + } + + private static PreparedStatement buildHistoryStatement(Connection connection, int charUUID, PvpHistoryType historyType) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = ""; + + switch (historyType) { + case KILLS: + queryString = "SELECT DISTINCT `victim_id`, `datetime` FROM warehouse_pvphistory where char_id = ? " + + "ORDER BY `datetime` DESC LIMIT 10"; + break; + case DEATHS: + queryString = "SELECT DISTINCT `char_id`,`datetime` FROM warehouse_pvphistory where `victim_id` = ? " + + "ORDER BY `datetime` DESC LIMIT 10"; + break; + } + + outStatement = connection.prepareStatement(queryString); + outStatement.setString(1, DataWarehouse.hasher.encrypt(charUUID)); + + return outStatement; + } + + public static LinkedList getCharacterPvPHistory(int charUUID, PvpHistoryType historyType) { + + // Member variable declaration + + LinkedList outList = new LinkedList<>(); + + try (Connection connection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = buildHistoryStatement(connection, charUUID, historyType); + ResultSet rs = statement.executeQuery()) { + + while (rs.next()) { + + switch (historyType) { + case KILLS: + outList.add((int) DataWarehouse.hasher.decrypt(rs.getString("victim_id"))[0]); + break; + case DEATHS: + outList.add((int) DataWarehouse.hasher.decrypt(rs.getString("char_id"))[0]); + break; + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + return outList; + } + + private static PreparedStatement buildLuaHistoryQueryStatement(Connection connection, int charUUID) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "CALL `pvpHistory`(?)"; + + outStatement = connection.prepareStatement(queryString); + outStatement.setString(1, DataWarehouse.hasher.encrypt(charUUID)); + + return outStatement; + } + + public static String getPvpHistoryString(int charUUID) { + + String outString; + String dividerString; + + String newLine = System.getProperty("line.separator"); + + outString = "[LUA_PVP() DATA WAREHOUSE]" + newLine; + dividerString = "--------------------------------" + newLine; + + try (Connection connection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = buildLuaHistoryQueryStatement(connection, charUUID); + ResultSet rs = statement.executeQuery()) { + + while (rs.next()) { + + int killCount; + int deathCount; + float killRatio; + + outString += "Total Magicbane murdered souls: " + rs.getInt("TOTALDEATHS") + newLine; + outString += dividerString; + outString += String.format("%-8s %-8s %-8s %-8s %n", "Period", "Kills", "Deaths", "K/D"); + outString += dividerString; + + killCount = rs.getInt("KILLCOUNT"); + deathCount = rs.getInt("DEATHCOUNT"); + + if (deathCount == 0) + killRatio = (float) killCount; + else + killRatio = (float) killCount / deathCount; + + try { + outString += String.format("%-8s %-8d %-8d %.2f %n", "Total", killCount, deathCount, killRatio); + + killCount = rs.getInt("DAILYKILLS"); + deathCount = rs.getInt("DAILYDEATHS"); + + if (deathCount == 0) + killRatio = (float) killCount; + else + killRatio = (float) killCount / deathCount; + + outString += String.format("%-8s %-8d %-8d %.2f %n", "24hrs", killCount, deathCount, killRatio); + + killCount = rs.getInt("HOURLYKILLS"); + deathCount = rs.getInt("HOURLYDEATHS"); + + if (deathCount == 0) + killRatio = (float) killCount; + else + killRatio = (float) killCount / deathCount; + + outString += String.format("%-8s %-8d %-8d %.2f %n", "1hr", killCount, deathCount, killRatio); + } catch (Exception e) { + Logger.error(e.toString()); + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + + return outString; + } + + public static PreparedStatement buildPvpPushStatement(Connection connection, ResultSet rs) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "INSERT INTO `warehouse_pvphistory` (`event_number`, `char_id`, `char_guild_id`, `char_nation_id`, `char_level`," + + " `victim_id`, `victim_guild_id`, `victim_nation_id`, `victim_level`," + + " `zone_id`, `zone_name`, `loc_x`, `loc_y`, `gave_exp`, `datetime`) " + + " VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; + + outStatement = connection.prepareStatement(queryString); + + // Bind record data + + outStatement.setInt(1, rs.getInt("event_number")); + outStatement.setString(2, rs.getString("char_id")); + outStatement.setString(3, rs.getString("char_guild_id")); + outStatement.setString(4, rs.getString("char_nation_id")); + outStatement.setInt(5, rs.getInt("char_level")); + + // Bind victim data + + outStatement.setString(6, rs.getString("victim_id")); + outStatement.setString(7, rs.getString("victim_guild_id")); + outStatement.setString(8, rs.getString("victim_nation_id")); + outStatement.setInt(9, rs.getInt("victim_level")); + + outStatement.setString(10, rs.getString("zone_id")); + outStatement.setString(11, rs.getString("zone_name")); + outStatement.setFloat(12, rs.getFloat("loc_x")); + outStatement.setFloat(13, rs.getFloat("loc_y")); + outStatement.setBoolean(14, rs.getBoolean("gave_exp")); + outStatement.setTimestamp(15, rs.getTimestamp("datetime")); + + return outStatement; + } + + public static PreparedStatement buildPvpQueryStatement(Connection connection) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "SELECT * FROM `warehouse_pvphistory` WHERE `event_number` > ?"; + outStatement = connection.prepareStatement(queryString); + outStatement.setInt(1, WarehousePushThread.pvpIndex); + return outStatement; + } + + void reset() { + this.player = null; + this.victim = null; + this.location = Vector3fImmutable.ZERO; + pvpExp = false; + } + + public void release() { + this.reset(); + recordPool.add(this); + } + + private PreparedStatement buildPvPInsertStatement(Connection connection) throws SQLException { + + Guild charGuild; + Guild victimGuild; + Zone zone; + PreparedStatement outStatement = null; + + String queryString = "INSERT INTO `warehouse_pvphistory` (`char_id`, `char_guild_id`, `char_nation_id`, `char_level`," + + " `victim_id`, `victim_guild_id`, `victim_nation_id`, `victim_level`," + + " `zone_id`, `zone_name`, `loc_x`, `loc_y`, `gave_exp`, `datetime`) " + + " VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; + + outStatement = connection.prepareStatement(queryString); + + charGuild = this.player.getGuild(); + victimGuild = this.victim.getGuild(); + + // Use a proxy in the situation where a char guild is null (errant) + + + // Retrieve the zone name where the PvP event occurred + + zone = ZoneManager.findSmallestZone(this.location); + + outStatement.setString(1, DataWarehouse.hasher.encrypt(this.player.getObjectUUID())); + outStatement.setString(2, DataWarehouse.hasher.encrypt(charGuild.getObjectUUID())); + outStatement.setString(3, DataWarehouse.hasher.encrypt(charGuild.getNation().getObjectUUID())); + outStatement.setInt(4, this.player.getLevel()); + + // Bind victim data + + outStatement.setString(5, DataWarehouse.hasher.encrypt(this.victim.getObjectUUID())); + outStatement.setString(6, DataWarehouse.hasher.encrypt(victimGuild.getObjectUUID())); + outStatement.setString(7, DataWarehouse.hasher.encrypt(victimGuild.getNation().getObjectUUID())); + outStatement.setInt(8, this.victim.getLevel()); + + outStatement.setString(9, DataWarehouse.hasher.encrypt(zone.getObjectUUID())); + outStatement.setString(10, zone.getName()); + outStatement.setFloat(11, this.location.getX()); + outStatement.setFloat(12, -this.location.getZ()); // flip sign on 'y' coordinate + outStatement.setBoolean(13, this.pvpExp); + outStatement.setTimestamp(14, Timestamp.valueOf(LocalDateTime.now())); + + return outStatement; + } + + + public void write() { + + try (Connection connection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = buildPvPInsertStatement(connection)) { + + statement.execute(); + + } catch (SQLException e) { + Logger.error( e.toString()); + } + + // Warehouse record for this pvp event written if code path reaches here. + // Time to update the respective kill counters. + + CharacterRecord.advanceKillCounter(this.player); + CharacterRecord.advanceDeathCounter(this.victim); + + } +} diff --git a/src/engine/db/archive/RealmRecord.java b/src/engine/db/archive/RealmRecord.java new file mode 100644 index 00000000..556eebe2 --- /dev/null +++ b/src/engine/db/archive/RealmRecord.java @@ -0,0 +1,142 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.archive; + +import engine.Enum; +import engine.objects.Realm; +import engine.workthreads.WarehousePushThread; + +import java.sql.*; +import java.time.LocalDateTime; +import java.util.concurrent.LinkedBlockingQueue; + +public class RealmRecord extends DataRecord { + + private static final LinkedBlockingQueue recordPool = new LinkedBlockingQueue<>(); + + private Realm realm; + private Enum.RecordEventType eventType; + private String cityHash; + private String guildHash; + private String charterType; + private LocalDateTime eventDateTime; + + private RealmRecord(Realm realm) { + this.recordType = Enum.DataRecordType.REALM; + this.realm = realm; + this.eventType = Enum.RecordEventType.CAPTURE; + + } + + public static RealmRecord borrow(Realm realm, Enum.RecordEventType eventType) { + RealmRecord realmRecord; + + realmRecord = recordPool.poll(); + + if (realmRecord == null) { + realmRecord = new RealmRecord(realm); + realmRecord.eventType = eventType; + } + else { + realmRecord.recordType = Enum.DataRecordType.REALM; + realmRecord.eventType = eventType; + realmRecord.realm = realm; + + } + + realmRecord.cityHash = realm.getRulingCity().getHash(); + realmRecord.guildHash = realm.getRulingCity().getGuild().getHash(); + realmRecord.charterType = Enum.CharterType.getCharterTypeByID(realmRecord.realm.getCharterType()).name(); + + if (realmRecord.eventType.equals(Enum.RecordEventType.CAPTURE)) + realmRecord.eventDateTime = realm.ruledSince; + else + realmRecord.eventDateTime = LocalDateTime.now(); + + return realmRecord; + } + + public static PreparedStatement buildRealmPushStatement(Connection connection, ResultSet rs) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "INSERT INTO `warehouse_realmhistory` (`event_number`, `realm_id`, `realm_name`, `charter`, `city_id`, `guild_id`, `eventType`, `datetime`) VALUES(?,?,?,?,?,?,?,?)"; + + outStatement = connection.prepareStatement(queryString); + + // Bind record data + + outStatement.setInt(1, rs.getInt("event_number")); + outStatement.setString(2, rs.getString("realm_id")); + outStatement.setString(3, rs.getString("realm_name")); + outStatement.setString(4, rs.getString("charter")); + outStatement.setString(5, rs.getString("city_id")); + outStatement.setString(6, rs.getString("guild_id")); + outStatement.setString(7, rs.getString("eventType")); + outStatement.setTimestamp(8, rs.getTimestamp("datetime")); + + return outStatement; + } + + public static PreparedStatement buildRealmQueryStatement(Connection connection) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "SELECT * FROM `warehouse_realmhistory` WHERE `event_number` > ?"; + outStatement = connection.prepareStatement(queryString); + outStatement.setInt(1, WarehousePushThread.realmIndex); + return outStatement; + } + + void reset() { + + this.realm = null; + this.cityHash = null; + this.guildHash = null; + this.eventDateTime = null; + this.charterType = null; + } + + public void release() { + this.reset(); + recordPool.add(this); + } + + private PreparedStatement buildRealmInsertStatement(Connection connection) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "INSERT INTO `warehouse_realmhistory` (`realm_id`, `realm_name`, `charter`, `city_id`, `guild_id`, `eventType`, `datetime`) VALUES(?,?,?,?,?,?,?)"; + outStatement = connection.prepareStatement(queryString); + + // Bind Record Data + + outStatement.setString(1, realm.getHash()); + outStatement.setString(2, realm.getRealmName()); + outStatement.setString(3, charterType); + outStatement.setString(4, cityHash); + outStatement.setString(5, guildHash); + outStatement.setString(6, eventType.name()); + outStatement.setTimestamp(7, Timestamp.valueOf(this.eventDateTime)); + + return outStatement; + } + + public void write() { + + try (Connection connection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = this.buildRealmInsertStatement(connection)) { + + statement.execute(); + + } catch (SQLException e) { + e.printStackTrace(); + } + + } + +} diff --git a/src/engine/db/handlers/dbAccountHandler.java b/src/engine/db/handlers/dbAccountHandler.java new file mode 100644 index 00000000..ebf6048d --- /dev/null +++ b/src/engine/db/handlers/dbAccountHandler.java @@ -0,0 +1,202 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.gameManager.ConfigManager; +import engine.gameManager.DbManager; +import engine.objects.Account; +import engine.objects.PlayerCharacter; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; + +public class dbAccountHandler extends dbHandlerBase { + + public dbAccountHandler() { + this.localClass = Account.class; + this.localObjectType = Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public Account GET_ACCOUNT(int id) { + if (id == 0) + return null; + Account account = (Account) DbManager.getFromCache(GameObjectType.Account, id); + if (account != null) + return account; + + prepareCallable("SELECT * FROM `obj_account` WHERE `UID`=?"); + setLong(1, (long) id); + + Account ac = null; + ac = (Account) getObjectSingle(id); + + if (ac != null) + ac.runAfterLoad(); + + return ac; + } + + public void SET_TRASH(String machineID) { + + prepareCallable("INSERT INTO dyn_trash(`machineID`, `count`)" + + " VALUES (?, 1) ON DUPLICATE KEY UPDATE `count` = `count` + 1;"); + + setString(1, machineID); + executeUpdate(); + + } + + public ArrayList GET_TRASH_LIST() { + + ArrayList machineList = new ArrayList<>(); + + prepareCallable("select `machineID` from `dyn_trash`"); + + try { + ResultSet rs = executeQuery(); + while (rs.next()) { + machineList.add(rs.getString(1)); + } + } catch (SQLException e) { + Logger.error( e); + } finally { + closeCallable(); + } + + return machineList; + } + + public boolean DELETE_VAULT_FOR_ACCOUNT(final int accountID) { + prepareCallable("DELETE FROM `object` WHERE `parent`=? && `type`='item'"); + setLong(1, (long) accountID); + return (executeUpdate() > 0); + } + + public ArrayList GET_ALL_CHARS_FOR_MACHINE(String machineID) { + + ArrayList trashList = new ArrayList<>(); + + prepareCallable("select DISTINCT UID from object \n" + + "where parent IN (select AccountID from dyn_login_history " + + " WHERE`machineID`=?)"); + setString(1, machineID); + + try { + ResultSet rs = executeQuery(); + while (rs.next()) { + + PlayerCharacter trashPlayer; + int playerID; + + playerID = rs.getInt(1); + trashPlayer = PlayerCharacter.getPlayerCharacter(playerID); + + if (trashPlayer == null) + continue;; + + if (trashPlayer.isDeleted() == false) + trashList.add(trashPlayer); + } + } catch (SQLException e) { + Logger.error( e); + } finally { + closeCallable(); + } + return trashList; + } + + public void CLEAR_TRASH_TABLE() { + prepareCallable("DELETE FROM dyn_trash"); + executeUpdate(); + } + + public Account GET_ACCOUNT(String uname) { + + if (Account.AccountsMap.get(uname) != null) + return this.GET_ACCOUNT(Account.AccountsMap.get(uname)); + + prepareCallable("SELECT * FROM `obj_account` WHERE `acct_uname`=?"); + setString(1, uname); + ArrayList temp = getObjectList(); + + if (temp.isEmpty()) + return null; + + if (temp.get(0) != null){ + temp.get(0).runAfterLoad(); + + if (ConfigManager.serverType.equals(Enum.ServerType.LOGINSERVER)) + Account.AccountsMap.put(uname, temp.get(0).getObjectUUID()); + + } + return temp.get(0); + } + + public void SET_ACCOUNT_LOGIN(final Account acc, String playerName, final String ip, final String machineID) { + + if (acc.getObjectUUID() == 0 || ip == null || ip.length() == 0) + return; + + prepareCallable("INSERT INTO dyn_login_history(`AccountID`, `accountName`, `characterName`, `ip`, `machineID`, `timeStamp`)" + + " VALUES (?, ?, ?, ?, ?, ?)"); + + setInt(1, acc.getObjectUUID()); + setString(2, acc.getUname()); + setString(3, playerName); + setString(4, ip); + setString(5, machineID); + setTimeStamp(6, System.currentTimeMillis()); + executeUpdate(); + } + + public String SET_PROPERTY(final Account a, String name, Object new_value) { + prepareCallable("CALL account_SETPROP(?,?,?)"); + setLong(1, (long) a.getObjectUUID()); + setString(2, name); + setString(3, String.valueOf(new_value)); + return getResult(); + } + + public String SET_PROPERTY(final Account a, String name, Object new_value, Object old_value) { + prepareCallable("CALL account_GETSETPROP(?,?,?,?)"); + setLong(1, (long) a.getObjectUUID()); + setString(2, name); + setString(3, String.valueOf(new_value)); + setString(4, String.valueOf(old_value)); + return getResult(); + } + + + public void updateDatabase(final Account acc) { + prepareCallable("UPDATE `obj_account` SET `acct_passwd`=?, " + + " `acct_lastCharUID`=?, `acct_salt`=?, `discordAccount`=?, " + + " status = ? WHERE `UID`=?"); + + setString(1, acc.getPasswd()); + setInt(2, acc.getLastCharIDUsed()); + setString(3, acc.getSalt()); + setString(4, acc.discordAccount); + setString(5, acc.status.name()); + setInt(6, acc.getObjectUUID()); + executeUpdate(); + } + + public void INVALIDATE_LOGIN_CACHE(long accountUID, String objectType) { + prepareCallable("INSERT IGNORE INTO login_cachelist (`UID`, `type`) VALUES(?,?);"); + setLong(1, accountUID); + setString(2, objectType); + executeUpdate(); + } + +} diff --git a/src/engine/db/handlers/dbBaneHandler.java b/src/engine/db/handlers/dbBaneHandler.java new file mode 100644 index 00000000..2748095e --- /dev/null +++ b/src/engine/db/handlers/dbBaneHandler.java @@ -0,0 +1,115 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.objects.Bane; +import engine.objects.Building; +import engine.objects.City; +import engine.objects.PlayerCharacter; +import org.joda.time.DateTime; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; + +public class dbBaneHandler extends dbHandlerBase { + + public dbBaneHandler() { + + } + + public boolean CREATE_BANE(City city, PlayerCharacter owner, Building stone) { + + prepareCallable("INSERT INTO `dyn_banes` (`cityUUID`, `ownerUUID`, `stoneUUID`, `placementDate`) VALUES(?,?,?,?)"); + setLong(1, (long) city.getObjectUUID()); + setLong(2, (long) owner.getObjectUUID()); + setLong(3, (long) stone.getObjectUUID()); + setTimeStamp(4, System.currentTimeMillis()); + + return (executeUpdate() > 0); + + } + + public Bane LOAD_BANE(int cityUUID) { + + Bane newBane = null; + + try { + + prepareCallable("SELECT * from dyn_banes WHERE `dyn_banes`.`cityUUID` = ?"); + + setLong(1, (long) cityUUID); + ResultSet rs = executeQuery(); + + if (rs.next()) { + newBane = new Bane(rs); + Bane.addBane(newBane); + } + + } catch (SQLException ex) { + java.util.logging.Logger.getLogger(dbBaneHandler.class.getName()).log(Level.SEVERE, null, ex); + } finally { + closeCallable(); + } + return newBane; + + } + + public ConcurrentHashMap LOAD_ALL_BANES() { + + ConcurrentHashMap baneList; + Bane thisBane; + + baneList = new ConcurrentHashMap<>(); + + int recordsRead = 0; + + prepareCallable("SELECT * FROM dyn_banes"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + recordsRead++; + thisBane = new Bane(rs); + baneList.put(thisBane.getCityUUID(), thisBane); + + } + + Logger.info("read: " + recordsRead + " cached: " + baneList.size()); + + } catch (SQLException e) { + Logger.error( e.toString()); + } finally { + closeCallable(); + } + return baneList; + } + + public boolean SET_BANE_TIME(DateTime toSet, int cityUUID) { + prepareCallable("UPDATE `dyn_banes` SET `liveDate`=? WHERE `cityUUID`=?"); + setTimeStamp(1, toSet.getMillis()); + setLong(2, cityUUID); + return (executeUpdate() > 0); + } + + public boolean REMOVE_BANE(Bane bane) { + + if (bane == null) + return false; + + prepareCallable("DELETE FROM `dyn_banes` WHERE `cityUUID` = ?"); + setLong(1, (long) bane.getCity().getObjectUUID()); + return (executeUpdate() > 0); + } +} diff --git a/src/engine/db/handlers/dbBaseClassHandler.java b/src/engine/db/handlers/dbBaseClassHandler.java new file mode 100644 index 00000000..3ae0f098 --- /dev/null +++ b/src/engine/db/handlers/dbBaseClassHandler.java @@ -0,0 +1,50 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.gameManager.DbManager; +import engine.objects.BaseClass; + +import java.util.ArrayList; + +public class dbBaseClassHandler extends dbHandlerBase { + + public dbBaseClassHandler() { + this.localClass = BaseClass.class; + this.localObjectType = Enum.GameObjectType.BaseClass; + } + + public BaseClass GET_BASE_CLASS(final int id) { + + if (id == 0) + return null; + BaseClass baseClass = (BaseClass) DbManager.getFromCache(GameObjectType.BaseClass, id); + if (baseClass != null) + return baseClass; + + + prepareCallable("SELECT * FROM `static_rune_baseclass` WHERE `ID` = ?;"); + setInt(1, id); + return (BaseClass) getObjectSingle(id); + } + + public ArrayList GET_BASECLASS_FOR_RACE(final int id) { + prepareCallable("SELECT b.* FROM `static_rune_baseclass` b, `static_rune_racebaseclass` r WHERE b.`ID` = r.`BaseClassID` && r.`RaceID` = ?"); + setInt(1, id); + return getObjectList(); + } + + public ArrayList GET_ALL_BASE_CLASSES(){ + prepareCallable("SELECT * FROM `static_rune_baseclass`;"); + return getObjectList(); + } +} diff --git a/src/engine/db/handlers/dbBlueprintHandler.java b/src/engine/db/handlers/dbBlueprintHandler.java new file mode 100644 index 00000000..7ecd4dfe --- /dev/null +++ b/src/engine/db/handlers/dbBlueprintHandler.java @@ -0,0 +1,86 @@ +package engine.db.handlers; + +import engine.objects.Blueprint; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; + +public class dbBlueprintHandler extends dbHandlerBase { + + public dbBlueprintHandler() { + + } + + public HashMap LOAD_ALL_DOOR_NUMBERS() { + + HashMap doorInfo; + doorInfo = new HashMap<>(); + + int doorUUID; + int doorNum; + int recordsRead = 0; + + prepareCallable("SELECT * FROM static_building_doors ORDER BY doorMeshUUID ASC"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + recordsRead++; + doorUUID = rs.getInt("doorMeshUUID"); + doorNum = rs.getInt("doorNumber"); + doorInfo.put(doorUUID, doorNum); + } + + Logger.info( "read: " + recordsRead + " cached: " + doorInfo.size()); + + } catch (SQLException e) { + Logger.error("LoadAllDoorNumbers: " + e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + return doorInfo; + } + + public HashMap LOAD_ALL_BLUEPRINTS() { + + HashMap blueprints; + Blueprint thisBlueprint; + + blueprints = new HashMap<>(); + int recordsRead = 0; + + prepareCallable("SELECT * FROM static_building_blueprint"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + recordsRead++; + thisBlueprint = new Blueprint(rs); + + blueprints.put(thisBlueprint.getBlueprintUUID(), thisBlueprint); + + // load mesh cache + Blueprint._meshLookup.putIfAbsent(thisBlueprint.getMeshForRank(-1), thisBlueprint); + Blueprint._meshLookup.putIfAbsent(thisBlueprint.getMeshForRank(0), thisBlueprint); + Blueprint._meshLookup.putIfAbsent(thisBlueprint.getMeshForRank(1), thisBlueprint); + Blueprint._meshLookup.putIfAbsent(thisBlueprint.getMeshForRank(3), thisBlueprint); + Blueprint._meshLookup.putIfAbsent(thisBlueprint.getMeshForRank(7), thisBlueprint); + + } + + Logger.info( "read: " + recordsRead + " cached: " + blueprints.size()); + + } catch (SQLException e) { + Logger.error("LoadAllBlueprints: " + e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + return blueprints; + } +} diff --git a/src/engine/db/handlers/dbBoonHandler.java b/src/engine/db/handlers/dbBoonHandler.java new file mode 100644 index 00000000..ca7b6db1 --- /dev/null +++ b/src/engine/db/handlers/dbBoonHandler.java @@ -0,0 +1,51 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.objects.Boon; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; + +public class dbBoonHandler extends dbHandlerBase { + + public dbBoonHandler() { + } + + + public ArrayList GET_BOON_AMOUNTS_FOR_ITEMBASEUUID(int itemBaseUUID){ + + ArrayListboons = new ArrayList<>(); + Boon thisBoon; + prepareCallable("SELECT * FROM `static_item_boons` WHERE `itemBaseID` = ?"); + setInt(1, itemBaseUUID); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + + thisBoon = new Boon(rs); + boons.add(thisBoon); + } + + + + } catch (SQLException e) { + Logger.error("GetBoonAmountsForItembaseUUID: " + e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + return boons; +} +} diff --git a/src/engine/db/handlers/dbBuildingHandler.java b/src/engine/db/handlers/dbBuildingHandler.java new file mode 100644 index 00000000..68eb1ba0 --- /dev/null +++ b/src/engine/db/handlers/dbBuildingHandler.java @@ -0,0 +1,809 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.Enum; +import engine.Enum.DbObjectType; +import engine.Enum.ProtectionState; +import engine.Enum.TaxType; +import engine.gameManager.DbManager; +import engine.math.Vector3fImmutable; +import engine.objects.*; +import engine.server.MBServerStatics; +import org.joda.time.DateTime; +import org.pmw.tinylog.Logger; + +import java.awt.geom.Line2D; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; + +public class dbBuildingHandler extends dbHandlerBase { + + public dbBuildingHandler() { + this.localClass = Building.class; + this.localObjectType = Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public Building CREATE_BUILDING(Building b) { + try { + b = this.addBuilding(b); + b.setObjectTypeMask(MBServerStatics.MASK_BUILDING); + } catch (Exception e) { + Logger.error(e); + b = null; + } + return b; + } + + /* + * + * @param worldServerID + * @param OwnerUUID + * @param name + * @param meshUUID + * @param meshScale + * @param currentHP + * @param protectionState + * @param currentGold + * @param rank + * @param upgradeDate + * @param blueprintUUID + * @param w + * @param rotY + * @return + */ + public Building CREATE_BUILDING(int parentZoneID, int OwnerUUID, String name, int meshUUID, + Vector3fImmutable location, float meshScale, int currentHP, + ProtectionState protectionState, int currentGold, int rank, + DateTime upgradeDate, int blueprintUUID, float w, float rotY) { + + Building toCreate = null; + try { + + prepareCallable("CALL `building_CREATE`(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ,? ,? ,?, ?);"); + + setInt(1, parentZoneID); + setInt(2, OwnerUUID); + setString(3, name); + setInt(4, meshUUID); + setFloat(5, location.x); + setFloat(6, location.y); + setFloat(7, location.z); + setFloat(8, meshScale); + setInt(9, currentHP); + setString(10, protectionState.name()); + setInt(11, currentGold); + setInt(12, rank); + + if (upgradeDate != null) + setTimeStamp(13, upgradeDate.getMillis()); + else + setNULL(13, java.sql.Types.DATE); + + setInt(14, blueprintUUID); + setFloat(15, w); + setFloat(16, rotY); + + int objectUUID = (int) getUUID(); + + if (objectUUID > 0) + toCreate = GET_BUILDINGBYUUID(objectUUID); + + } catch (Exception e) { + Logger.error("CREATE_BUILDING", e.getMessage()); + return null; + } + return toCreate; + + } + + public boolean DELETE_FROM_DATABASE(final Building b) { + + return removeFromBuildings(b); + } + + public ArrayList GET_ALL_BUILDINGS_FOR_ZONE(Zone zone) { + prepareCallable("SELECT `obj_building`.*, `object`.`parent` FROM `object` INNER JOIN `obj_building` ON `obj_building`.`UID` = `object`.`UID` WHERE `object`.`parent` = ?;"); + setLong(1, zone.getObjectUUID()); + return getLargeObjectList(); + } + + public ArrayList GET_ALL_BUILDINGS() { + prepareCallable("SELECT `obj_building`.*, `object`.`parent` FROM `object` INNER JOIN `obj_building` ON `obj_building`.`UID` = `object`.`UID`;"); + return getLargeObjectList(); + } + + public Building GET_BUILDINGBYUUID(int uuid) { + if (uuid == 0) + return null; + + Building building = (Building) DbManager.getFromCache(Enum.GameObjectType.Building, uuid); + + if (building != null) + return building; + + prepareCallable("SELECT `obj_building`.*, `object`.`parent` FROM `object` INNER JOIN `obj_building` ON `obj_building`.`UID` = `object`.`UID` WHERE `object`.`UID` = ?;"); + + setLong(1, (long) uuid); + return (Building) getObjectSingle(uuid); + + } + + public Building GET_BUILDING_BY_MESH(final int meshID) { + Building toReturn = null; + prepareCallable("CALL building_GETBYMESH(?)"); + setInt(1, meshID); + try { + ResultSet rs = executeQuery(); + if (rs.next()) + toReturn = new Building(rs); + rs.close(); + } catch (SQLException e) { + Logger.error("Building", e); + return null; + } finally { + closeCallable(); + } + return toReturn; + } + + public String SET_PROPERTY(final Building b, String name, Object new_value) { + prepareCallable("CALL building_SETPROP(?,?,?)"); + setLong(1, b.getObjectUUID()); + setString(2, name); + setString(3, String.valueOf(new_value)); + return getResult(); + } + + public String SET_PROPERTY(final Building b, String name, Object new_value, Object old_value) { + prepareCallable("CALL building_GETSETPROP(?,?,?,?)"); + setLong(1, b.getObjectUUID()); + setString(2, name); + setString(3, String.valueOf(new_value)); + setString(4, String.valueOf(old_value)); + return getResult(); + } + + public int MOVE_BUILDING(long buildingID, long parentID, float locX, float locY, float locZ) { + prepareCallable("UPDATE `object` INNER JOIN `obj_building` On `object`.`UID` = `obj_building`.`UID` SET `object`.`parent`=?, `obj_building`.`locationX`=?, `obj_building`.`locationY`=?, `obj_building`.`locationZ`=? WHERE `obj_building`.`UID`=?;"); + setLong(1, parentID); + setFloat(2, locX); + setFloat(3, locY); + setFloat(4, locZ); + setLong(5, buildingID); + return executeUpdate(); + } + + private Building addBuilding(Building toAdd) { + prepareCallable("CALL `building_CREATE`(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); + setLong(1, toAdd.getParentZoneID()); + setLong(2, toAdd.getOwnerUUID()); + setString(3, toAdd.getName()); + setInt(4, toAdd.getMeshUUID()); + setFloat(5, toAdd.getStatLat()); + setFloat(6, toAdd.getStatAlt()); + setFloat(7, toAdd.getStatLon()); + setFloat(8, toAdd.getMeshScale().x); + setInt(9, (int) toAdd.getHealth()); + setString(10, toAdd.getProtectionState().name()); + setInt(11, toAdd.getStrongboxValue()); + setInt(12, toAdd.getRank()); + + // Write Joda DateTime to database + // *** Refactor : Wrap this logic in our + // own override setDateTime() ? + if (toAdd.getUpgradeDateTime() != null) + setLocalDateTime(13, toAdd.getUpgradeDateTime()); + else + setNULL(13, java.sql.Types.DATE); + + setInt(14, toAdd.getBlueprintUUID()); + setFloat(15, toAdd.getw()); + setFloat(16, toAdd.getRot().y); + + int objectUUID = (int) getUUID(); + + if (objectUUID > 0) + return GET_BUILDINGBYUUID(objectUUID); + return null; + + } + + private boolean removeFromBuildings(final Building b) { + prepareCallable("DELETE FROM `object` WHERE `UID` = ?"); + setLong(1, b.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean ClaimAsset(final long SetBuildingID, int newowner, int OldOwner) { + + prepareCallable("UPDATE `obj_building` SET `ownerUUID`=? WHERE `UID`=? and `ownerUUID`= ?"); + setInt(1, newowner); + setLong(2, SetBuildingID); + setLong(3, OldOwner); + return (executeUpdate() > 0); + } + + public boolean CHANGE_NAME(Building b, String newName) { + prepareCallable("UPDATE `obj_building` SET `name`=? WHERE `UID`=?"); + setString(1, newName); + setLong(2, b.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean SET_RESERVE(Building b, int reserveAmount) { + prepareCallable("UPDATE `obj_building` SET `reserve`=? WHERE `UID`=?"); + setInt(1, reserveAmount); + setLong(2, b.getObjectUUID()); + return (executeUpdate() > 0); + } + + //CAS update to rank + public boolean CHANGE_RANK(final long buildingID, int newRank) { + prepareCallable("UPDATE `obj_building` SET `rank`=? WHERE `UID`=?"); + setInt(1, newRank); + setLong(2, buildingID); + return (executeUpdate() > 0); + } + + public boolean UPDATE_BUILDING_HEALTH(final long buildingID, int NewHealth) { + prepareCallable("UPDATE `obj_building` SET `currentHP`=? WHERE `UID`=?"); + setInt(1, NewHealth); + setLong(2, buildingID); + return (executeUpdate() > 0); + } + + public boolean UPDATE_BUILDING_ALTITUDE(final long buildingID, float newAltitude) { + prepareCallable("UPDATE `obj_building` SET `locationY`=? WHERE `UID`=?"); + setFloat(1, newAltitude); + setLong(2, buildingID); + return (executeUpdate() > 0); + } + + public boolean UPDATE_PROTECTIONSTATE(final long buildingUUID, ProtectionState protectionState) { + + try { + prepareCallable("UPDATE `obj_building` SET `protectionState`=? WHERE `UID`=?"); + setString(1, protectionState.name()); + setLong(2, buildingUUID); + return (executeUpdate() > 0); + } catch (Exception e) { + Logger.error(e.toString()); + return false; + } + } + + public boolean UPDATE_DOOR_LOCK(final int buildingUUID, int doorFlags) { + + try { + prepareCallable("UPDATE obj_building SET doorState = ? WHERE UID = ?"); + + setInt(1, doorFlags); + setInt(2, buildingUUID); + + executeUpdate(); + return true; + } catch (Exception e) { + return false; + } + } + + public boolean ADD_TO_FRIENDS_LIST(final long buildingID, final long friendID, final long guildID, final int friendType) { + prepareCallable("INSERT INTO `dyn_building_friends` (`buildingUID`, `playerUID`,`guildUID`, `friendType`) VALUES (?,?,?,?)"); + setLong(1, buildingID); + setLong(2, friendID); + setLong(3, guildID); + setInt(4, friendType); + return (executeUpdate() > 0); + } + + public boolean REMOVE_FROM_FRIENDS_LIST(final long buildingID, long friendID, long guildID, int friendType) { + prepareCallable("DELETE FROM `dyn_building_friends` WHERE `buildingUID`=? AND `playerUID`=? AND `guildUID` =? AND `friendType` = ?"); + setLong(1, buildingID); + setLong(2, friendID); + setLong(3,guildID); + setInt(4, friendType); + return (executeUpdate() > 0); + } + + public boolean REMOVE_FROM_CONDEMNED_LIST(final long buildingID, long friendID, long guildID, int friendType) { + prepareCallable("DELETE FROM `dyn_building_condemned` WHERE `buildingUID`=? AND `playerUID`=? AND `guildUID` =? AND `friendType` = ?"); + setLong(1, buildingID); + setLong(2, friendID); + setLong(3,guildID); + setInt(4, friendType); + return (executeUpdate() > 0); + } + + public void CLEAR_FRIENDS_LIST(final long buildingID) { + prepareCallable("DELETE FROM `dyn_building_friends` WHERE `buildingUID`=?"); + setLong(1, buildingID); + executeUpdate(); + } + + public void CLEAR_CONDEMNED_LIST(final long buildingID) { + prepareCallable("DELETE FROM `dyn_building_condemned` WHERE `buildingUID`=?"); + setLong(1, buildingID); + executeUpdate(); + } + + public boolean CLEAR_PATROL(final long buildingID) { + prepareCallable("DELETE FROM `dyn_building_patrol_points` WHERE `buildingUID`=?"); + setLong(1, buildingID); + return (executeUpdate() > 0); + } + + public void LOAD_ALL_FRIENDS_FOR_BUILDING(Building building) { + + if (building == null) + return; + + prepareCallable("SELECT * FROM `dyn_building_friends` WHERE `buildingUID` = ?"); + setInt(1,building.getObjectUUID()); + + try { + ResultSet rs = executeQuery(); + + //shrines cached in rs for easy cache on creation. + while (rs.next()) { + BuildingFriends friend = new BuildingFriends(rs); + switch(friend.getFriendType()){ + case 7: + building.getFriends().put(friend.getPlayerUID(), friend); + break; + case 8: + case 9: + building.getFriends().put(friend.getGuildUID(), friend); + break; + } + } + + } catch (SQLException e) { + Logger.error("LOAD friends for building: " + e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + + } + + public void LOAD_ALL_CONDEMNED_FOR_BUILDING(Building building) { + + if (building == null) + return; + + prepareCallable("SELECT * FROM `dyn_building_condemned` WHERE `buildingUID` = ?"); + setInt(1,building.getObjectUUID()); + + try { + ResultSet rs = executeQuery(); + + //shrines cached in rs for easy cache on creation. + while (rs.next()) { + Condemned condemn = new Condemned(rs); + switch(condemn.getFriendType()){ + case 2: + building.getCondemned().put(condemn.getPlayerUID(), condemn); + break; + case 4: + case 5: + building.getCondemned().put(condemn.getGuildUID(), condemn); + break; + } + } + + } catch (SQLException e) { + Logger.error("LOAD Condemned for building: " + e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + + } + + public ArrayList LOAD_PATROL_POINTS(Building building) { + if (building == null) + return null; + ArrayList patrolPoints = new ArrayList<>(); + + + prepareCallable("SELECT * FROM `dyn_building_patrol_points` WHERE `buildingUID` = ?"); + setInt(1,building.getObjectUUID()); + + try { + ResultSet rs = executeQuery(); + + //shrines cached in rs for easy cache on creation. + while (rs.next()) { + float x1 = rs.getFloat("patrolX"); + float y1 = rs.getFloat("patrolY"); + float z1 = rs.getFloat("patrolZ"); + Vector3fImmutable patrolPoint = new Vector3fImmutable(x1,y1,z1); + patrolPoints.add(patrolPoint); + } + + } catch (SQLException e) { + Logger.error("LOAD Patrol Points for building: " + e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + + return patrolPoints; + + } + + public boolean ADD_TO_CONDEMNLIST(final long parentUID, final long playerUID, final long guildID, final int friendType) { + prepareCallable("INSERT INTO `dyn_building_condemned` (`buildingUID`, `playerUID`,`guildUID`, `friendType`) VALUES (?,?,?,?)"); + setLong(1, parentUID); + setLong(2, playerUID); + setLong(3, guildID); + setInt(4, friendType); + return (executeUpdate() > 0); + } + + public boolean ADD_TO_PATROL(final long parentUID, final Vector3fImmutable patrolPoint) { + prepareCallable("INSERT INTO `dyn_building_patrol_points` (`buildingUID`, `patrolX`,`patrolY`, `patrolZ`) VALUES (?,?,?,?)"); + setLong(1, parentUID); + setFloat(2, (int)patrolPoint.x); + setFloat(3, (int)patrolPoint.y); + setFloat(4, (int)patrolPoint.z); + return (executeUpdate() > 0); + } + + public boolean ADD_TO_COLLIDE(final long parentUID, final float startX, final float startY, final float endX, final float endY) { + prepareCallable("INSERT INTO `static_building_colliders` (`MeshID`, `startX`,`startY`, `endX`, `endY`) VALUES (?,?,?,?,?)"); + setLong(1, parentUID); + setFloat(2, startX); + setFloat(3, startY); + setFloat(4, endX); + setFloat(5, endY); + return (executeUpdate() > 0); + } + + public ArrayList GET_COLLIDERS(final long meshID) { + ArrayList colliders = new ArrayList<>(); + prepareCallable("SELECT * FROM `static_building_colliders` WHERE `MeshID`=? AND `doorID` =0"); + setLong(1, meshID); + try { + ResultSet rs = executeQuery(); + while (rs.next()) { + int startX = rs.getInt("startX"); + int startY = rs.getInt("startY"); + int endX = rs.getInt("endX"); + int endY = rs.getInt("endY"); + colliders.add(new Line2D.Float(startX,startY,endX,endY)); + } + + rs.close(); + } catch (SQLException e) { + Logger.error("dbBuildingHandler.GET_COLLIDERS", e); + } finally { + closeCallable(); + } + return colliders; + } + + public ArrayList GET_DOOR_COLLIDERS(final long meshID) { + ArrayList colliders = new ArrayList<>(); + prepareCallable("SELECT * FROM `static_building_colliders` WHERE `MeshID`=? AND `doorID` <> 0"); + setLong(1, meshID); + try { + ResultSet rs = executeQuery(); + while (rs.next()) { + int startX = rs.getInt("startX"); + int startY = rs.getInt("startY"); + int endX = rs.getInt("endX"); + int endY = rs.getInt("endY"); + colliders.add(new Line2D.Float(startX,startY,endX,endY)); + } + + rs.close(); + } catch (SQLException e) { + Logger.error("dbBuildingHandler.GET_COLLIDERS", e); + } finally { + closeCallable(); + } + return colliders; + } + public HashMap> LOAD_BUILDING_REGIONS() { + + HashMap> regions; + BuildingRegions thisRegions; + + + regions = new HashMap<>(); + int recordsRead = 0; + + prepareCallable("SELECT * FROM static_building_regions"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + recordsRead++; + thisRegions = new BuildingRegions(rs); + if (regions.get(thisRegions.getBuildingID()) == null){ + ArrayList regionsList = new ArrayList<>(); + regionsList.add(thisRegions); + regions.put(thisRegions.getBuildingID(), regionsList); + } + else{ + ArrayListregionsList = regions.get(thisRegions.getBuildingID()); + regionsList.add(thisRegions); + regions.put(thisRegions.getBuildingID(), regionsList); + } + } + + Logger.info( "read: " + recordsRead + " cached: " + regions.size()); + + } catch (SQLException e) { + Logger.error(": " + e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + return regions; + } + + public HashMap LOAD_MESH_BOUNDS() { + + HashMap meshBoundsMap; + MeshBounds meshBounds; + + meshBoundsMap = new HashMap<>(); + int recordsRead = 0; + + prepareCallable("SELECT * FROM static_mesh_bounds"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + recordsRead++; + meshBounds = new MeshBounds(rs); + + meshBoundsMap.put(meshBounds.meshID, meshBounds); + + } + + Logger.info( "read: " + recordsRead + " cached: " + meshBoundsMap.size()); + + } catch (SQLException e) { + Logger.error("LoadMeshBounds: " + e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + return meshBoundsMap; + } + + public HashMap> LOAD_ALL_STATIC_COLLIDERS() { + + HashMap> colliders; + StaticColliders thisColliders; + + + colliders = new HashMap<>(); + int recordsRead = 0; + + prepareCallable("SELECT * FROM static_building_colliders"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + recordsRead++; + thisColliders = new StaticColliders(rs); + if (colliders.get(thisColliders.getMeshID()) == null){ + ArrayList colliderList = new ArrayList<>(); + colliderList.add(thisColliders); + colliders.put(thisColliders.getMeshID(), colliderList); + } + else{ + ArrayListcolliderList = colliders.get(thisColliders.getMeshID()); + colliderList.add(thisColliders); + colliders.put(thisColliders.getMeshID(), colliderList); + } + + + } + + Logger.info( "read: " + recordsRead + " cached: " + colliders.size()); + + } catch (SQLException e) { + Logger.error("LoadAllBlueprints: " + e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + return colliders; + } + + // This public class inserted here as it's a generic utility function + // with no other good place for it. If you find a good home for it + // feel free to move it. - + + public final DbObjectType GET_UID_ENUM(long object_UID) { + + DbObjectType storedEnum = DbObjectType.INVALID; + String typeString; + + if (object_UID == 0) + return storedEnum; + + // Set up call to stored procedure + prepareCallable("CALL object_UID_ENUM(?)"); + setLong(1, object_UID); + + try { + + // Evaluate database ordinal and return enum + storedEnum = DbObjectType.valueOf(getString("type").toUpperCase()); + + } catch (Exception e) { + storedEnum = DbObjectType.INVALID; + Logger.error("UID_ENUM ", "Orphaned Object? Lookup failed for UID: " + object_UID); + } finally { + closeCallable(); + } + return storedEnum; + } + + public ConcurrentHashMap GET_FRIENDS(final long buildingID) { + ConcurrentHashMap friendsList = new ConcurrentHashMap<>(); + prepareCallable("SELECT * FROM `dyn_building_friends` WHERE `buildingUID`=?"); + setLong(1, buildingID); + try { + ResultSet rs = executeQuery(); + while (rs.next()) { + int friendType = rs.getInt("friendType"); + switch (friendType) { + case 7: + friendsList.put(rs.getInt("playerUID"), 7); + break; + case 8: + friendsList.put(rs.getInt("guildUID"), 8); + break; + case 9: + friendsList.put(rs.getInt("guildUID"), 9); + } + } + + rs.close(); + } catch (SQLException e) { + Logger.error("dbBuildingHandler.GET_FRIENDS_GUILD_IC", e); + } finally { + closeCallable(); + } + return friendsList; + } + + public boolean updateBuildingRank(final Building b, int Rank) { + + prepareCallable("UPDATE `obj_building` SET `rank`=?," + + "`upgradeDate`=?, `meshUUID`=?, `currentHP`=? " + + "WHERE `UID` = ?"); + + setInt(1, Rank); + setNULL(2, java.sql.Types.DATE); + setInt(3, b.getBlueprint().getMeshForRank(Rank)); + setInt(4, b.getBlueprint().getMaxHealth(Rank)); + setInt(5, b.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean updateReverseKOS(final Building b, boolean reverse) { + + prepareCallable("UPDATE `obj_building` SET `reverseKOS`=? " + + "WHERE `UID` = ?"); + setBoolean(1, reverse); + setInt(2, b.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean updateActiveCondemn(final Condemned condemn, boolean active) { + + prepareCallable("UPDATE `dyn_building_condemned` SET `active`=? " + + "WHERE`buildingUID` = ? AND `playerUID` = ? AND `guildUID` = ? AND `friendType` = ?"); + setBoolean(1, active); + setInt(2, condemn.getParent()); + setInt(3, condemn.getPlayerUID()); + setInt(4, condemn.getGuildUID()); + setInt(5, condemn.getFriendType()); + return (executeUpdate() > 0); + } + + public boolean updateBuildingOwner(final Building building, int ownerUUID) { + + prepareCallable("UPDATE `obj_building` SET `ownerUUID`=? " + + " WHERE `UID` = ?"); + + setInt(1, ownerUUID); + setInt(2, building.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean updateBuildingUpgradeTime(LocalDateTime upgradeDateTime, Building toUpgrade, int costToUpgrade) { + + prepareCallable("UPDATE obj_building SET upgradeDate=?, currentGold=? " + + "WHERE UID = ?"); + + if (upgradeDateTime == null) + setNULL(1, java.sql.Types.DATE); + else + setTimeStamp(1, upgradeDateTime.atZone(ZoneId.systemDefault()) + .toInstant().toEpochMilli()); + + setInt(2, toUpgrade.getStrongboxValue() - costToUpgrade); + setInt(3, toUpgrade.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean updateMaintDate(Building building) { + + prepareCallable("UPDATE obj_building SET maintDate=? " + + "WHERE UID = ?"); + + if (building.maintDateTime == null) + setNULL(1, java.sql.Types.DATE); + else + setLocalDateTime(1, building.maintDateTime); + + setInt(2, building.getObjectUUID()); + + return (executeUpdate() > 0); + } + + public boolean addTaxes(Building building, TaxType taxType, int amount, boolean enforceKOS){ + prepareCallable("UPDATE obj_building SET taxType=?, taxAmount = ?, enforceKOS = ? " + + "WHERE UID = ?"); + + setString(1, taxType.name()); + setInt(2, amount); + setBoolean(3, enforceKOS); + setInt(4, building.getObjectUUID()); + + return (executeUpdate() > 0); + + } + + public boolean removeTaxes(Building building){ + prepareCallable("UPDATE obj_building SET taxType=?, taxAmount = ?, enforceKOS = ?, taxDate = ? " + + "WHERE UID = ?"); + + setString(1, TaxType.NONE.name()); + setInt(2, 0); + setBoolean(3, false); + setNULL(4, java.sql.Types.DATE); + setInt(5, building.getObjectUUID()); + + + + return (executeUpdate() > 0); + + } + + public boolean acceptTaxes(Building building) { + + prepareCallable("UPDATE obj_building SET taxDate=? " + + "WHERE UID = ?"); + + setTimeStamp(1, DateTime.now().plusDays(7).getMillis()); + setInt(2, building.getObjectUUID()); + + return (executeUpdate() > 0); + } + + + +} diff --git a/src/engine/db/handlers/dbBuildingLocationHandler.java b/src/engine/db/handlers/dbBuildingLocationHandler.java new file mode 100644 index 00000000..c9dadd64 --- /dev/null +++ b/src/engine/db/handlers/dbBuildingLocationHandler.java @@ -0,0 +1,27 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.objects.BuildingLocation; + +import java.util.ArrayList; + +public class dbBuildingLocationHandler extends dbHandlerBase { + + public dbBuildingLocationHandler() { + this.localClass = BuildingLocation.class; + this.localObjectType = engine.Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public ArrayList LOAD_ALL_BUILDING_LOCATIONS() { + prepareCallable("SELECT * FROM `static_building_location`;"); + return getObjectList(); + } +} diff --git a/src/engine/db/handlers/dbCSSessionHandler.java b/src/engine/db/handlers/dbCSSessionHandler.java new file mode 100644 index 00000000..d76aa810 --- /dev/null +++ b/src/engine/db/handlers/dbCSSessionHandler.java @@ -0,0 +1,100 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.gameManager.DbManager; +import engine.objects.Account; +import engine.objects.PlayerCharacter; +import engine.session.CSSession; +import engine.util.StringUtils; +import org.pmw.tinylog.Logger; + +import java.net.InetAddress; +import java.sql.ResultSet; +import java.sql.SQLException; + +public class dbCSSessionHandler extends dbHandlerBase { + + public dbCSSessionHandler() { + this.localClass = CSSession.class; + this.localObjectType = engine.Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public boolean ADD_CSSESSION(String secKey, Account acc, InetAddress inet, String machineID) { + prepareCallable("INSERT INTO `dyn_session` (`secretKey`, `accountID`, `discordAccount`, `sessionIP`, machineID) VALUES (?,?,?,INET_ATON(?),?)"); + setString(1, secKey); + setLong(2, acc.getObjectUUID()); + setString(3, acc.discordAccount); + setString(4, StringUtils.InetAddressToClientString(inet)); + setString(5, machineID); + return (executeUpdate() != 0); + } + // This method returns population metrics from the database + + public String GET_POPULATION_STRING() { + + String outString = null; + + // Set up call to stored procedure + prepareCallable("CALL GET_POPULATION_STRING()"); + + try { + + // Evaluate database ordinal and return enum + outString = getString("popstring"); + + } catch (Exception e) { + Logger.error( "Failure in stored procedure:" + e.getMessage()); + } finally { + closeCallable(); + } + return outString; + } + + public boolean DELETE_UNUSED_CSSESSION(String secKey) { + prepareCallable("DELETE FROM `dyn_session` WHERE `secretKey`=? && `characterID` IS NULL"); + setString(1, secKey); + return (executeUpdate() != 0); + } + + public boolean DELETE_CSSESSION(String secKey) { + prepareCallable("DELETE FROM `dyn_session` WHERE `secretKey`=?"); + setString(1, secKey); + return (executeUpdate() != 0); + } + + public boolean UPDATE_CSSESSION(String secKey, int charID) { + prepareCallable("UPDATE `dyn_session` SET `characterID`=? WHERE `secretKey`=?"); + setInt(1, charID); + setString(2, secKey); + return (executeUpdate() != 0); + } + + public CSSession GET_CSSESSION(String secKey) { + CSSession css = null; + prepareCallable("SELECT `accountID`, `characterID`, `machineID` FROM `dyn_session` WHERE `secretKey`=?"); + setString(1, secKey); + try { + + ResultSet rs = executeQuery(); + + if (rs.next()) { + css = new CSSession(secKey, DbManager.AccountQueries.GET_ACCOUNT(rs.getInt("accountID")), PlayerCharacter.getPlayerCharacter(rs + .getInt("characterID")), getString("machineID")); + } + rs.close(); + } catch (SQLException e) { + Logger.error("Error with seckey: " + secKey); + } finally { + closeCallable(); + } + return css; + } +} diff --git a/src/engine/db/handlers/dbCharacterPowerHandler.java b/src/engine/db/handlers/dbCharacterPowerHandler.java new file mode 100644 index 00000000..31a64f8a --- /dev/null +++ b/src/engine/db/handlers/dbCharacterPowerHandler.java @@ -0,0 +1,113 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.Enum; +import engine.gameManager.DbManager; +import engine.objects.CharacterPower; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.concurrent.ConcurrentHashMap; + +public class dbCharacterPowerHandler extends dbHandlerBase { + + public dbCharacterPowerHandler() { + this.localClass = CharacterPower.class; + this.localObjectType = Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public CharacterPower ADD_CHARACTER_POWER(CharacterPower toAdd) { + if (CharacterPower.getOwner(toAdd) == null || toAdd.getPower() == null) { + Logger.error("dbCharacterSkillHandler.ADD_Power", toAdd.getObjectUUID() + " missing owner or powersBase"); + return null; + } + + prepareCallable("INSERT INTO `dyn_character_power` (`CharacterID`, `powersBaseToken`, `trains`) VALUES (?, ?, ?);"); + setLong(1, (long)CharacterPower.getOwner(toAdd).getObjectUUID()); + setInt(2, toAdd.getPower().getToken()); + setInt(3, toAdd.getTrains()); + int powerID = insertGetUUID(); + return GET_CHARACTER_POWER(powerID); + + } + + public int DELETE_CHARACTER_POWER(final int objectUUID) { + prepareCallable("DELETE FROM `dyn_character_power` WHERE `UID` = ?"); + setLong(1, (long)objectUUID); + return executeUpdate(); + } + + public CharacterPower GET_CHARACTER_POWER(int objectUUID) { + + CharacterPower cp = (CharacterPower) DbManager.getFromCache(Enum.GameObjectType.CharacterPower, objectUUID); + if (cp != null) + return cp; + prepareCallable("SELECT * FROM `dyn_character_power` WHERE `UID` = ?"); + setLong(1, (long)objectUUID); + return (CharacterPower) getObjectSingle(objectUUID); + } + + public ConcurrentHashMap GET_POWERS_FOR_CHARACTER(PlayerCharacter pc) { + ConcurrentHashMap powers = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + int objectUUID = pc.getObjectUUID(); + prepareCallable("SELECT * FROM `dyn_character_power` WHERE CharacterID = ?"); + setLong(1, (long)objectUUID); + ResultSet rs = executeQuery(); + try { + while (rs.next()) { + CharacterPower cp = new CharacterPower(rs, pc); + if (cp.getPower() != null) + powers.put(cp.getPower().getToken(), cp); + } + rs.close(); + } catch (SQLException e) { + Logger.error("CharacterPower.getCharacterPowerForCharacter", "Exception:" + e.getMessage()); + } finally { + closeCallable(); + } + return powers; + } + + public void UPDATE_TRAINS(final CharacterPower pow) { + //skip update if nothing changed + if (!pow.isTrained()) + return; + + prepareCallable("UPDATE `dyn_character_power` SET `trains`=? WHERE `UID`=?"); + setShort(1, (short)pow.getTrains()); + setInt(2, pow.getObjectUUID()); + executeUpdate(); + pow.setTrained(false); + } + + public void updateDatabase(final CharacterPower pow) { + if (pow.getPower() == null) { + Logger.error( "Failed to find powersBase for Power " + pow.getObjectUUID()); + return; + } + if (CharacterPower.getOwner(pow) == null) { + Logger.error( "Failed to find owner for Power " + pow.getObjectUUID()); + return; + } + + + prepareCallable("UPDATE `dyn_character_power` SET `PowersBaseToken`=?, `CharacterID`=?, `trains`=? WHERE `UID`=?"); + setInt(1, pow.getPower().getToken()); + setInt(2, CharacterPower.getOwner(pow).getObjectUUID()); + setShort(3, (short)pow.getTrains()); + setInt(4, pow.getObjectUUID()); + executeUpdate(); + pow.setTrained(false); + } +} diff --git a/src/engine/db/handlers/dbCharacterRuneHandler.java b/src/engine/db/handlers/dbCharacterRuneHandler.java new file mode 100644 index 00000000..868774f8 --- /dev/null +++ b/src/engine/db/handlers/dbCharacterRuneHandler.java @@ -0,0 +1,63 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.Enum; +import engine.gameManager.DbManager; +import engine.objects.CharacterRune; + +import java.util.ArrayList; + +public class dbCharacterRuneHandler extends dbHandlerBase { + + public dbCharacterRuneHandler() { + this.localClass = CharacterRune.class; + this.localObjectType = Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public CharacterRune ADD_CHARACTER_RUNE(final CharacterRune toAdd) { + prepareCallable("INSERT INTO `dyn_character_rune` (`CharacterID`, `RuneBaseID`) VALUES (?, ?);"); + setLong(1, (long)toAdd.getPlayerID()); + setInt(2, toAdd.getRuneBaseID()); + int runeID = insertGetUUID(); + return GET_CHARACTER_RUNE(runeID); + } + + public CharacterRune GET_CHARACTER_RUNE(int runeID) { + + CharacterRune charRune = (CharacterRune) DbManager.getFromCache(Enum.GameObjectType.CharacterRune, runeID); + if (charRune != null) + return charRune; + prepareCallable("SELECT * FROM `dyn_character_rune` WHERE `UID`=?"); + setInt(1, runeID); + return (CharacterRune) getObjectSingle(runeID); + } + + + public boolean DELETE_CHARACTER_RUNE(final CharacterRune cr) { + prepareCallable("DELETE FROM `dyn_character_rune` WHERE `UID`=?;"); + setLong(1, (long)cr.getObjectUUID()); + return (executeUpdate() != 0); + } + + public ArrayList GET_RUNES_FOR_CHARACTER(final int characterId) { + prepareCallable("SELECT * FROM `dyn_character_rune` WHERE `CharacterID` = ?"); + setInt(1, characterId); + return getObjectList(); + } + + public void updateDatabase(final CharacterRune cr) { + prepareCallable("UPDATE `dyn_character_rune` SET `CharacterID`=?, `RuneBaseID`=? WHERE `UID` = ?"); + setInt(1, cr.getPlayerID()); + setInt(2, cr.getRuneBaseID()); + setLong(3, (long) cr.getObjectUUID()); + executeUpdate(); + } +} diff --git a/src/engine/db/handlers/dbCharacterSkillHandler.java b/src/engine/db/handlers/dbCharacterSkillHandler.java new file mode 100644 index 00000000..e4a93779 --- /dev/null +++ b/src/engine/db/handlers/dbCharacterSkillHandler.java @@ -0,0 +1,116 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.Enum; +import engine.gameManager.DbManager; +import engine.objects.AbstractCharacter; +import engine.objects.CharacterSkill; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.concurrent.ConcurrentHashMap; + +public class dbCharacterSkillHandler extends dbHandlerBase { + + public dbCharacterSkillHandler() { + this.localClass = CharacterSkill.class; + this.localObjectType = Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public CharacterSkill ADD_SKILL(CharacterSkill toAdd) { + if (CharacterSkill.GetOwner(toAdd) == null || toAdd.getSkillsBase() == null) { + Logger.error("dbCharacterSkillHandler.ADD_SKILL", toAdd.getObjectUUID() + " missing owner or skillsBase"); + return null; + } + + prepareCallable("INSERT INTO `dyn_character_skill` (`CharacterID`, `skillsBaseID`, `trains`) VALUES (?, ?, ?);"); + setLong(1, (long)CharacterSkill.GetOwner(toAdd).getObjectUUID()); + setInt(2, toAdd.getSkillsBase().getObjectUUID()); + setInt(3, toAdd.getNumTrains()); + int skillID = insertGetUUID(); + return GET_SKILL(skillID); + } + + public boolean DELETE_SKILL(final int objectUUID) { + prepareCallable("DELETE FROM `dyn_character_skill` WHERE `UID` = ?"); + setLong(1, (long)objectUUID); + return (executeUpdate() != 0); + } + + public CharacterSkill GET_SKILL(final int objectUUID) { + CharacterSkill skill = (CharacterSkill) DbManager.getFromCache(Enum.GameObjectType.CharacterSkill, objectUUID); + if (skill != null) + return skill; + prepareCallable("SELECT * FROM `dyn_character_skill` WHERE `UID` = ?"); + setInt(1, objectUUID); + return (CharacterSkill) getObjectSingle(objectUUID); + } + + public ConcurrentHashMap GET_SKILLS_FOR_CHARACTER(final AbstractCharacter ac) { + ConcurrentHashMap skills = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + if (ac == null || (!(ac.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)))) + return skills; + PlayerCharacter pc = (PlayerCharacter) ac; + int objectUUID = pc.getObjectUUID(); + + prepareCallable("SELECT * FROM `dyn_character_skill` WHERE `CharacterID` = ?"); + setInt(1, objectUUID); + try { + ResultSet rs = executeQuery(); + while (rs.next()) { + CharacterSkill cs = new CharacterSkill(rs, pc); + if (cs.getSkillsBase() != null) + skills.put(cs.getSkillsBase().getName(), cs); + } + rs.close(); + } catch (SQLException e) { + Logger.error("CharacterSkill.getCharacterSkillForCharacter", e); + } finally { + closeCallable(); + } + return skills; + } + + + public void UPDATE_TRAINS(final CharacterSkill cs) { + if (!cs.isTrained()) + return; + + prepareCallable("UPDATE `dyn_character_skill` SET `trains`=? WHERE `UID` = ?"); + setShort(1, (short)cs.getNumTrains()); + setLong(2, (long)cs.getObjectUUID()); + if (executeUpdate() != 0) + cs.syncTrains(); + } + + public void updateDatabase(final CharacterSkill cs) { + if (cs.getSkillsBase() == null) { + Logger.error("Failed to find skillsBase for Skill " + cs.getObjectUUID()); + return; + } + if (CharacterSkill.GetOwner(cs) == null) { + Logger.error("Failed to find owner for Skill " + cs.getObjectUUID()); + return; + } + + prepareCallable("UPDATE `dyn_character_skill` SET `skillsBaseID`=?, `CharacterID`=?, `trains`=? WHERE `UID`=?"); + setInt(1, cs.getSkillsBase().getObjectUUID()); + setInt(2, CharacterSkill.GetOwner(cs).getObjectUUID()); + setShort(3, (short)cs.getNumTrains()); + setLong(4, (long)cs.getObjectUUID()); + if (executeUpdate() != 0) + cs.syncTrains(); + } + +} diff --git a/src/engine/db/handlers/dbCityHandler.java b/src/engine/db/handlers/dbCityHandler.java new file mode 100644 index 00000000..13101ffe --- /dev/null +++ b/src/engine/db/handlers/dbCityHandler.java @@ -0,0 +1,196 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.Enum; +import engine.gameManager.DbManager; +import engine.objects.AbstractGameObject; +import engine.objects.Building; +import engine.objects.City; +import engine.objects.Zone; +import org.pmw.tinylog.Logger; + +import java.net.UnknownHostException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDateTime; +import java.util.ArrayList; + +public class dbCityHandler extends dbHandlerBase { + + public dbCityHandler() { + this.localClass = City.class; + this.localObjectType = Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public ArrayList CREATE_CITY(int ownerID, int parentZoneID, int realmID, float xCoord, float yCoord, float zCoord, float rotation, float W, String name, LocalDateTime established) { + prepareCallable("CALL `city_CREATE`(?, ?, ?, ?, ?, ?, ?, ?, ?,?,?)"); + LocalDateTime upgradeTime = LocalDateTime.now().plusHours(2); + setLong(1, (long) ownerID); //objectUUID of owning player + setLong(2, (long) parentZoneID); //objectUUID of parent (continent) zone + setLong(3, (long) realmID); //objectUUID of realm city belongs in + setFloat(4, xCoord); //xOffset from parentZone center + setFloat(5, yCoord); //yOffset from parentZone center + setFloat(6, zCoord); //zOffset from parentZone center + setString(7, name); //city name + setLocalDateTime(8, established); + setFloat(9, rotation); + setFloat(10, W); + setLocalDateTime(11, upgradeTime); + ArrayList list = new ArrayList<>(); + + try { + boolean work = execute(); + if (work) { + ResultSet rs = this.cs.get().getResultSet(); + while (rs.next()) { + addObject(list, rs); + } + rs.close(); + } else { + Logger.info("City Placement Failed: " + this.cs.get().toString()); + return list; //city creation failure + } + while (this.cs.get().getMoreResults()) { + ResultSet rs = this.cs.get().getResultSet(); + while (rs.next()) { + addObject(list, rs); + } + rs.close(); + } + } catch (SQLException e) { + Logger.info("City Placement Failed, SQLException: " + this.cs.get().toString() + e.toString()); + return list; //city creation failure + } catch (UnknownHostException e) { + Logger.info("City Placement Failed, UnknownHostException: " + this.cs.get().toString()); + return list; //city creation failure + } finally { + closeCallable(); + } + return list; + } + + public static void addObject(ArrayList list, ResultSet rs) throws SQLException, UnknownHostException { + String type = rs.getString("type"); + switch (type) { + case "zone": + Zone zone = new Zone(rs); + DbManager.addToCache(zone); + list.add(zone); + break; + case "building": + Building building = new Building(rs); + DbManager.addToCache(building); + list.add(building); + break; + case "city": + City city = new City(rs); + DbManager.addToCache(city); + list.add(city); + break; + } + } + + public ArrayList GET_CITIES_BY_ZONE(final int objectUUID) { + prepareCallable("SELECT `obj_city`.*, `object`.`parent` FROM `obj_city` INNER JOIN `object` ON `object`.`UID` = `obj_city`.`UID` WHERE `object`.`parent`=?;"); + setLong(1, (long) objectUUID); + + return getObjectList(); + } + + public City GET_CITY(final int cityId) { + City city = (City) DbManager.getFromCache(Enum.GameObjectType.City, cityId); + if (city != null) + return city; + prepareCallable("SELECT `obj_city`.*, `object`.`parent` FROM `obj_city` INNER JOIN `object` ON `object`.`UID` = `obj_city`.`UID` WHERE `object`.`UID`=?;"); + setLong(1, (long) cityId); + city = (City) getObjectSingle(cityId); + return city; + } + + public String SET_PROPERTY(final City c, String name, Object new_value) { + prepareCallable("CALL city_SETPROP(?,?,?)"); + setLong(1, (long) c.getObjectUUID()); + setString(2, name); + setString(3, String.valueOf(new_value)); + return getResult(); + } + + public String SET_PROPERTY(final City c, String name, Object new_value, Object old_value) { + prepareCallable("CALL city_GETSETPROP(?,?,?,?)"); + setLong(1, (long) c.getObjectUUID()); + setString(2, name); + setString(3, String.valueOf(new_value)); + setString(4, String.valueOf(old_value)); + return getResult(); + } + + public boolean updateforceRename(City city, boolean value) { + + prepareCallable("UPDATE `obj_city` SET `forceRename`=?" + + " WHERE `UID` = ?"); + setByte(1, (value == true) ? (byte) 1 : (byte) 0); + setInt(2, city.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean updateOpenCity(City city, boolean value) { + + prepareCallable("UPDATE `obj_city` SET `open`=?" + + " WHERE `UID` = ?"); + setByte(1, (value == true) ? (byte) 1 : (byte) 0); + setInt(2, city.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean updateTOL(City city, int tolID) { + + prepareCallable("UPDATE `obj_city` SET `treeOfLifeUUID`=?" + + " WHERE `UID` = ?"); + setInt(1,tolID); + setInt(2, city.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean renameCity(City city, String name) { + + prepareCallable("UPDATE `obj_city` SET `name`=?" + + " WHERE `UID` = ?"); + setString(1, name); + setInt(2, city.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean updateSiegesWithstood(City city, int value) { + + prepareCallable("UPDATE `obj_city` SET `siegesWithstood`=?" + + " WHERE `UID` = ?"); + setInt(1, value); + setInt(2, city.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean updateRealmTaxDate(City city, LocalDateTime localDateTime) { + + prepareCallable("UPDATE `obj_city` SET `realmTaxDate` =?" + + " WHERE `UID` = ?"); + setLocalDateTime(1, localDateTime); + setInt(2,city.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean DELETE_CITY(final City city) { + + prepareCallable("DELETE FROM `object` WHERE `UID` = ? AND `type` = 'city'"); + setInt(1, city.getObjectUUID()); + return (executeUpdate() != 0); + } + +} diff --git a/src/engine/db/handlers/dbContractHandler.java b/src/engine/db/handlers/dbContractHandler.java new file mode 100644 index 00000000..f8b41433 --- /dev/null +++ b/src/engine/db/handlers/dbContractHandler.java @@ -0,0 +1,157 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.Enum; +import engine.gameManager.DbManager; +import engine.objects.Contract; +import engine.objects.ItemBase; +import engine.objects.MobEquipment; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; + +public class dbContractHandler extends dbHandlerBase { + + public dbContractHandler() { + this.localClass = Contract.class; + this.localObjectType = Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public Contract GET_CONTRACT(final int objectUUID) { + Contract contract = (Contract) DbManager.getFromCache(Enum.GameObjectType.Contract, objectUUID); + if (contract != null) + return contract; + if (objectUUID == 0) + return null; + prepareCallable("SELECT * FROM `static_npc_contract` WHERE `ID` = ?"); + setInt(1, objectUUID); + return (Contract) getObjectSingle(objectUUID); + } + + public ArrayList GET_CONTRACT_BY_RACE(final int objectUUID) { + + ArrayList contracts = new ArrayList<>(); + + prepareCallable("SELECT * FROM static_npc_contract WHERE `mobbaseID` =?;"); + setLong(1, objectUUID); + + try { + ResultSet rs = executeQuery(); + + //shrines cached in rs for easy cache on creation. + while (rs.next()) { + Contract contract = new Contract(rs); + if (contract != null) + contracts.add(contract); + } + + } catch (SQLException e) { + Logger.error( e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + return contracts; + } + + public void GET_GENERIC_INVENTORY(final Contract contract) { + + prepareCallable("SELECT * FROM `static_npc_inventoryset` WHERE `inventorySet` = ?;"); + setInt(1, contract.inventorySet); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + //handle item base + int itemBaseID = rs.getInt("itembaseID"); + + ItemBase ib = ItemBase.getItemBase(itemBaseID); + + if (ib != null) { + + MobEquipment me = new MobEquipment(ib, 0, 0); + contract.getSellInventory().add(me); + + //handle magic effects + String prefix = rs.getString("prefix"); + int pRank = rs.getInt("pRank"); + String suffix = rs.getString("suffix"); + int sRank = rs.getInt("sRank"); + + if (prefix != null) { + me.setPrefix(prefix, pRank); + me.setIsID(true); + } + + if (suffix != null) { + me.setSuffix(suffix, sRank); + me.setIsID(true); + } + + } + } + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode() + ' ' + e.getMessage()); + } finally { + closeCallable(); + } + } + + public void GET_SELL_LISTS(final Contract con) { + prepareCallable("SELECT * FROM `static_npc_contract_selltype` WHERE `contractID` = ?;"); + setInt(1, con.getObjectUUID()); + try { + ResultSet rs = executeQuery(); + ArrayList item = con.getBuyItemType(); + ArrayList skill = con.getBuySkillToken(); + ArrayList unknown = con.getBuyUnknownToken(); + while (rs.next()) { + int type = rs.getInt("type"); + int value = rs.getInt("value"); + if (type == 1) { + item.add(value); + } else if (type == 2) { + skill.add(value); + } else if (type == 3) { + unknown.add(value); + } + } + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode() + ' ' + e.getMessage()); + } finally { + closeCallable(); + } + } + + public boolean updateAllowedBuildings(final Contract con, final long slotbitvalue) { + prepareCallable("UPDATE `static_npc_contract` SET `allowedBuildingTypeID`=? WHERE `contractID`=?"); + setLong(1, slotbitvalue); + setInt(2, con.getContractID()); + return (executeUpdate() > 0); + } + + public boolean updateDatabase(final Contract con) { + prepareCallable("UPDATE `static_npc_contract` SET `contractID`=?, `name`=?, " + + "`mobbaseID`=?, `classID`=?, vendorDialog=?, iconID=?, allowedBuildingTypeID=? WHERE `ID`=?"); + setInt(1, con.getContractID()); + setString(2, con.getName()); + setInt(3, con.getMobbaseID()); + setInt(4, con.getClassID()); + setInt(5, (con.getVendorDialog() != null) ? con.getVendorDialog().getObjectUUID() : 0); + setInt(6, con.getIconID()); + setInt(8, con.getObjectUUID()); + setLong(7, con.getAllowedBuildings().toLong()); + return (executeUpdate() > 0); + } +} diff --git a/src/engine/db/handlers/dbEffectsBaseHandler.java b/src/engine/db/handlers/dbEffectsBaseHandler.java new file mode 100644 index 00000000..9e360278 --- /dev/null +++ b/src/engine/db/handlers/dbEffectsBaseHandler.java @@ -0,0 +1,181 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +public class dbEffectsBaseHandler extends dbHandlerBase { + + public dbEffectsBaseHandler() { + + } + + + + public boolean CreateEffectBase(int token, String IDString,String name,int flags){ + prepareCallable("INSERT INTO `wpak_static_power_effectbase` (`token`,`IDString`,`name`,`flags`) VALUES (?,?,?,?)"); + setInt(1,token); + setString(2,IDString); + setString(3,name); + setInt(4,flags); + + return (executeUpdate() > 0); + } + + public boolean CreateEffectBaseRAW(String IDString,String type,String detail){ + prepareCallable("INSERT INTO `wpak_effect_effectbase_raw` (`token`,`IDString`,`name`,`flags`) VALUES (?,?,?,?)"); + setString(1,IDString); + setString(2,type); + setString(3,detail); + + return (executeUpdate() > 0); + } + + public boolean CreateEffectSource(String IDString,String source){ + prepareCallable("INSERT INTO `wpak_static_power_sourcetype` (`IDString`,`source`) VALUES (?,?)"); + + setString(1,IDString); + setString(2,source); + + return (executeUpdate() > 0); + } + + public boolean CreateEffectSourceRAW(String IDString,String type,String detail){ + prepareCallable("INSERT INTO `wpak_effect_source_raw` (`effectID`,`type`, `text`) VALUES (?,?,?)"); + + setString(1,IDString); + setString(2,type); + setString(3,detail); + + return (executeUpdate() > 0); + } + + public boolean CreateEffectCondition(String IDString,String powerOrEffect,String type,float amount,float ramp,byte useAddFormula,String damageType1,String damageType2,String damageType3){ + prepareCallable("INSERT INTO `wpak_static_power_failcondition` (`IDString`,`powerOrEffect`,`type`,`amount`,`ramp`,`useAddFormula`,`damageType1`,`damageType2`,`damageType3`) VALUES (?,?,?,?,?,?,?,?,?)"); + setString(1,IDString); + setString(2,powerOrEffect); + setString(3,type); + setFloat(4,amount); + setFloat(5,ramp); + setByte(6,useAddFormula); + setString(7,damageType1); + setString(8,damageType2); + setString(9,damageType3); + + return (executeUpdate() > 0); + } + + public boolean CreateEffectConditionRAW(String IDString,String type,String detail){ + prepareCallable("INSERT INTO `wpak_effect_condition_raw` (`effectID`,`type`, `text`) VALUES (?,?,?)"); + setString(1,IDString); + setString(2,type); + setString(3,detail); + return (executeUpdate() > 0); + } + + public boolean CreateEffectMod(String IDString,String modType,float minMod,float maxMod,float percentMod,float ramp,byte useRampAdd,String type,String string1,String string2){ + prepareCallable("INSERT INTO `wpak_static_power_effectmod` (`IDString`,`modType`,`minMod`,`maxMod`,`percentMod`,`ramp`,`useRampAdd`,`type`,`string1`,`string2`) VALUES (?,?,?,?,?,?,?,?,?,?)"); + setString(1, IDString); + setString(2, modType); + setFloat(3, minMod); + setFloat(4, maxMod); + setFloat(5, percentMod); + setFloat(6, ramp); + setByte(7, useRampAdd); + setString(8, type); + setString(9, string1); + setString(10, string2); + + return (executeUpdate() > 0); + } + + public boolean CreateEffectModRAW(String IDString,String type,String detail){ + prepareCallable("INSERT INTO `wpak_effect_mod_raw` (`effectID`,`type`, `text`) VALUES (?,?,?)"); + setString(1,IDString); + setString(2,type); + setString(3,detail); + + return (executeUpdate() > 0); + } + + + public boolean CreatePowerPowerAction(String IDString,String type,String effectID,String effectID2,String deferredPowerID,float levelCap,float levelCapRamp,String damageType,int numIterations,String effectSourceToRemove,String trackFilter,int maxTrack,int mobID,int mobLevel,int simpleDamage,String transferFromType,String transferToType,float transferAmount,float transferRamp,float transferEfficiency,float transferEfficiencyRamp,int flags){ + prepareCallable("INSERT INTO `wpak_static_power_poweraction` (`IDString`,`type`,`effectID`,`effectID2`,`deferredPowerID`,`levelCap`,`levelCapRamp`,`damageType`,`numIterations`,`effectSourceToRemove`,`trackFilter`,`maxTrack`,`mobID`,`mobLevel`,`simpleDamage`,`transferFromType`,`transferToType`,`transferAmount`,`transferRamp`,`transferEfficiency`,`transferEfficiencyRamp`,`flags`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"); + + setString(1,IDString); + setString(2,type); + setString(3,effectID); + setString(4,effectID2); + setString(5,deferredPowerID); + setFloat(6,levelCap); + setFloat(7,levelCapRamp); + setString(8,damageType); + setInt(9,numIterations); + setString(10,effectSourceToRemove); + setString(11,trackFilter); + setInt(12,maxTrack); + setInt(13,mobID); + setInt(14,mobLevel); + setInt(15,simpleDamage); + setString(16,transferFromType); + setString(17,transferToType); + setFloat(18,transferAmount); + setFloat(19,transferRamp); + setFloat(20,transferEfficiency); + setFloat(21,transferEfficiencyRamp); + setInt(22,flags); + + return (executeUpdate() > 0); + } + + public boolean CreatePowerPowerActionRAW(String IDString,String type,String detail){ + prepareCallable("INSERT INTO `wpak_effect_poweraction_raw` (`effectID`,`type`, `text`) VALUES (?,?,?)"); + + setString(1,IDString); + setString(2,type); + setString(3,detail); + + return (executeUpdate() > 0); + } + + public boolean ClearAllEffectBase(){ + prepareCallable("DELETE from `wpak_static_power_effectbase`"); + executeUpdate(); + + prepareCallable(" DELETE from `wpak_static_power_sourcetype` "); + executeUpdate(); + + prepareCallable(" DELETE from `wpak_static_power_failcondition` WHERE `powerOrEffect` = ?"); + setString(1,"Effect"); + executeUpdate(); + + prepareCallable(" DELETE from `wpak_static_power_effectmod` "); + executeUpdate(); + + return true; + + } + + public boolean ResetIncrement(){ + prepareCallable("ALTER TABLE `wpak_static_power_effectbase` AUTO_INCREMENT = 1"); + executeUpdate(); + + prepareCallable("ALTER TABLE `wpak_static_power_sourcetype` AUTO_INCREMENT = 1"); + executeUpdate(); + + prepareCallable("ALTER TABLE `wpak_static_power_failcondition` AUTO_INCREMENT = 1"); + executeUpdate(); + + prepareCallable("ALTER TABLE `wpak_static_power_effectmod` AUTO_INCREMENT = 1"); + executeUpdate(); + + + return true; + } + +} diff --git a/src/engine/db/handlers/dbEffectsResourceCostHandler.java b/src/engine/db/handlers/dbEffectsResourceCostHandler.java new file mode 100644 index 00000000..cd86d0b6 --- /dev/null +++ b/src/engine/db/handlers/dbEffectsResourceCostHandler.java @@ -0,0 +1,30 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.objects.EffectsResourceCosts; + +import java.util.ArrayList; + +public class dbEffectsResourceCostHandler extends dbHandlerBase { + + public dbEffectsResourceCostHandler() { + this.localClass = EffectsResourceCosts.class; + this.localObjectType = engine.Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + + + public ArrayList GET_ALL_EFFECT_RESOURCES(String idString) { + prepareCallable("SELECT * FROM `static_power_effectcost` WHERE `IDString` = ?"); + setString(1, idString); + return getObjectList(); + } +} diff --git a/src/engine/db/handlers/dbEnchantmentHandler.java b/src/engine/db/handlers/dbEnchantmentHandler.java new file mode 100644 index 00000000..383f3d8c --- /dev/null +++ b/src/engine/db/handlers/dbEnchantmentHandler.java @@ -0,0 +1,51 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.concurrent.ConcurrentHashMap; + +public class dbEnchantmentHandler extends dbHandlerBase { + + public ConcurrentHashMap GET_ENCHANTMENTS_FOR_ITEM(final int id) { + ConcurrentHashMap enchants = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + prepareCallable("SELECT * FROM `dyn_item_enchantment` WHERE `ItemID`=?;"); + setLong(1, (long)id); + try { + ResultSet resultSet = executeQuery(); + while (resultSet.next()) + enchants.put(resultSet.getString("powerAction"), resultSet.getInt("rank")); + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode()); + } finally { + closeCallable(); + } + return enchants; + } + + public boolean CREATE_ENCHANTMENT_FOR_ITEM(long itemID, String powerAction, int rank) { + prepareCallable("INSERT INTO `dyn_item_enchantment` (`itemID`, `powerAction`, `rank`) VALUES (?, ?, ?);"); + setLong(1, itemID); + setString(2, powerAction); + setInt(3, rank); + return (executeUpdate() != 0); + } + + public boolean CLEAR_ENCHANTMENTS(long itemID) { + prepareCallable("DELETE FROM `dyn_item_enchantment` WHERE `itemID`=?;"); + setLong(1, itemID); + return (executeUpdate() != 0); + } + +} diff --git a/src/engine/db/handlers/dbGuildHandler.java b/src/engine/db/handlers/dbGuildHandler.java new file mode 100644 index 00000000..43d43593 --- /dev/null +++ b/src/engine/db/handlers/dbGuildHandler.java @@ -0,0 +1,486 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.Enum; +import engine.Enum.GuildHistoryType; +import engine.gameManager.DbManager; +import engine.objects.*; +import engine.server.MBServerStatics; +import org.joda.time.DateTime; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; + +public class dbGuildHandler extends dbHandlerBase { + + public dbGuildHandler() { + this.localClass = Guild.class; + this.localObjectType = engine.Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public int BANISH_FROM_GUILD_OFFLINE(final int target, boolean sourceIsGuildLeader) { + if (!sourceIsGuildLeader) //one IC cannot banish another IC + prepareCallable("UPDATE `obj_character` SET `guildUID`=NULL, `guild_isInnerCouncil`=0, `guild_isTaxCollector`=0," + + " `guild_isRecruiter`=0, `guild_isFullMember`=0, `guild_title`=0 WHERE `UID`=? && `guild_isInnerCouncil`=0"); + else + prepareCallable("UPDATE `obj_character` SET `guildUID`=NULL, `guild_isInnerCouncil`=0, `guild_isTaxCollector`=0," + + " `guild_isRecruiter`=0, `guild_isFullMember`=0, `guild_title`=0 WHERE `UID`=?"); + setLong(1, (long) target); + return executeUpdate(); + } + + + + public boolean ADD_TO_BANISHED_FROM_GUILDLIST(int target, long characterID) { + prepareCallable("INSERT INTO `dyn_guild_banishlist` (`GuildID`, `CharacterID`) VALUES (?,?)"); + setLong(1, (long) target); + setLong(2, characterID); + return (executeUpdate() > 0); + } + + public boolean REMOVE_FROM_BANISH_LIST(int target, long characterID) { + prepareCallable("DELETE FROM `dyn_guild_banishlist` (`GuildID`, `CharacterID`) VALUES (?,?)"); + setLong(1, (long) target); + setLong(2, characterID); + return (executeUpdate() > 0); + } + + public boolean ADD_TO_GUILDHISTORY(int target, PlayerCharacter pc, DateTime historyDate, GuildHistoryType historyType) { + prepareCallable("INSERT INTO `dyn_character_guildhistory` (`GuildID`, `CharacterID`, `historyDate`, `historyType`) VALUES (?,?,?,?)"); + setLong(1, (long) target); + setLong(2, pc.getObjectUUID()); + + if (historyDate == null) + setNULL(3, java.sql.Types.DATE); + else + setTimeStamp(3, historyDate.getMillis()); + setString(4,historyType.name()); + return (executeUpdate() > 0); + } + + //TODO Need to get this working. + public ArrayList GET_GUILD_HISTORY_OF_PLAYER(final int id) { + prepareCallable("SELECT g.* FROM `obj_guild` g, `dyn_character_guildhistory` l WHERE g.`UID` = l.`GuildID` && l.`CharacterID` = ?"); + setLong(1, (long) id); + return getObjectList(); + } + + public String GET_GUILD_LIST(int guildType) { + + String newLine = System.getProperty("line.separator"); + String outputStr = null; + ResultSet resultSet; + + // Setup and execute stored procedure + + prepareCallable("CALL `guild_GETLIST`(?)"); + setInt(1, guildType); + resultSet = executeQuery(); + + // Build formatted string with data from query + + outputStr += newLine; + outputStr += String.format("%-10s %-30s %-10s %-10s", "UUID", "Name", "GL UUID", "TOL_UUID"); + outputStr += newLine; + + try { + + while (resultSet.next()) { + + outputStr += String.format("%-10d %-30s %-10d %-10d", resultSet.getInt(1), + resultSet.getString(2), resultSet.getInt(3), resultSet.getInt(4)); + outputStr += newLine; + + } + + // Exception handling + + } catch (SQLException e) { + Logger.error( e.getMessage()); + } finally { + closeCallable(); + } + + return outputStr; + } + + + + + public ArrayList GET_GUILD_ALLIES(final int id) { + prepareCallable("SELECT g.* FROM `obj_guild` g, `dyn_guild_allianceenemylist` l " + + "WHERE l.isAlliance = 1 && l.OtherGuildID = g.UID && l.GuildID=?"); + setLong(1, (long) id); + return getObjectList(); + + } + + public static ArrayList GET_GUILD_BANISHED(final int id) { + + return new ArrayList<>(); + + // Bugfix + // prepareCallable("SELECT * FROM `obj_character`, `dyn_guild_banishlist` WHERE `obj_character.char_isActive` = 1 AND `dyn_guild_banishlist.CharacterID` = `obj_character.UID` AND `obj_character.GuildID`=?"); + + //prepareCallable("SELECT * FROM `obj_character` `,` `dyn_guild_banishlist` WHERE obj_character.char_isActive = 1 AND dyn_guild_banishlist.CharacterID = obj_character.UID AND dyn_guild_banishlist.GuildID = ?"); + //setLong(1, (long) id); + + //return getObjectList(); + } + + public ArrayList GET_GUILD_ENEMIES(final int id) { + prepareCallable("SELECT g.* FROM `obj_guild` g, `dyn_guild_allianceenemylist` l " + + "WHERE l.isAlliance = 0 && l.OtherGuildID = g.UID && l.GuildID=?"); + setLong(1, (long) id); + return getObjectList(); + } + + public ArrayList GET_GUILD_KOS_CHARACTER(final int id) { + prepareCallable("SELECT c.* FROM `obj_character` c, `dyn_guild_characterkoslist` l WHERE c.`char_isActive` = 1 && l.`KOSCharacterID` = c.`UID` && l.`GuildID`=?"); + setLong(1, (long) id); + return getObjectList(); + } + + public ArrayList GET_GUILD_KOS_GUILD(final int id) { + prepareCallable("SELECT g.* FROM `obj_guild` g, `dyn_guild_guildkoslist` l " + + "WHERE l.KOSGuildID = g.UID && l.GuildID = ?"); + setLong(1, (long) id); + return getObjectList(); + } + + public ArrayList GET_SUB_GUILDS(final int guildID) { + prepareCallable("SELECT `obj_guild`.*, `object`.`parent` FROM `object` INNER JOIN `obj_guild` ON `obj_guild`.`UID` = `object`.`UID` WHERE `object`.`parent` = ?;"); + setInt(1, guildID); + return getObjectList(); + } + + + public Guild GET_GUILD(int id) { + Guild guild = (Guild) DbManager.getFromCache(Enum.GameObjectType.Guild, id); + if (guild != null) + return guild; + if (id == 0) + return Guild.getErrantGuild(); + prepareCallable("SELECT `obj_guild`.*, `object`.`parent` FROM `obj_guild` INNER JOIN `object` ON `object`.`UID` = `obj_guild`.`UID` WHERE `object`.`UID`=?"); + setLong(1, (long) id); + return (Guild) getObjectSingle(id); + } + + public ArrayList GET_ALL_GUILDS() { + + prepareCallable("SELECT `obj_guild`.*, `object`.`parent` FROM `obj_guild` INNER JOIN `object` ON `object`.`UID` = `obj_guild`.`UID`"); + + return getObjectList(); + } + + public boolean IS_CREST_UNIQUE(final GuildTag gt) { + boolean valid = false; + if (gt.backgroundColor01 == gt.backgroundColor02) { + //both background colors the same, ignore backgroundDesign + prepareCallable("SELECT `name` FROM `obj_guild` WHERE `backgroundColor01`=? && `backgroundColor02`=? && `symbolColor`=? && `symbol`=?;"); + setInt(1, gt.backgroundColor01); + setInt(2, gt.backgroundColor02); + setInt(3, gt.symbolColor); + setInt(4, gt.symbol); + + } else { + prepareCallable("SELECT `name` FROM `obj_guild` WHERE `backgroundColor01`=? && `backgroundColor02`=? && `symbolColor`=? && `backgroundDesign`=? && `symbol`=?;"); + setInt(1, gt.backgroundColor01); + setInt(2, gt.backgroundColor02); + setInt(3, gt.symbolColor); + setInt(4, gt.backgroundDesign); + setInt(5, gt.symbol); + } + try { + ResultSet rs = executeQuery(); + if (!rs.next()) + valid = true; + rs.close(); + } catch (SQLException e) { + Logger.error(e.getMessage()); + } + return valid; + } + + public String SET_PROPERTY(final Guild g, String name, Object new_value) { + prepareCallable("CALL guild_SETPROP(?,?,?)"); + setLong(1, (long) g.getObjectUUID()); + setString(2, name); + setString(3, String.valueOf(new_value)); + return getResult(); + } + + public String SET_PROPERTY(final Guild g, String name, Object new_value, Object old_value) { + prepareCallable("CALL guild_GETSETPROP(?,?,?,?)"); + setLong(1, (long) g.getObjectUUID()); + setString(2, name); + setString(3, String.valueOf(new_value)); + setString(4, String.valueOf(old_value)); + return getResult(); + } + + public boolean SET_GUILD_OWNED_CITY(int guildID, int cityID) { + prepareCallable("UPDATE `obj_guild` SET `ownedCity`=? WHERE `UID`=?"); + setLong(1, (long) cityID); + setLong(2, (long) guildID); + return (executeUpdate() > 0); + } + + public boolean SET_GUILD_LEADER(int objectUUID,int guildID) { + prepareCallable("UPDATE `obj_guild` SET `leaderUID`=? WHERE `UID`=?"); + setLong(1, (long) objectUUID); + setLong(2, (long) guildID); + return (executeUpdate() > 0); + } + + + public boolean IS_NAME_UNIQUE(final String name) { + boolean valid = false; + prepareCallable("SELECT `name` FROM `obj_guild` WHERE `name`=?;"); + setString(1, name); + try { + ResultSet rs = executeQuery(); + if (!rs.next()) + valid = true; + rs.close(); + } catch (SQLException e) { + Logger.warn(e.getMessage()); + } + return valid; + + } + + public Guild SAVE_TO_DATABASE(Guild g) { + prepareCallable("CALL `guild_CREATE`(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + + GuildTag gt = g.getGuildTag(); + if ( gt == null) + return null; + setLong(1, MBServerStatics.worldUUID); + setLong(2, g.getGuildLeaderUUID()); + setString(3, g.getName()); + setInt(4, gt.backgroundColor01); + setInt(5, gt.backgroundColor02); + setInt(6, gt.symbolColor); + setInt(7, gt.backgroundDesign); + setInt(8 , gt.symbol); + setInt(9, g.getCharter()); + setString(10, g.getLeadershipType()); + setString(11, g.getMotto()); + + int objectUUID = (int) getUUID(); + if (objectUUID > 0) + return GET_GUILD(objectUUID); + return null; + } + + public boolean UPDATE_GUILD_RANK_OFFLINE(int target, int newRank, int guildId) { + prepareCallable("UPDATE `obj_character` SET `guild_title`=? WHERE `UID`=? && `guildUID`=?"); + setInt(1, newRank); + setInt(2, target); + setInt(3, guildId); + return (executeUpdate() > 0); + } + + public boolean UPDATE_PARENT(int guildUID, int parentUID) { + prepareCallable("UPDATE `object` SET `parent`=? WHERE `UID`=?"); + setInt(1, parentUID); + setInt(2, guildUID); + return (executeUpdate() > 0); + } + + public int DELETE_GUILD(final Guild guild) { + prepareCallable("DELETE FROM `object` WHERE `UID` = ?"); + setLong(1, (long) guild.getObjectUUID()); + return executeUpdate(); + } + + public boolean UPDATE_MINETIME(int guildUID, int mineTime) { + prepareCallable("UPDATE `obj_guild` SET `mineTime`=? WHERE `UID`=?"); + setInt(1, mineTime); + setInt(2, guildUID); + return (executeUpdate() > 0); + } + + public int UPDATE_GUILD_STATUS_OFFLINE(int target, boolean isInnerCouncil, boolean isRecruiter, boolean isTaxCollector, int guildId) { + int updateMask = 0; + prepareCallable("SELECT `guild_isInnerCouncil`, `guild_isTaxCollector`, `guild_isRecruiter` FROM `obj_character` WHERE `UID`=? && `guildUID`=?"); + setLong(1, (long) target); + setLong(2, (long) guildId); + try { + ResultSet rs = executeQuery(); + + //If the first query had no results, neither will the second + if (rs.first()) { + //Determine what is different + if (rs.getBoolean("guild_isInnerCouncil") != isInnerCouncil) + updateMask |= 4; + if (rs.getBoolean("guild_isRecruiter") != isRecruiter) + updateMask |= 2; + if (rs.getBoolean("guild_isTaxCollector") != isTaxCollector) + updateMask |= 1; + } + rs.close(); + } catch (SQLException e) { + Logger.error( e.toString()); + } + prepareCallable("UPDATE `obj_character` SET `guild_isInnerCouncil`=?, `guild_isTaxCollector`=?, `guild_isRecruiter`=?, `guild_isFullMember`=? WHERE `UID`=? && `guildUID`=?"); + setBoolean(1, isInnerCouncil); + setBoolean(2, isRecruiter); + setBoolean(3, isTaxCollector); + setBoolean(4, ((updateMask > 0))); //If you are becoming an officer, or where an officer, your a full member... + setLong(5, (long) target); + setLong(6, (long) guildId); + return executeUpdate(); + + } + + + // *** Refactor: Why are we saving tags/charter in update? + // It's not like this shit ever changes. + + public boolean updateDatabase(final Guild g) { + prepareCallable("UPDATE `obj_guild` SET `name`=?, `backgroundColor01`=?, `backgroundColor02`=?, `symbolColor`=?, `backgroundDesign`=?, `symbol`=?, `charter`=?, `motd`=?, `icMotd`=?, `nationMotd`=?, `leaderUID`=? WHERE `UID`=?"); + setString(1, g.getName()); + setInt(2, g.getGuildTag().backgroundColor01); + setInt(3, g.getGuildTag().backgroundColor02); + setInt(4, g.getGuildTag().symbolColor); + setInt(5, g.getGuildTag().backgroundDesign); + setInt(6, g.getGuildTag().symbol); + setInt(7, g.getCharter()); + setString(8, g.getMOTD()); + setString(9, g.getICMOTD()); + setString(10, ""); + setInt(11, g.getGuildLeaderUUID()); + setLong(12, (long) g.getObjectUUID()); + return (executeUpdate() != 0); + } + public boolean ADD_TO_ALLIANCE_LIST(final long sourceGuildID, final long targetGuildID, boolean isRecommended, boolean isAlly, String recommender) { + prepareCallable("INSERT INTO `dyn_guild_allianceenemylist` (`GuildID`, `OtherGuildID`,`isRecommended`, `isAlliance`, `recommender`) VALUES (?,?,?,?,?)"); + setLong(1, sourceGuildID); + setLong(2, targetGuildID); + setBoolean(3, isRecommended); + setBoolean(4, isAlly); + setString(5, recommender); + return (executeUpdate() > 0); + } + + public boolean REMOVE_FROM_ALLIANCE_LIST(final long sourceGuildID, long targetGuildID) { + prepareCallable("DELETE FROM `dyn_guild_allianceenemylist` WHERE `GuildID`=? AND `OtherGuildID`=?"); + setLong(1, sourceGuildID); + setLong(2, targetGuildID); + return (executeUpdate() > 0); + } + + public boolean UPDATE_RECOMMENDED(final long sourceGuildID, long targetGuildID) { + prepareCallable("UPDATE `dyn_guild_allianceenemylist` SET `isRecommended` = ? WHERE `GuildID`=? AND `OtherGuildID`=?"); + setByte(1,(byte)0); + setLong(2, sourceGuildID); + setLong(3, targetGuildID); + return (executeUpdate() > 0); + } + + public boolean UPDATE_ALLIANCE(final long sourceGuildID, long targetGuildID, boolean isAlly) { + prepareCallable("UPDATE `dyn_guild_allianceenemylist` SET `isAlliance` = ? WHERE `GuildID`=? AND `OtherGuildID`=?"); + setBoolean(1,isAlly); + setLong(2, sourceGuildID); + setLong(3, targetGuildID); + return (executeUpdate() > 0); + } + + public boolean UPDATE_ALLIANCE_AND_RECOMMENDED(final long sourceGuildID, long targetGuildID, boolean isAlly) { + prepareCallable("UPDATE `dyn_guild_allianceenemylist` SET `isRecommended` = ?, `isAlliance` = ? WHERE `GuildID`=? AND `OtherGuildID`=?"); + setByte(1,(byte)0); + setBoolean(2,isAlly); + setLong(3, sourceGuildID); + setLong(4, targetGuildID); + + return (executeUpdate() > 0); + } + + public void LOAD_ALL_ALLIANCES_FOR_GUILD(Guild guild) { + + if (guild == null) + return; + + prepareCallable("SELECT * FROM `dyn_guild_allianceenemylist` WHERE `GuildID` = ?"); + setInt(1,guild.getObjectUUID()); + + try { + ResultSet rs = executeQuery(); + + //shrines cached in rs for easy cache on creation. + while (rs.next()) { + GuildAlliances guildAlliance = new GuildAlliances(rs); + guild.guildAlliances.put(guildAlliance.getAllianceGuild(), guildAlliance); + } + + + } catch (SQLException e) { + Logger.error( e.getMessage()); + } finally { + closeCallable(); + } + + } + + public void LOAD_GUILD_HISTORY_FOR_PLAYER(PlayerCharacter pc) { + + if (pc == null) + return; + + prepareCallable("SELECT * FROM `dyn_character_guildhistory` WHERE `CharacterID` = ?"); + setInt(1,pc.getObjectUUID()); + + try { + ArrayList tempList = new ArrayList<>(); + ResultSet rs = executeQuery(); + + //shrines cached in rs for easy cache on creation. + while (rs.next()) { + GuildHistory guildHistory = new GuildHistory(rs); + tempList.add(guildHistory); + } + + pc.setGuildHistory(tempList); + + + } catch (SQLException e) { + Logger.error(e.getMessage()); + } finally { + closeCallable(); + } + + } + + //TODO uncomment this when finished with guild history warehouse integration +// public HashMap GET_WAREHOUSE_GUILD_HISTORY(){ +// +// HashMap tempMap = new HashMap<>(); +// prepareCallable("SELECT * FROM `warehouse_guildhistory` WHERE `eventType` = 'CREATE'"); +// try { +// ResultSet rs = executeQuery(); +// +// while (rs.next()) { +// GuildRecord guildRecord = new GuildRecord(rs); +// tempMap.put(guildRecord.guildID, guildRecord); +// } +// }catch (Exception e){ +// Logger.error(e); +// } +// return tempMap; +// +// } + + +} diff --git a/src/engine/db/handlers/dbHandlerBase.java b/src/engine/db/handlers/dbHandlerBase.java new file mode 100644 index 00000000..2531d854 --- /dev/null +++ b/src/engine/db/handlers/dbHandlerBase.java @@ -0,0 +1,470 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.gameManager.ConfigManager; +import engine.gameManager.DbManager; +import engine.objects.AbstractGameObject; +import engine.objects.AbstractWorldObject; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.*; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashSet; + +public abstract class dbHandlerBase { + + /* + * CallableStatements handled below this line! + */ + protected Class localClass = null; + protected GameObjectType localObjectType; + protected final ThreadLocal cs = new ThreadLocal<>(); + + protected final void prepareCallable(final String sql) { + try { + this.cs.set((CallableStatement) DbManager.getConn().prepareCall(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)); + } catch (SQLException e) { + Logger.error("DbManager.getConn", e); + Logger.error("SQL Error number: " + e.getErrorCode()); + } + } + + protected final void setDate(int parameterIndex, Date value) { + try { + this.cs.get().setDate(parameterIndex, value); + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode()); + } + } + + protected final void setInt(int parameterIndex, int value) { + try { + this.cs.get().setInt(parameterIndex, value); + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode()); + } + } + + protected final void setLong(int parameterIndex, long value) { + try { + this.cs.get().setLong(parameterIndex, value); + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode()); + } + } + + protected final void setFloat(int parameterIndex, float value) { + try { + this.cs.get().setFloat(parameterIndex, value); + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode()); + } + } + + protected final void setShort(int parameterIndex, short value) { + try { + this.cs.get().setShort(parameterIndex, value); + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode()); + } + } + + protected final void setString(int parameterIndex, String value) { + try { + this.cs.get().setString(parameterIndex, value); + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode()); + } + } + + protected final void setBytes(int parameterIndex, byte[] value) { + try { + this.cs.get().setBytes(parameterIndex, value); + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode()); + } + } + + protected final void setByte(int parameterIndex, byte value) { + try { + this.cs.get().setByte(parameterIndex, value); + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode()); + } + } + + protected final void setBoolean(int parameterIndex, boolean value) { + try { + this.cs.get().setBoolean(parameterIndex, value); + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode()); + } + } + + protected final void setNULL(int parameterIndex, int type) { + try { + this.cs.get().setNull(parameterIndex, type); + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode()); + } + } + + protected final void setLocalDateTime(int parameterIndex, LocalDateTime localDateTime) { + + try { + this.cs.get().setTimestamp(parameterIndex, Timestamp.valueOf(localDateTime)); + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode()); + } + } + + protected final void setTimeStamp(int parameterIndex, long time) { + try { + this.cs.get().setTimestamp(parameterIndex, new java.sql.Timestamp(time)); + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode()); + } + } + + protected final boolean execute() { + try { + return this.cs.get().execute(); + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode()); + logSQLCommand(); + } + return false; + } + + protected final ResultSet executeQuery() { + try { + return this.cs.get().executeQuery(); + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode()); + logSQLCommand(); + } + return null; + } + + protected final int executeUpdate() { + return executeUpdate(true); + } + + protected final int executeUpdate(boolean close) { + try { + return this.cs.get().executeUpdate(); + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode()); + logSQLCommand(); + } finally { + if (close) + closeCallable(); + } + return 0; + } + + protected final void logSQLCommand() { + try { + Logger.error("Failed SQL Command: " + this.cs.get().toString()); + } catch (Exception e) { + + } + } + + // Common return values from the database when calling stored procedures, abstracted to this layer + protected final String getResult(){ + try { + ResultSet rs = this.executeQuery(); + if (rs.next() && !isError(rs)) + return rs.getString("result"); + } catch (SQLException e) { + Logger.error(e); + logSQLCommand(); + } finally { + closeCallable(); + } + return null; + } + + // Used for Stored procedures that return true when they succeed. + protected final boolean worked() { + try { + ResultSet rs = this.executeQuery(); + if (rs.next() && !isError(rs)) + return rs.getBoolean("result"); + } catch (SQLException e) { + Logger.error(e); + logSQLCommand(); + } finally { + closeCallable(); + } + return false; + } + + // Common return values from the database when calling stored procedures, abstracted to this layer + protected final long getUUID(){ + try { + ResultSet rs = this.executeQuery(); + if (rs.next() && !isError(rs)) + return rs.getLong("UID"); + } catch (SQLException e) { + Logger.error(e); + logSQLCommand(); + } finally { + closeCallable(); + } + return -1; + } + + protected final String getString(String field) { + try { + ResultSet rs = this.executeQuery(); + if (rs.next()) + return rs.getString(field); + } catch (SQLException e) { + Logger.error(e); + logSQLCommand(); + } finally { + closeCallable(); + } + return ""; + } + + protected final long getLong(String field) { + try { + ResultSet rs = this.executeQuery(); + if (rs.next()) + return rs.getLong(field); + } catch (SQLException e) { + Logger.error(e); + logSQLCommand(); + } finally { + closeCallable(); + } + return 0L; + } + + protected final int getInt(String field) { + try { + ResultSet rs = this.executeQuery(); + if (rs.next()) + return rs.getInt(field); + } catch (SQLException e) { + Logger.error(e); + logSQLCommand(); + } finally { + closeCallable(); + } + return 0; + } + + protected final int insertGetUUID() { + int key = 0; + try { + this.cs.get().executeUpdate(); + ResultSet rs = this.cs.get().getGeneratedKeys(); + if (rs.next()) + key = rs.getInt(1); + } catch (SQLException e) { + Logger.error(e); + logSQLCommand(); + } finally { + closeCallable(); + } + return key; + } + + protected final boolean isError(ResultSet rs) throws SQLException { + ResultSetMetaData rsmd = rs.getMetaData(); + if (rsmd.getColumnCount() > 0 && !rsmd.getColumnName(1).equals("errorno")) + return false; + printError(rs); + return true; + } + + protected final void printError(ResultSet rs) { + try { + int errorNum = rs.getInt("errorno"); + String errorMsg = rs.getString("errormsg"); + Logger.error("SQLError: errorNum: " + errorNum + ", errorMsg: " + errorMsg); + logSQLCommand(); + } catch (SQLException e) {} + } + + protected final void getColumNames(ResultSet rs) throws SQLException { + ResultSetMetaData rsmd = rs.getMetaData(); + int numColumns = rsmd.getColumnCount(); + String out = "Column names for resultSet: "; + for (int i=1; i AbstractGameObject getObjectSingle(int id) { + return getObjectSingle(id, false, true); + } + + protected AbstractGameObject getObjectSingle(int id, boolean forceFromDB, boolean storeInCache) { + + if (cs.get() == null){ + return null; + } + + if (!forceFromDB) { + if (DbManager.inCache(localObjectType, id)) { + closeCallable(); + return DbManager.getFromCache(localObjectType, id); + } + } + + AbstractGameObject out = null; + + try { + if (MBServerStatics.DB_ENABLE_QUERY_OUTPUT) + Logger.info( "[GetObjectList] Executing query:" + cs.get().toString()); + + ResultSet rs = cs.get().executeQuery(); + + if (rs.next()) { + out = localClass.getConstructor(ResultSet.class).newInstance(rs); + + if (storeInCache) + DbManager.addToCache(out); + } + + rs.close(); + + } catch (Exception e) { + Logger.error("AbstractGameObject", e); + out = null; + } finally { + closeCallable(); + } + + // Only call runAfterLoad() for objects instanced on the world server + + if ((out != null && out instanceof AbstractWorldObject) && + (ConfigManager.serverType.equals(Enum.ServerType.WORLDSERVER) || + (out.getObjectType() == GameObjectType.Guild))) + ((AbstractWorldObject)out).runAfterLoad(); + + return out; + } + + protected void closeCallable() { + try { + if (this.cs.get() != null) + this.cs.get().close(); + } catch (SQLException e) {} + } + + protected ArrayList getObjectList() { + return getObjectList(20, false); + } + + protected ArrayList getLargeObjectList() { + return getObjectList(2000, false); + } + + @SuppressWarnings("unchecked") + protected ArrayList getObjectList(int listSize, boolean forceFromDB) { + + String query = "No Callable Statement accessable."; + + ArrayList out = new ArrayList<>(listSize); + + if (this.cs.get() == null) + return out; + + try { + + CallableStatement css = this.cs.get(); + + if (css != null) + query = this.cs.get().toString(); + + if (MBServerStatics.DB_ENABLE_QUERY_OUTPUT) + Logger.info( "[GetObjectList] Executing query:" + query); + + ResultSet rs = this.cs.get().executeQuery(); + + while (rs.next()) { + + int id = rs.getInt(1); + + if (!forceFromDB && DbManager.inCache(localObjectType, id)) { + out.add((T) DbManager.getFromCache(localObjectType, id)); + } else { + AbstractGameObject toAdd = localClass.getConstructor(ResultSet.class).newInstance(rs); + DbManager.addToCache(toAdd); + out.add((T) toAdd); + + if (toAdd != null && toAdd instanceof AbstractWorldObject) + ((AbstractWorldObject)toAdd).runAfterLoad(); + + } + } + rs.close(); + } catch (Exception e) { + Logger.error(localClass.getCanonicalName(), "List Failure: " + query, e); + e.printStackTrace(); + return new ArrayList<>(); // Do we want a null return on error? + } finally { + closeCallable(); + } + + return out; + } + + /* Prepared Statements handled below this line */ + + protected HashSet getIntegerList(final int columnNumber) { + + if (MBServerStatics.DB_ENABLE_QUERY_OUTPUT) + Logger.info("[GetIntegerList] Executing query:" + this.cs.toString()); + + HashSet out = new HashSet<>(); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + out.add(rs.getInt(columnNumber)); + } + rs.close(); + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode()); + } finally { + closeCallable(); + } + return out; + } +} diff --git a/src/engine/db/handlers/dbHeightMapHandler.java b/src/engine/db/handlers/dbHeightMapHandler.java new file mode 100644 index 00000000..1ea5d54c --- /dev/null +++ b/src/engine/db/handlers/dbHeightMapHandler.java @@ -0,0 +1,47 @@ +package engine.db.handlers; + +import engine.InterestManagement.HeightMap; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class dbHeightMapHandler extends dbHandlerBase { + + public dbHeightMapHandler() { + + + } + + public void LOAD_ALL_HEIGHTMAPS() { + + HeightMap thisHeightmap; + + int recordsRead = 0; + int worthlessDupes = 0; + + HeightMap.heightMapsCreated = 0; + + prepareCallable("SELECT * FROM static_zone_heightmap INNER JOIN static_zone_size ON static_zone_size.loadNum = static_zone_heightmap.zoneLoadID"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + recordsRead++; + thisHeightmap = new HeightMap(rs); + + if (thisHeightmap.getHeightmapImage() == null) { + Logger.info( "Imagemap for " + thisHeightmap.getHeightMapID() + " was null"); + continue; + } + } + } catch (SQLException e) { + Logger.error("LoadAllHeightMaps: " + e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + } + +} diff --git a/src/engine/db/handlers/dbItemBaseHandler.java b/src/engine/db/handlers/dbItemBaseHandler.java new file mode 100644 index 00000000..6217f7d3 --- /dev/null +++ b/src/engine/db/handlers/dbItemBaseHandler.java @@ -0,0 +1,152 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.objects.EquipmentSetEntry; +import engine.objects.ItemBase; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; + +public class dbItemBaseHandler extends dbHandlerBase { + + public dbItemBaseHandler() { + + } + + public void LOAD_BAKEDINSTATS(ItemBase itemBase) { + + try { + prepareCallable("SELECT * FROM `static_item_bakedinstat` WHERE `itemID` = ?"); + setInt(1, itemBase.getUUID()); + + ResultSet rs = executeQuery(); + + while (rs.next()) { + + if (rs.getBoolean("fromUse")) + itemBase.getUsedStats().put(rs.getInt("token"), rs.getInt("numTrains")); + else + itemBase.getBakedInStats().put(rs.getInt("token"), rs.getInt("numTrains")); + } + } catch (SQLException e) { + Logger.error( e.toString()); + } finally { + closeCallable(); + } + } + + public void LOAD_ANIMATIONS(ItemBase itemBase) { + + ArrayList tempList = new ArrayList<>(); + ArrayList tempListOff = new ArrayList<>(); + try { + prepareCallable("SELECT * FROM `static_itembase_animations` WHERE `itemBaseUUID` = ?"); + setInt(1, itemBase.getUUID()); + + ResultSet rs = executeQuery(); + + while (rs.next()) { + int animation = rs.getInt("animation"); + + boolean rightHand = rs.getBoolean("rightHand"); + + if (rightHand) + tempList.add(animation); + else + tempListOff.add(animation); + + } + } catch (SQLException e) { + Logger.error( e.toString()); + } finally { + closeCallable(); + } + + itemBase.setAnimations(tempList); + itemBase.setOffHandAnimations(tempListOff); + } + + public void LOAD_ALL_ITEMBASES() { + + ItemBase itemBase; + + int recordsRead = 0; + + prepareCallable("SELECT * FROM static_itembase"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + recordsRead++; + itemBase = new ItemBase(rs); + + // Add ItemBase to internal cache + + ItemBase.addToCache(itemBase); + } + + Logger.info( "read: " + recordsRead + "cached: " + ItemBase.getUUIDCache().size()); + + } catch (SQLException e) { + Logger.error( e.toString()); + } finally { + closeCallable(); + } + } + + public HashMap> LOAD_EQUIPMENT_FOR_NPC_AND_MOBS() { + + HashMap> equipmentSets; + EquipmentSetEntry equipmentSetEntry; + int equipSetID; + + equipmentSets = new HashMap<>(); + int recordsRead = 0; + + prepareCallable("SELECT * FROM static_npc_equipmentset"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + recordsRead++; + + equipSetID = rs.getInt("equipmentSet"); + equipmentSetEntry = new EquipmentSetEntry(rs); + + if (equipmentSets.get(equipSetID) == null){ + ArrayList equipList = new ArrayList<>(); + equipList.add(equipmentSetEntry); + equipmentSets.put(equipSetID, equipList); + } + else{ + ArrayListequipList = equipmentSets.get(equipSetID); + equipList.add(equipmentSetEntry); + equipmentSets.put(equipSetID, equipList); + } + } + + Logger.info("read: " + recordsRead + " cached: " + equipmentSets.size()); + + } catch (SQLException e) { + Logger.error( e.toString()); + } finally { + closeCallable(); + } + return equipmentSets; + } +} diff --git a/src/engine/db/handlers/dbItemHandler.java b/src/engine/db/handlers/dbItemHandler.java new file mode 100644 index 00000000..6a2e14f3 --- /dev/null +++ b/src/engine/db/handlers/dbItemHandler.java @@ -0,0 +1,430 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.Enum.ItemContainerType; +import engine.Enum.ItemType; +import engine.Enum.OwnerType; +import engine.objects.*; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.HashSet; + + +public class dbItemHandler extends dbHandlerBase { + + public dbItemHandler() { + this.localClass = Item.class; + this.localObjectType = engine.Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public Item ADD_ITEM(Item toAdd) { + prepareCallable("CALL `item_CREATE`(?, ?, ?, ?, ?, ?, ?, ?, ?,?);"); + setInt(1, toAdd.getOwnerID()); + setInt(2, toAdd.getItemBaseID()); + setInt(3, toAdd.getChargesRemaining()); + setInt(4, toAdd.getDurabilityCurrent()); + setInt(5, toAdd.getDurabilityMax()); + if (toAdd.getNumOfItems() < 1) + setInt(6, 1); + else + setInt(6, toAdd.getNumOfItems()); + + switch (toAdd.containerType) { + case INVENTORY: + setString(7, "inventory"); + break; + case EQUIPPED: + setString(7, "equip"); + break; + case BANK: + setString(7, "bank"); + break; + case VAULT: + setString(7, "vault"); + break; + case FORGE: + setString(7, "forge"); + break; + default: + setString(7, "none"); //Shouldn't be here + break; + } + + setByte(8, toAdd.getEquipSlot()); + setInt(9, toAdd.getFlags()); + setString(10, toAdd.getCustomName()); + int objectUUID = (int) getUUID(); + + if (objectUUID > 0) + return GET_ITEM(objectUUID); + return null; + } + + public boolean DELETE_ITEM(final Item item) { + prepareCallable("DELETE FROM `object` WHERE `UID`=? && `type`='item' limit 1"); + setLong(1, (long) item.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean DELETE_ITEM(final int itemUUID) { + prepareCallable("DELETE FROM `object` WHERE `UID`=? && `type`='item' limit 1"); + setLong(1, (long) itemUUID); + return (executeUpdate() > 0); + } + + public String GET_OWNER(int ownerID) { + prepareCallable("SELECT `type` FROM `object` WHERE `UID`=?"); + setLong(1, (long) ownerID); + return getString("type"); + } + + public boolean DO_TRADE(HashSet from1, HashSet from2, + CharacterItemManager man1, CharacterItemManager man2, + Item inventoryGold1, Item inventoryGold2, int goldFrom1, int goldFrom2) { + + AbstractCharacter ac1 = man1.getOwner(); + AbstractCharacter ac2 = man2.getOwner(); + if (ac1 == null || ac2 == null || inventoryGold1 == null || inventoryGold2 == null) + return false; + + prepareCallable("CALL `item_TRADE`(?, ?, ?, ?, ?, ?, ?, ?)"); + setString(1, formatTradeString(from1)); + setLong(2, (long) ac1.getObjectUUID()); + setString(3, formatTradeString(from2)); + setLong(4, (long) ac2.getObjectUUID()); + setInt(5, goldFrom1); + setLong(6, (long) inventoryGold1.getObjectUUID()); + setInt(7, goldFrom2); + setLong(8, (long) inventoryGold2.getObjectUUID()); + return worked(); + } + + private static String formatTradeString(HashSet list) { + int size = list.size(); + + String ret = ""; + if (size == 0) + return ret; + boolean start = true; + for (int i : list) { + if (start){ + ret += i; + start = false; + } + else + ret += "," + i; + } + return ret; + } + + public ArrayList GET_EQUIPPED_ITEMS(final int targetId) { + prepareCallable("SELECT `obj_item`.*, `object`.`parent`, `object`.`type` FROM `object` INNER JOIN `obj_item` ON `object`.`UID` = `obj_item`.`UID` WHERE `object`.`parent`=? && `obj_item`.`item_container`='equip';"); + setLong(1, (long) targetId); + return getObjectList(); + } + + public Item GET_ITEM(final int id) { + + + prepareCallable("SELECT `obj_item`.*, `object`.`parent`, `object`.`type` FROM `object` INNER JOIN `obj_item` ON `object`.`UID` = `obj_item`.`UID` WHERE `object`.`UID`=?;"); + setLong(1, (long) id); + return (Item) getObjectSingle(id); + } + + public Item GET_GOLD_FOR_PLAYER(final int playerID, final int goldID, int worldID) { + prepareCallable("SELECT `obj_item`.*, `object`.`parent`, `object`.`type` FROM `object` INNER JOIN `obj_item` ON `object`.`UID` = `obj_item`.`UID` WHERE `object`.`parent`=? AND `obj_item`.`item_itembaseID`=?;"); + setInt(1, playerID); + setInt(2, goldID); + int objectUUID = (int) getUUID(); + return (Item) getObjectSingle(objectUUID); + + } + + public ArrayList GET_ITEMS_FOR_ACCOUNT(final int accountId) { + prepareCallable("SELECT `obj_item`.*, `object`.`parent`, `object`.`type` FROM `object` INNER JOIN `obj_item` ON `object`.`UID` = `obj_item`.`UID` WHERE `object`.`parent`=?;"); + setLong(1, (long) accountId); + return getObjectList(); + } + + public ArrayList GET_ITEMS_FOR_NPC(final int npcId) { + prepareCallable("SELECT `obj_item`.*, `object`.`parent`, `object`.`type` FROM `object` INNER JOIN `obj_item` ON `object`.`UID` = `obj_item`.`UID` WHERE `object`.`parent`=?"); + setLong(1, (long) npcId); + return getObjectList(); + } + + public ArrayList GET_ITEMS_FOR_PC(final int id) { + prepareCallable("SELECT `obj_item`.*, `object`.`parent`, `object`.`type` FROM `object` INNER JOIN `obj_item` ON `object`.`UID` = `obj_item`.`UID` WHERE `object`.`parent`=?"); + setLong(1, (long) id); + return getLargeObjectList(); + } + + public ArrayList GET_ITEMS_FOR_PLAYER_AND_ACCOUNT(final int playerID, final int accountID) { + prepareCallable("SELECT `obj_item`.*, `object`.`parent`, `object`.`type` FROM `object` INNER JOIN `obj_item` ON `object`.`UID` = `obj_item`.`UID` WHERE (`object`.`parent`=? OR `object`.`parent`=?)"); + setLong(1, (long) playerID); + setLong(2, (long) accountID); + return getLargeObjectList(); + } + + public boolean MOVE_GOLD(final Item from, final Item to, final int amt) { + int newFromAmt = from.getNumOfItems() - amt; + int newToAmt = to.getNumOfItems() + amt; + prepareCallable("UPDATE `obj_item` SET `item_numberOfItems` = CASE WHEN `UID`=? THEN ? WHEN `UID`=? THEN ? END WHERE `UID` IN (?, ?);"); + setLong(1, (long) from.getObjectUUID()); + setInt(2, newFromAmt); + setLong(3, (long) to.getObjectUUID()); + setInt(4, newToAmt); + setLong(5, (long) from.getObjectUUID()); + setLong(6, (long) to.getObjectUUID()); + return (executeUpdate() != 0); + } + + public boolean ORPHAN_INVENTORY(final HashSet inventory) { + boolean worked = true; + for (Item item : inventory) { + + if (item.getItemBase().getType().equals(ItemType.GOLD)) + continue; + + prepareCallable("UPDATE `obj_item` LEFT JOIN `object` ON `object`.`UID` = `obj_item`.`UID` SET `object`.`parent`=NULL, `obj_item`.`item_container`='none' WHERE `object`.`UID`=?;"); + setLong(1, (long) item.getObjectUUID()); + if (executeUpdate() == 0) + worked = false; + else + item.zeroItem(); + } + return worked; + } + + public Item PURCHASE_ITEM_FROM_VENDOR(final PlayerCharacter pc, final ItemBase ib) { + Item item = null; + byte charges = 0; + charges = (byte) ib.getNumCharges(); + short durability = (short) ib.getDurability(); + + Item temp = new Item(ib, pc.getObjectUUID(), + OwnerType.PlayerCharacter, charges, charges, durability, durability, + true, false,ItemContainerType.INVENTORY, (byte) 0, + new ArrayList<>(),""); + try { + item = this.ADD_ITEM(temp); + } catch (Exception e) { + Logger.error(e); + } + return item; + } + + public HashSet GET_ITEMS_FOR_VENDOR(final int vendorID) { + prepareCallable("SELECT ID FROM static_itembase WHERE vendorType = ?"); + setInt(1, vendorID); + return getIntegerList(1); + } + + public ArrayList GET_ITEMS_FOR_VENDOR_FORGING(final int npcID) { + prepareCallable("SELECT `obj_item`.*, `object`.`parent` FROM `object` INNER JOIN `obj_item` ON `object`.`UID` = `obj_item`.`UID` WHERE `object`.`parent`=? AND `obj_item`.`item_container` =?"); + setLong(1, (long) npcID); + setString(2, "forge"); + return getObjectList(); + } + + public String SET_PROPERTY(final Item i, String name, Object new_value) { + prepareCallable("CALL item_SETPROP(?,?,?)"); + setLong(1, (long) i.getObjectUUID()); + setString(2, name); + setString(3, String.valueOf(new_value)); + return getResult(); + } + + public String SET_PROPERTY(final Item i, String name, Object new_value, Object old_value) { + prepareCallable("CALL item_GETSETPROP(?,?,?,?)"); + setLong(1, (long) i.getObjectUUID()); + setString(2, name); + setString(3, String.valueOf(new_value)); + setString(4, String.valueOf(old_value)); + return getResult(); + } + + //Used to transfer a single item between owners or equip or vault or bank or inventory + public boolean UPDATE_OWNER(final Item item, int newOwnerID, boolean ownerNPC, boolean ownerPlayer, + boolean ownerAccount, ItemContainerType containerType, int slot) { + + prepareCallable("CALL `item_TRANSFER_OWNER`(?, ?, ?, ? )"); + setLong(1, (long) item.getObjectUUID()); + if (newOwnerID != 0) + setLong(2, (long) newOwnerID); + else + setNULL(2, java.sql.Types.BIGINT); + + switch (containerType) { + case INVENTORY: + setString(3, "inventory"); + break; + case EQUIPPED: + setString(3, "equip"); + break; + case BANK: + setString(3, "bank"); + break; + case VAULT: + setString(3, "vault"); + break; + case FORGE: + setString(3, "forge"); + break; + default: + setString(3, "none"); //Shouldn't be here + break; + } + setInt(4, slot); + return worked(); + } + + public boolean SET_DURABILITY(final Item item, int value) { + prepareCallable("UPDATE `obj_item` SET `item_durabilityCurrent`=? WHERE `UID`=? AND `item_durabilityCurrent`=?"); + setInt(1, value); + setLong(2, (long) item.getObjectUUID()); + setInt(3, (int) item.getDurabilityCurrent()); + return (executeUpdate() != 0); + + } + + //Update an item except ownership + public boolean UPDATE_DATABASE(final Item item) { + prepareCallable("UPDATE `obj_item` SET `item_itembaseID`=?, `item_chargesRemaining`=?, `item_durabilityCurrent`=?, `item_durabilityMax`=?, `item_numberOfItems`=? WHERE `UID`=?"); + setInt(1, item.getItemBaseID()); + setInt(2, item.getChargesRemaining()); + setInt(3, item.getDurabilityCurrent()); + setInt(4, item.getDurabilityMax()); + setInt(5, item.getNumOfItems()); + setLong(6, (long) item.getObjectUUID()); + return (executeUpdate() != 0); + } + + public boolean UPDATE_ROLL_COMPLETE(final Item item) { + prepareCallable("UPDATE `obj_item` SET `item_container` = ?, `item_dateToUpgrade` = ? WHERE `UID` = ?"); + setString(1, "forge"); + setLong(2, 0L); + setLong(3, (long) item.getObjectUUID()); + return (executeUpdate() != 0); + } + + public boolean SET_DATE_TO_UPGRADE(final Item item, long date) { + prepareCallable("UPDATE `obj_item` SET `item_dateToUPGRADE` = ? WHERE `UID` = ?"); + setLong(1, date); + setLong(2, (long) item.getObjectUUID()); + return (executeUpdate() != 0); + } + + public boolean UPDATE_FORGE_TO_INVENTORY(final Item item) { + prepareCallable("UPDATE `obj_item` SET `item_container` = ? WHERE `UID` = ? AND `item_container` = 'forge';"); + setString(1, "inventory"); + setLong(2, (long) item.getObjectUUID()); + return (executeUpdate() != 0); + } + + /** + * Attempts to update the quantity of this gold item + * + * @param value New quantity of gold + * @return True on success + */ + public boolean UPDATE_GOLD(final Item item, int value) { + if (item == null) + return false; + return UPDATE_GOLD(item, value, item.getNumOfItems()); + } + + /** + * Attempts to update the quantity of this gold item using CAS + * + * @return True on success + */ + public boolean UPDATE_GOLD(final Item item, int newValue, int oldValue) { + + if (item.getItemBase().getType().equals(ItemType.GOLD) == false) + return false; + + prepareCallable("UPDATE `obj_item` SET `item_numberOfItems`=? WHERE `UID`=?"); + setInt(1, newValue); + setLong(2, (long) item.getObjectUUID()); + return (executeUpdate() != 0); + } + + /** + * Attempts to update the value of two Gold items simultaneously. + * + * @param value New gold quantity for this item + * @param otherGold Other Gold item being modified + * @param valueOtherGold New quantity of gold for other item + * @return True on success + */ + public boolean UPDATE_GOLD(Item gold, int value, Item otherGold, int valueOtherGold) { + + if (gold.getItemBase().getType().equals(ItemType.GOLD) == false) + return false; + + if (otherGold.getItemBase().getType().equals(ItemType.GOLD) == false) + return false; + + int firstOld = gold.getNumOfItems(); + int secondOld = gold.getNumOfItems(); + + prepareCallable("UPDATE `obj_item` SET `item_numberOfItems` = CASE WHEN `UID`=? AND `item_numberOfItems`=? THEN ? WHEN `UID`=? AND `item_numberOfItems`=? THEN ? END WHERE `UID` IN (?, ?);"); + setLong(1, (long) gold.getObjectUUID()); + setInt(2, firstOld); + setInt(3, value); + setLong(4, (long) otherGold.getObjectUUID()); + setInt(5, secondOld); + setInt(6, valueOtherGold); + setLong(7, (long) gold.getObjectUUID()); + setLong(8, (long) otherGold.getObjectUUID()); + return (executeUpdate() != 0); + } + + public boolean UPDATE_REMAINING_CHARGES(final Item item) { + prepareCallable("UPDATE `obj_item` SET `item_chargesRemaining` = ? WHERE `UID` = ?"); + setInt(1, item.getChargesRemaining()); + setLong(2, (long) item.getObjectUUID()); + return (executeUpdate() != 0); + } + + // This is necessary because default number of items is 1. + // When we create gold, we want it to start at 0 quantity. + + public boolean ZERO_ITEM_STACK(Item item) { + prepareCallable("UPDATE `obj_item` SET `item_numberOfItems`=0 WHERE `UID` = ?"); + setLong(1, (long) item.getObjectUUID()); + return (executeUpdate() != 0); + } + + public boolean UPDATE_FLAGS(Item item) { + prepareCallable("UPDATE `obj_item` SET `item_flags`=? WHERE `UID` = ?"); + setInt(1, item.getFlags()); + setLong(2, (long) item.getObjectUUID()); + return (executeUpdate() != 0); + } + + public boolean UPDATE_VALUE(Item item,int value) { + prepareCallable("UPDATE `obj_item` SET `item_value`=? WHERE `UID` = ?"); + setInt(1, value); + setLong(2, (long) item.getObjectUUID()); + return (executeUpdate() != 0); + } + + public boolean UPDATE_FLAGS(Item item, int flags) { + prepareCallable("UPDATE `obj_item` SET `item_flags`=? WHERE `UID` = ?"); + setInt(1, flags); + setLong(2, (long) item.getObjectUUID()); + return (executeUpdate() != 0); + } + + +} diff --git a/src/engine/db/handlers/dbKitHandler.java b/src/engine/db/handlers/dbKitHandler.java new file mode 100644 index 00000000..b558ec82 --- /dev/null +++ b/src/engine/db/handlers/dbKitHandler.java @@ -0,0 +1,36 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.objects.Kit; + +import java.util.ArrayList; + +public class dbKitHandler extends dbHandlerBase { + + public dbKitHandler() { + this.localClass = Kit.class; + this.localObjectType = engine.Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public ArrayList GET_KITS_FOR_RACE_AND_BASECLASS(int raceID, int baseClassID) { + prepareCallable("SELECT vk.* FROM `static_rune_validkit` vk, `static_rune_racebaseclass` rbc WHERE rbc.`RaceID` = ? " + + "&& rbc.`BaseClassID` = ? && rbc.`ID` = vk.`RaceBaseClassesID`"); + setInt(1, raceID); + setInt(2, baseClassID); + return getObjectList(); + } + + public ArrayList GET_ALL_KITS() { + prepareCallable("SELECT * FROM `static_rune_validkit`"); + + return getObjectList(); + } +} diff --git a/src/engine/db/handlers/dbLootTableHandler.java b/src/engine/db/handlers/dbLootTableHandler.java new file mode 100644 index 00000000..cab88c5d --- /dev/null +++ b/src/engine/db/handlers/dbLootTableHandler.java @@ -0,0 +1,233 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.loot.LootGroup; +import engine.loot.LootManager; +import engine.loot.ModifierGroup; +import engine.loot.ModifierTable; +import engine.objects.Item; +import engine.objects.LootTable; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class dbLootTableHandler extends dbHandlerBase { + + public dbLootTableHandler() { + + } + + + public void populateLootGroups() { + int recordsRead = 0; + prepareCallable("SELECT `groupID`, `minRoll`, `maxRoll`, `lootTableID`, `pModTableID`, `sModTableID` FROM `static_lootgroups`"); + + try { + ResultSet rs = executeQuery(); + if (rs != null) + while (rs.next()) { + recordsRead++; + LootTable lootTable = LootTable.getLootGroup(rs.getInt("groupID")); + lootTable.addRow(rs.getFloat("minRoll"), rs.getFloat("maxRoll"), rs.getInt("lootTableID"), rs.getInt("pModTableID"), rs.getInt("sModTableID"), ""); + } + + Logger.info("read: " + recordsRead + " cached: " + LootTable.getLootGroups().size()); + } catch (SQLException e) { + } finally { + closeCallable(); + } + } + + public void populateLootTables() { + int recordsRead = 0; + + prepareCallable("SELECT `lootTable`, `minRoll`, `maxRoll`, `itemBaseUUID`, `minSpawn`, `maxSpawn` FROM `static_loottables`"); + + try { + ResultSet rs = executeQuery(); + if (rs != null) + while (rs.next()) { + recordsRead++; + LootTable lootTable = LootTable.getLootTable(rs.getInt("lootTable")); + lootTable.addRow(rs.getFloat("minRoll"), rs.getFloat("maxRoll"), rs.getInt("itemBaseUUID"), rs.getInt("minSpawn"), rs.getInt("maxSpawn"), ""); + } + + Logger.info("read: " + recordsRead + " cached: " + LootTable.getLootTables().size()); + } catch (SQLException e) { + } finally { + closeCallable(); + } + } + + public void populateModTables() { + + int recordsRead = 0; + + prepareCallable("SELECT `modTable`,`minRoll`,`maxRoll`,`value`,`action` FROM `static_modtables`"); + + try { + ResultSet rs = executeQuery(); + if (rs != null) + while (rs.next()) { + recordsRead++; + LootTable lootTable = LootTable.getModTable(rs.getInt("modTable")); + lootTable.addRow(rs.getFloat("minRoll"), rs.getFloat("maxRoll"), rs.getInt("value"), 0, 0, rs.getString("action")); + } + Logger.info("read: " + recordsRead + " cached: " + LootTable.getModTables().size()); + } catch (SQLException e) { + } finally { + closeCallable(); + } + } + + public void populateModGroups() { + + int recordsRead = 0; + + prepareCallable("SELECT `modGroup`,`minRoll`,`maxRoll`,`subTableID` FROM `static_modgroups`"); + + try { + ResultSet rs = executeQuery(); + if (rs != null) + while (rs.next()) { + recordsRead++; + LootTable lootTable = LootTable.getModGroup(rs.getInt("modGroup")); + lootTable.addRow(rs.getFloat("minRoll"), rs.getFloat("maxRoll"), rs.getInt("subTableID"), 0, 0, ""); + } + Logger.info("read: " + recordsRead + " cached: " + LootTable.getModGroups().size()); + } catch (SQLException e) { + } finally { + closeCallable(); + } + } + + public void LOAD_ENCHANT_VALUES() { + + prepareCallable("SELECT `IDString`, `minMod` FROM `static_power_effectmod` WHERE `modType` = ?"); + setString(1,"Value"); + + try { + ResultSet rs = executeQuery(); + while (rs.next()) { + Item.addEnchantValue(rs.getString("IDString"), rs.getInt("minMod")); + } + } catch (SQLException e) { + Logger.error( e); + } finally { + closeCallable(); + } + } + + public void LOAD_ALL_LOOTGROUPS() { + + LootGroup lootGroup; + int recordsRead = 0; + + prepareCallable("SELECT * FROM static_lootgroups"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + recordsRead++; + lootGroup = new LootGroup(rs); + LootManager.addLootGroup(lootGroup); + } + + Logger.info( "read: " + recordsRead); + + } catch (SQLException e) { + Logger.error( e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + } + + public void LOAD_ALL_LOOTTABLES() { + + engine.loot.LootTable lootTable; + int recordsRead = 0; + + prepareCallable("SELECT * FROM static_loottables"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + recordsRead++; + lootTable = new engine.loot.LootTable(rs); + LootManager.addLootTable(lootTable); + } + + Logger.info("read: " + recordsRead); + + } catch (SQLException e) { + Logger.error( e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + } + + public void LOAD_ALL_MODGROUPS() { + + ModifierGroup modGroup; + int recordsRead = 0; + + prepareCallable("SELECT * FROM static_modgroups"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + recordsRead++; + modGroup = new ModifierGroup(rs); + LootManager.addModifierGroup(modGroup); + } + + Logger.info( "read: " + recordsRead); + + } catch (SQLException e) { + Logger.error(e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + } + + public void LOAD_ALL_MODTABLES() { + + ModifierTable modTable; + int recordsRead = 0; + + prepareCallable("SELECT * FROM static_modtables"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + recordsRead++; + modTable = new ModifierTable(rs); + LootManager.addModifierTable(modTable); + } + + Logger.info( "read: " + recordsRead); + + } catch (SQLException e) { + Logger.error( e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + } +} diff --git a/src/engine/db/handlers/dbMenuHandler.java b/src/engine/db/handlers/dbMenuHandler.java new file mode 100644 index 00000000..426f4c4a --- /dev/null +++ b/src/engine/db/handlers/dbMenuHandler.java @@ -0,0 +1,28 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.objects.MenuOption; + +import java.util.ArrayList; + +public class dbMenuHandler extends dbHandlerBase { + + public dbMenuHandler() { + this.localClass = MenuOption.class; + this.localObjectType = engine.Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public ArrayList GET_MENU_OPTIONS(final int id) { + prepareCallable("SELECT * FROM `static_npc_menuoption` WHERE menuID = ?"); + setInt(1, id); + return getObjectList(); + } +} diff --git a/src/engine/db/handlers/dbMineHandler.java b/src/engine/db/handlers/dbMineHandler.java new file mode 100644 index 00000000..acc8384b --- /dev/null +++ b/src/engine/db/handlers/dbMineHandler.java @@ -0,0 +1,117 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.Enum; +import engine.gameManager.DbManager; +import engine.objects.Mine; +import engine.objects.MineProduction; +import engine.objects.Resource; + +import java.time.LocalDateTime; +import java.util.ArrayList; + +public class dbMineHandler extends dbHandlerBase { + + public dbMineHandler() { + this.localClass = Mine.class; + this.localObjectType = Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public Mine GET_MINE(int id) { + + if (id == 0) + return null; + + Mine mine = (Mine) DbManager.getFromCache(Enum.GameObjectType.Mine, id); + if (mine != null) + return mine; + + prepareCallable("SELECT `obj_building`.*, `object`.`parent` FROM `object` INNER JOIN `obj_building` ON `obj_building`.`UID` = `object`.`UID` WHERE `object`.`UID` = ?;"); + + setLong(1, (long) id); + return (Mine) getObjectSingle(id); + + } + + public ArrayList GET_ALL_MINES_FOR_SERVER() { + prepareCallable("SELECT `obj_mine`.*, `object`.`parent` FROM `object` INNER JOIN `obj_mine` ON `obj_mine`.`UID` = `object`.`UID`"); + return getObjectList(); + } + + public boolean CHANGE_OWNER(Mine mine, int playerUID) { + prepareCallable("UPDATE `obj_mine` SET `mine_ownerUID`=? WHERE `UID`=?"); + setInt(1, playerUID); + setLong(2, (long) mine.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean CHANGE_RESOURCE(Mine mine, Resource resource) { + prepareCallable("UPDATE `obj_mine` SET `mine_resource`=? WHERE `UID`=?"); + setString(1, resource.name()); + setLong(2, (long) mine.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean CHANGE_TYPE(Mine mine, MineProduction productionType) { + prepareCallable("UPDATE `obj_mine` SET `mine_type`=? WHERE `UID`=?"); + setString(1, productionType.name()); + setLong(2, (long) mine.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean CHANGE_MINE_TIME(Mine mine, LocalDateTime mineOpenTime) { + prepareCallable("UPDATE `obj_mine` SET `mine_openDate`=? WHERE `UID`=?"); + setLocalDateTime(1, mineOpenTime); + setLong(2, (long) mine.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean SET_FLAGS(Mine mine, int newFlags) { + prepareCallable("UPDATE `obj_mine` SET `flags`=? WHERE `UID`=?"); + setInt(1, newFlags); + setLong(2, (long) mine.getObjectUUID()); + return (executeUpdate() > 0); + } + + public String SET_PROPERTY(final Mine m, String name, Object new_value) { + prepareCallable("CALL mine_SETPROP(?,?,?)"); + setLong(1, (long) m.getObjectUUID()); + setString(2, name); + setString(3, String.valueOf(new_value)); + return getResult(); + } + + // Advance all the mine windows respective to the current day + // at boot time. This ensures that mines always go live + // no matter what date in the database + + public String SET_PROPERTY(final Mine m, String name, Object new_value, Object old_value) { + prepareCallable("CALL mine_GETSETPROP(?,?,?,?)"); + setLong(1, (long) m.getObjectUUID()); + setString(2, name); + setString(3, String.valueOf(new_value)); + setString(4, String.valueOf(old_value)); + return getResult(); + } + +} diff --git a/src/engine/db/handlers/dbMobBaseHandler.java b/src/engine/db/handlers/dbMobBaseHandler.java new file mode 100644 index 00000000..33f98990 --- /dev/null +++ b/src/engine/db/handlers/dbMobBaseHandler.java @@ -0,0 +1,351 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.Enum.GameObjectType; +import engine.gameManager.DbManager; +import engine.objects.*; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; + +public class dbMobBaseHandler extends dbHandlerBase { + + public dbMobBaseHandler() { + this.localClass = MobBase.class; + this.localObjectType = engine.Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public MobBase GET_MOBBASE(int id, boolean forceDB) { + + + if (id == 0) + return null; + + MobBase mobBase = (MobBase) DbManager.getFromCache(GameObjectType.MobBase, id); + + if ( mobBase != null) + return mobBase; + + prepareCallable("SELECT * FROM `static_npc_mobbase` WHERE `ID`=?"); + setInt(1, id); + return (MobBase) getObjectSingle(id, forceDB, true); + } + + public ArrayList GET_ALL_MOBBASES() { + prepareCallable("SELECT * FROM `static_npc_mobbase`;"); + return getObjectList(); + } + + public void SET_AI_DEFAULTS() { + prepareCallable("SELECT * FROM `static_ai_defaults`"); + try { + ResultSet rs = executeQuery(); + while (rs.next()) { + MBServerStatics.AI_BASE_AGGRO_RANGE = rs.getInt("aggro_range"); + MBServerStatics.AI_PATROL_DIVISOR = rs.getInt("patrol_chance"); + MBServerStatics.AI_DROP_AGGRO_RANGE = rs.getInt("drop_aggro_range"); + MBServerStatics.AI_POWER_DIVISOR = rs.getInt("cast_chance"); + MBServerStatics.AI_RECALL_RANGE = rs.getInt("recall_range"); + MBServerStatics.AI_PET_HEEL_DISTANCE = rs.getInt("pet_heel_distance"); + } + rs.close(); + } catch (SQLException e) { + Logger.error( e.getMessage()); + } finally { + closeCallable(); + } + + } + + public boolean UPDATE_AI_DEFAULTS() { + prepareCallable("UPDATE `static_ai_defaults` SET `aggro_range` = ?,`patrol_chance`= ?,`drop_aggro_range`= ?,`cast_chance`= ?,`recall_range`= ? WHERE `ID` = 1"); + setInt(1, MBServerStatics.AI_BASE_AGGRO_RANGE); + setInt(2, MBServerStatics.AI_PATROL_DIVISOR); + setInt(3, MBServerStatics.AI_DROP_AGGRO_RANGE); + setInt(4, MBServerStatics.AI_POWER_DIVISOR); + setInt(5, MBServerStatics.AI_RECALL_RANGE); + return (executeUpdate() > 0); + + } + + public boolean UPDATE_FLAGS(int mobBaseID, long flags) { + prepareCallable("UPDATE `static_npc_mobbase` SET `flags` = ? WHERE `ID` = ?"); + setLong(1, flags); + setInt(2, mobBaseID); + return (executeUpdate() > 0); + + } + + public HashMap LOAD_STATIC_POWERS(int mobBaseUUID) { + HashMap powersList = new HashMap<>(); + prepareCallable("SELECT * FROM `static_npc_mobbase_powers` WHERE `mobbaseUUID`=?"); + setInt(1, mobBaseUUID); + try { + ResultSet rs = executeQuery(); + while (rs.next()) { + + powersList.put(rs.getInt("token"), rs.getInt("rank")); + } + rs.close(); + } catch (SQLException e) { + Logger.error( e.getMessage()); + } finally { + closeCallable(); + } + return powersList; + + } + + public ArrayList LOAD_STATIC_EFFECTS(int mobBaseUUID) { + ArrayList effectsList = new ArrayList<>(); + + prepareCallable("SELECT * FROM `static_npc_mobbase_effects` WHERE `mobbaseUUID` = ?"); + setInt(1, mobBaseUUID); + + try { + ResultSet rs = executeQuery(); + while (rs.next()) { + MobBaseEffects mbs = new MobBaseEffects(rs); + effectsList.add(mbs); + } + rs.close(); + } catch (SQLException e) { + Logger.error( e.getMessage()); + } finally { + closeCallable(); + } + return effectsList; + + } + + public ArrayList GET_RUNEBASE_EFFECTS(int runeID) { + ArrayList effectsList = new ArrayList<>(); + prepareCallable("SELECT * FROM `static_npc_mobbase_effects` WHERE `mobbaseUUID` = ?"); + setInt(1, runeID); + + try { + ResultSet rs = executeQuery(); + while (rs.next()) { + + MobBaseEffects mbs = new MobBaseEffects(rs); + effectsList.add(mbs); + } + rs.close(); + } catch (SQLException e) { + Logger.error (e.getMessage()); + } finally { + closeCallable(); + } + + return effectsList; + + } + + public MobBaseStats LOAD_STATS(int mobBaseUUID) { + MobBaseStats mbs = MobBaseStats.GetGenericStats(); + + prepareCallable("SELECT * FROM `static_npc_mobbase_stats` WHERE `mobbaseUUID` = ?"); + setInt(1, mobBaseUUID); + try { + ResultSet rs = executeQuery(); + while (rs.next()) { + + mbs = new MobBaseStats(rs); + } + + } catch (SQLException e) { + Logger.error(e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + return mbs; + + } + + public ArrayList LOAD_RUNES_FOR_MOBBASE(int mobBaseUUID) { + + ArrayList runes = new ArrayList<>(); + prepareCallable("SELECT * FROM `static_npc_mobbase_runes` WHERE `mobbaseUUID` = ?"); + setInt(1, mobBaseUUID); + try { + ResultSet rs = executeQuery(); + while (rs.next()) { + int runeID = rs.getInt("runeID"); + RuneBase rune = RuneBase.getRuneBase(runeID); + runes.add(rune); + } + + } catch (SQLException e) { + Logger.error(e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + return runes; + + } + + public boolean ADD_MOBBASE_EFFECT(int mobBaseUUID, int token, int rank, int reqLvl) { + prepareCallable("INSERT INTO `static_npc_mobbase_effects` (`mobbaseUUID`, `token`, `rank`, `reqLvl`) VALUES (?, ?, ?, ?);"); + setInt(1, mobBaseUUID); + setInt(2, token); + setInt(3, rank); + setInt(4, reqLvl); + return (executeUpdate() > 0); + } + + public boolean ADD_MOBBASE_POWER(int mobBaseUUID, int token, int rank) { + prepareCallable("INSERT INTO `static_npc_mobbase_powers` (`mobbaseUUID`, `token`, `rank`) VALUES (?, ?, ?);"); + setInt(1, mobBaseUUID); + setInt(2, token); + setInt(3, rank); + return (executeUpdate() > 0); + } + + public boolean UPDATE_SKILLS(int ID, int skillsID) { + prepareCallable("UPDATE `static_npc_mobbase` SET `baseSkills`=? WHERE `ID`=?;"); + setInt(1, skillsID); + setInt(2, ID); + return (executeUpdate() > 0); + } + + public boolean ADD_MOBBASE_RUNE(int mobBaseUUID, int runeID) { + prepareCallable("INSERT INTO `static_npc_mobbase_runes` (`mobbaseUUID`, `runeID`) VALUES (?, ?);"); + setInt(1, mobBaseUUID); + setInt(2, runeID); + return (executeUpdate() > 0); + } + + public MobBase COPY_MOBBASE(MobBase toAdd, String name) { + prepareCallable("INSERT INTO `static_npc_mobbase` (`loadID`, `lootTableID`, `name`, `level`, `health`, `atr`, `defense`, `minDmg`,`maxDmg`, `goldMod`, `seeInvis`, `flags`, `noaggro`, `spawntime`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); + setInt(1, toAdd.getLoadID()); + setInt(2, toAdd.getLootTable()); + setString(3, (name.length() > 0) ? name : toAdd.getFirstName()); + setInt(4, toAdd.getLevel()); + setFloat(5, toAdd.getHealthMax()); + setInt(5, toAdd.getAtr()); + setInt(6, toAdd.getDefense()); + setFloat(7, toAdd.getMinDmg()); + setFloat(8, toAdd.getMaxDmg()); + setInt(9, toAdd.getGoldMod()); + setInt(10, toAdd.getSeeInvis()); + setLong(11, toAdd.getFlags().toLong()); + setLong(12, toAdd.getNoAggro().toLong()); + setInt(13, toAdd.getSpawnTime()); + int objectUUID = insertGetUUID(); + if (objectUUID > 0) + return GET_MOBBASE(objectUUID, true); + return null; + } + + public boolean RENAME_MOBBASE(int ID, String newName) { + prepareCallable("UPDATE `static_npc_mobbase` SET `name`=? WHERE `ID`=?;"); + setString(1, newName); + setInt(2, ID); + return (executeUpdate() > 0); + } + + + public void LOAD_ALL_MOBBASE_LOOT(int mobBaseID) { + + if (mobBaseID == 0) + return; + ArrayList mobLootList = new ArrayList<>(); + prepareCallable("SELECT * FROM `static_mob_loottable` WHERE `mobBaseID` = ?"); + setInt(1,mobBaseID); + + try { + ResultSet rs = executeQuery(); + + //shrines cached in rs for easy cache on creation. + while (rs.next()) { + + MobLootBase mobLootBase = new MobLootBase(rs); + mobLootList.add(mobLootBase); + + } + + MobLootBase.MobLootSet.put(mobBaseID, mobLootList); + + } catch (SQLException e) { + Logger.error( e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + + } + + public void LOAD_ALL_MOBBASE_SPEEDS(MobBase mobBase) { + + if (mobBase.getLoadID() == 0) + return; + ArrayList mobLootList = new ArrayList<>(); + prepareCallable("SELECT * FROM `static_npc_mobbase_race` WHERE `mobbaseID` = ?"); + setInt(1,mobBase.getLoadID()); + + try { + ResultSet rs = executeQuery(); + + //shrines cached in rs for easy cache on creation. + while (rs.next()) { + float walk = rs.getFloat("walkStandard"); + float walkCombat = rs.getFloat("walkCombat"); + float run = rs.getFloat("runStandard"); + float runCombat = rs.getFloat("runCombat"); + mobBase.updateSpeeds(walk, walkCombat, run, runCombat); + } + + + } catch (SQLException e) { + Logger.error(e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + + } + + public HashMap LOAD_GOLD_FOR_MOBBASE() { + + HashMap goldSets; + MobbaseGoldEntry goldSetEntry; + int mobbaseID; + + goldSets = new HashMap<>(); + int recordsRead = 0; + + prepareCallable("SELECT * FROM static_npc_mobbase_gold"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + recordsRead++; + + mobbaseID = rs.getInt("mobbaseID"); + goldSetEntry = new MobbaseGoldEntry(rs); + goldSets.put(mobbaseID, goldSetEntry); + + } + + Logger.info("read: " + recordsRead + " cached: " + goldSets.size()); + + } catch (SQLException e) { + Logger.error(e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + return goldSets; + } +} diff --git a/src/engine/db/handlers/dbMobHandler.java b/src/engine/db/handlers/dbMobHandler.java new file mode 100644 index 00000000..c26312db --- /dev/null +++ b/src/engine/db/handlers/dbMobHandler.java @@ -0,0 +1,316 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.ai.MobileFSM.STATE; +import engine.math.Vector3fImmutable; +import engine.objects.Mob; +import engine.objects.PlayerCharacter; +import engine.objects.Zone; +import engine.server.MBServerStatics; +import engine.server.world.WorldServer; +import org.joda.time.DateTime; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; + +public class dbMobHandler extends dbHandlerBase { + + public dbMobHandler() { + this.localClass = Mob.class; + this.localObjectType = engine.Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public Mob ADD_MOB(Mob toAdd, boolean isMob) + { + prepareCallable("CALL `mob_CREATE`(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); + setLong(1, toAdd.getParentZoneID()); + setInt(2, toAdd.getMobBaseID()); + setInt(3, toAdd.getGuildUUID()); + setFloat(4, toAdd.getSpawnX()); + setFloat(5, toAdd.getSpawnY()); + setFloat(6, toAdd.getSpawnZ()); + setInt(7, 0); + setFloat(8, toAdd.getSpawnRadius()); + setInt(9, toAdd.getTrueSpawnTime()); + if (toAdd.getContract() != null) + setInt(10, toAdd.getContract().getContractID()); + else + setInt(10, 0); + setInt(11, toAdd.getBuildingID()); + setInt(12, toAdd.getLevel()); + int objectUUID = (int) getUUID(); + if (objectUUID > 0) + return GET_MOB(objectUUID); + return null; + } + + public Mob ADD_SIEGE_MOB(Mob toAdd, boolean isMob) + { + prepareCallable("CALL `mob_SIEGECREATE`(?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); + setLong(1, toAdd.getParentZoneID()); + setInt(2, toAdd.getMobBaseID()); + setInt(3, toAdd.getGuildUUID()); + setFloat(4, toAdd.getSpawnX()); + setFloat(5, toAdd.getSpawnY()); + setFloat(6, toAdd.getSpawnZ()); + setInt(7,0); + setFloat(8, toAdd.getSpawnRadius()); + setInt(9, toAdd.getTrueSpawnTime()); + setInt(10, toAdd.getBuildingID()); + + int objectUUID = (int) getUUID(); + if (objectUUID > 0) + return GET_MOB(objectUUID); + return null; + } + + public boolean updateUpgradeTime(Mob mob, DateTime upgradeDateTime) { + + + + try { + + prepareCallable("UPDATE obj_mob SET upgradeDate=? " + + "WHERE UID = ?"); + + if (upgradeDateTime == null) + setNULL(1, java.sql.Types.DATE); + else + setTimeStamp(1, upgradeDateTime.getMillis()); + + setInt(2, mob.getObjectUUID()); + executeUpdate(); + } catch (Exception e) { + Logger.error("Mob.updateUpgradeTime", "UUID: " + mob.getObjectUUID()); + return false; + } + return true; + } + + public int DELETE_MOB(final Mob mob) { + prepareCallable("DELETE FROM `object` WHERE `UID` = ?"); + setLong(1, mob.getDBID()); + return executeUpdate(); + } + + public void LOAD_PATROL_POINTS(Mob captain) { + + + + prepareCallable("SELECT * FROM `dyn_guards` WHERE `captainUID` = ?"); + setInt(1,captain.getObjectUUID()); + + try { + ResultSet rs = executeQuery(); + + //shrines cached in rs for easy cache on creation. + while (rs.next()) { + int mobBaseID = rs.getInt("mobBaseID"); + String name = rs.getString("name"); + Mob toCreate = captain.createGuardMob(mobBaseID, captain.getGuild(), captain.getParentZone(), captain.getBuilding().getLoc(), captain.getLevel(),name); + if (toCreate == null) + return; + + // toCreate.despawn(); + if (toCreate != null) { + + toCreate.setTimeToSpawnSiege(System.currentTimeMillis() + MBServerStatics.FIFTEEN_MINUTES); + toCreate.setDeathTime(System.currentTimeMillis()); + toCreate.setState(STATE.Respawn); + + } + } + + + } catch (SQLException e) { + Logger.error( e.toString()); + } finally { + closeCallable(); + } + + + + } + + public boolean ADD_TO_GUARDS(final long captainUID, final int mobBaseID, final String name, final int slot) { + prepareCallable("INSERT INTO `dyn_guards` (`captainUID`, `mobBaseID`,`name`, `slot`) VALUES (?,?,?,?)"); + setLong(1, captainUID); + setInt(2, mobBaseID); + setString(3, name); + setInt(4, slot); + return (executeUpdate() > 0); + } + + public boolean REMOVE_FROM_GUARDS(final long captainUID, final int mobBaseID, final int slot) { + prepareCallable("DELETE FROM `dyn_guards` WHERE `captainUID`=? AND `mobBaseID`=? AND `slot` =?"); + setLong(1, captainUID); + setInt(2, mobBaseID); + setInt(3,slot); + return (executeUpdate() > 0); + } + + + public ArrayList GET_ALL_MOBS_FOR_ZONE(Zone zone) { + prepareCallable("SELECT `obj_mob`.*, `object`.`parent` FROM `object` INNER JOIN `obj_mob` ON `obj_mob`.`UID` = `object`.`UID` WHERE `object`.`parent` = ?;"); + setLong(1, zone.getObjectUUID()); + return getLargeObjectList(); + } + + public ArrayList GET_ALL_MOBS_FOR_BUILDING(int buildingID) { + prepareCallable("SELECT * FROM `obj_mob` WHERE `mob_buildingID` = ?"); + setInt(1, buildingID); + return getObjectList(); + } + + public ArrayList GET_ALL_MOBS() { + prepareCallable("SELECT `obj_mob`.*, `object`.`parent` FROM `object` INNER JOIN `obj_mob` ON `obj_mob`.`UID` = `object`.`UID`;"); + return getObjectList(); + } + + public Mob GET_MOB(final int objectUUID) { + prepareCallable("SELECT `obj_mob`.*, `object`.`parent` FROM `object` INNER JOIN `obj_mob` ON `obj_mob`.`UID` = `object`.`UID` WHERE `object`.`UID` = ?;"); + setLong(1, objectUUID); + return (Mob) getObjectSingle(objectUUID); + } + + public int MOVE_MOB(long mobID, long parentID, float locX, float locY, float locZ) { + prepareCallable("UPDATE `object` INNER JOIN `obj_mob` On `object`.`UID` = `obj_mob`.`UID` SET `object`.`parent`=?, `obj_mob`.`mob_spawnX`=?, `obj_mob`.`mob_spawnY`=?, `obj_mob`.`mob_spawnZ`=? WHERE `obj_mob`.`UID`=?;"); + setLong(1, parentID); + setFloat(2, locX); + setFloat(3, locY); + setFloat(4, locZ); + setLong(5, mobID); + return executeUpdate(); + } + + public boolean UPDATE_MOB_BUILDING(int buildingID, int mobID) { + prepareCallable("UPDATE `object` INNER JOIN `obj_mob` On `object`.`UID` = `obj_mob`.`UID` SET `obj_mob`.`mob_buildingID`=? WHERE `obj_mob`.`UID`=?;"); + setInt(1, buildingID); + setInt(2, mobID); + return (executeUpdate() > 0); + } + + public String SET_PROPERTY(final Mob m, String name, Object new_value) { + prepareCallable("CALL mob_SETPROP(?,?,?)"); + setLong(1, m.getObjectUUID()); + setString(2, name); + setString(3, String.valueOf(new_value)); + return getResult(); + } + + public String SET_PROPERTY(final Mob m, String name, Object new_value, Object old_value) { + prepareCallable("CALL mob_GETSETPROP(?,?,?,?)"); + setLong(1, m.getObjectUUID()); + setString(2, name); + setString(3, String.valueOf(new_value)); + setString(4, String.valueOf(old_value)); + return getResult(); + } + + + public static boolean COPY_ZONE_MOBILES(PlayerCharacter pc, Zone sourceZone, Zone targetZone) { + + ArrayList sourceMobList; + Vector3fImmutable worldDelta; + Mob newMobile; + + // Sanity check. Can't copy a non existent zone + + if ((sourceZone == null) || (targetZone == null)) + return false; + + // Generate collections for all buildings in each zone + + + for (Mob mobile : sourceZone.zoneMobSet) { + + // Calculate world coordinate offset between zones + + worldDelta = new Vector3fImmutable(targetZone.getAbsX(), targetZone.getAbsY(), targetZone.getAbsZ()); + worldDelta = worldDelta.subtract(new Vector3fImmutable(sourceZone.getAbsX(), sourceZone.getAbsY(), sourceZone.getAbsZ())); + + newMobile = Mob.createMob(mobile.getLoadID(), + mobile.getLoc().add(worldDelta), null, true, targetZone, mobile.getBuilding(), 0); + + if (newMobile != null) { + newMobile.updateDatabase(); + } + + } + + return true; + } + + + public void LOAD_RUNES_FOR_FIDELITY_MOBS() { + + + + + + prepareCallable("SELECT static_zone_npc.npcID,static_zone_npc.loadNum, static_zone_npc.classID, static_zone_npc.professionID, static_zone_npc.extraRune, static_zone_npc.extraRune2 FROM static_zone_npc ; "); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + + int loadNum = rs.getInt("loadNum"); + int fidelityID = rs.getInt("npcID"); + int classID = rs.getInt("classID"); + int professionID = rs.getInt("professionID"); + int extraRune = rs.getInt("extraRune"); + int extraRune2 = rs.getInt("extraRune2"); + + if (WorldServer.ZoneFidelityMobRunes.get(loadNum) == null) + WorldServer.ZoneFidelityMobRunes.put(loadNum, new HashMap<>()); + ArrayList runeList; + if (WorldServer.ZoneFidelityMobRunes.get(loadNum).get(fidelityID) == null){ + runeList = new ArrayList<>(4); + }else + runeList = WorldServer.ZoneFidelityMobRunes.get(loadNum).get(fidelityID); + + + + if (classID != 0) + runeList.add(classID); + if (professionID != 0) + runeList.add(professionID); + if(extraRune != 0) + runeList.add(extraRune); + + if (extraRune2 != 0) + runeList.add(extraRune2); + + WorldServer.ZoneFidelityMobRunes.get(loadNum).put(fidelityID, runeList); + + + } + + rs.close(); + + + + } catch (SQLException e) { + Logger.error( e.toString()); + } finally { + closeCallable(); + + } + + } + + +} diff --git a/src/engine/db/handlers/dbNPCHandler.java b/src/engine/db/handlers/dbNPCHandler.java new file mode 100644 index 00000000..cc4623e9 --- /dev/null +++ b/src/engine/db/handlers/dbNPCHandler.java @@ -0,0 +1,397 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.Enum.ProfitType; +import engine.objects.*; +import org.joda.time.DateTime; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; + +public class dbNPCHandler extends dbHandlerBase { + + public dbNPCHandler() { + this.localClass = NPC.class; + this.localObjectType = engine.Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public NPC ADD_NPC(NPC toAdd, boolean isMob) { + prepareCallable("CALL `npc_CREATE`(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); + setLong(1, toAdd.getParentZoneID()); + setString(2, toAdd.getName()); + setInt(3, toAdd.getContractID()); + setInt(4, toAdd.getGuildUUID()); + setFloat(5, toAdd.getSpawnX()); + setFloat(6, toAdd.getSpawnY()); + setFloat(7, toAdd.getSpawnZ()); + setInt(8, toAdd.getLevel()); + setFloat(9, toAdd.getBuyPercent()); + setFloat(10, toAdd.getSellPercent()); + if (toAdd.getBuilding() != null) { + setInt(11, toAdd.getBuilding().getObjectUUID()); + } else { + setInt(11, 0); + } + + int objectUUID = (int) getUUID(); + if (objectUUID > 0) { + return GET_NPC(objectUUID); + } + return null; + } + + public int DELETE_NPC(final NPC npc) { + if (npc.isStatic()) { + return DELETE_STATIC_NPC(npc); + } + + npc.removeFromZone(); + prepareCallable("DELETE FROM `object` WHERE `UID` = ?"); + setLong(1, (long) npc.getDBID()); + return executeUpdate(); + } + + private int DELETE_STATIC_NPC(final NPC npc) { + npc.removeFromZone(); + prepareCallable("DELETE FROM `_init_npc` WHERE `ID` = ?"); + setInt(1, npc.getDBID()); + return executeUpdate(); + } + + public ArrayList GET_ALL_NPCS_FOR_ZONE(Zone zone) { + prepareCallable("SELECT `obj_npc`.*, `object`.`parent` FROM `object` INNER JOIN `obj_npc` ON `obj_npc`.`UID` = `object`.`UID` WHERE `object`.`parent` = ?;"); + setLong(1, (long) zone.getObjectUUID()); + return getLargeObjectList(); + } + + public ArrayList GET_ALL_NPCS() { + prepareCallable("SELECT `obj_npc`.*, `object`.`parent` FROM `object` INNER JOIN `obj_npc` ON `obj_npc`.`UID` = `object`.`UID`;"); + + return getObjectList(); + } + + public ArrayList GET_NPCS_BY_BUILDING(final int buildingID) { + prepareCallable("SELECT `obj_npc`.*, `object`.`parent` FROM `obj_npc` INNER JOIN `object` ON `obj_npc`.`UID` = `object`.`UID` WHERE `npc_buildingID` = ? LIMIT 3"); + setInt(1, buildingID); + return getObjectList(); + } + + public NPC GET_NPC(final int objectUUID) { + prepareCallable("SELECT `obj_npc`.*, `object`.`parent` FROM `object` INNER JOIN `obj_npc` ON `obj_npc`.`UID` = `object`.`UID` WHERE `object`.`UID` = ?;"); + setLong(1, (long) objectUUID); + return (NPC) getObjectSingle(objectUUID); + } + + public int MOVE_NPC(long npcID, long parentID, float locX, float locY, float locZ) { + prepareCallable("UPDATE `object` INNER JOIN `obj_npc` On `object`.`UID` = `obj_npc`.`UID` SET `object`.`parent`=?, `obj_npc`.`npc_spawnX`=?, `obj_npc`.`npc_spawnY`=?, `obj_npc`.`npc_spawnZ`=? WHERE `obj_npc`.`UID`=?;"); + setLong(1, parentID); + setFloat(2, locX); + setFloat(3, locY); + setFloat(4, locZ); + setLong(5, npcID); + return executeUpdate(); + } + + + public String SET_PROPERTY(final NPC n, String name, Object new_value) { + prepareCallable("CALL npc_SETPROP(?,?,?)"); + setLong(1, (long) n.getDBID()); + setString(2, name); + setString(3, String.valueOf(new_value)); + return getResult(); + } + + public String SET_PROPERTY(final NPC n, String name, Object new_value, Object old_value) { + prepareCallable("CALL npc_GETSETPROP(?,?,?,?)"); + setLong(1, (long) n.getDBID()); + setString(2, name); + setString(3, String.valueOf(new_value)); + setString(4, String.valueOf(old_value)); + return getResult(); + } + + public void updateDatabase(final NPC npc) { + prepareCallable("UPDATE obj_npc SET npc_name=?, npc_contractID=?, npc_typeID=?, npc_guildID=?," + + " npc_spawnX=?, npc_spawnY=?, npc_spawnZ=?, npc_level=? ," + + " npc_buyPercent=?, npc_sellPercent=?, npc_buildingID=? WHERE UID = ?"); + setString(1, npc.getName()); + setInt(2, (npc.getContract() != null) ? npc.getContract().getObjectUUID() : 0); + setInt(3, 0); + setInt(4, (npc.getGuild() != null) ? npc.getGuild().getObjectUUID() : 0); + setFloat(5, npc.getBindLoc().x); + setFloat(6, npc.getBindLoc().y); + setFloat(7, npc.getBindLoc().z); + setShort(8, npc.getLevel()); + setFloat(9, npc.getBuyPercent()); + setFloat(10, npc.getSellPercent()); + setInt(11, (npc.getBuilding() != null) ? npc.getBuilding().getObjectUUID() : 0); + setInt(12, npc.getDBID()); + executeUpdate(); + } + + public boolean updateUpgradeTime(NPC npc, DateTime upgradeDateTime) { + + + + try { + + prepareCallable("UPDATE obj_npc SET upgradeDate=? " + + "WHERE UID = ?"); + + if (upgradeDateTime == null) + setNULL(1, java.sql.Types.DATE); + else + setTimeStamp(1, upgradeDateTime.getMillis()); + + setInt(2, npc.getObjectUUID()); + executeUpdate(); + } catch (Exception e) { + Logger.error("UUID: " + npc.getObjectUUID()); + return false; + } + return true; + } + + public boolean UPDATE_BUY_PROFIT(NPC npc,float percent) { + prepareCallable("UPDATE `obj_npc` SET `npc_buyPercent`=? WHERE `UID`=?"); + setFloat(1, percent); + setLong(2, npc.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean UPDATE_SELL_PROFIT(NPC npc,float percent) { + prepareCallable("UPDATE `obj_npc` SET `npc_sellPercent`=? WHERE `UID`=?"); + setFloat(1, percent); + setLong(2, npc.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean UPDATE_SLOT(NPC npc,int slot) { + prepareCallable("UPDATE `obj_npc` SET `npc_slot`=? WHERE `UID`=?"); + setFloat(1, slot); + setLong(2, npc.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean UPDATE_MOBBASE(NPC npc, int mobBaseID) { + prepareCallable("UPDATE `obj_npc` SET `npc_raceID`=? WHERE `UID`=?"); + setLong(1, mobBaseID); + setLong(2, npc.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean UPDATE_EQUIPSET(NPC npc, int equipSetID) { + prepareCallable("UPDATE `obj_npc` SET `equipsetID`=? WHERE `UID`=?"); + setInt(1, equipSetID); + setLong(2, npc.getObjectUUID()); + return (executeUpdate() > 0); + } + + public boolean UPDATE_NAME(NPC npc,String name) { + prepareCallable("UPDATE `obj_npc` SET `npc_name`=? WHERE `UID`=?"); + setString(1, name); + setLong(2, npc.getObjectUUID()); + return (executeUpdate() > 0); + } + + public void LOAD_PIRATE_NAMES() { + + String pirateName; + int mobBase; + int recordsRead = 0; + + prepareCallable("SELECT * FROM static_piratenames"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + recordsRead++; + mobBase = rs.getInt("mobbase"); + pirateName = rs.getString("first_name"); + + // Handle new mobbbase entries + + if (NPC._pirateNames.get(mobBase) == null) { + NPC._pirateNames.putIfAbsent(mobBase, new ArrayList<>()); + } + + // Insert name into proper arraylist + + NPC._pirateNames.get(mobBase).add(pirateName); + + } + + Logger.info("names read: " + recordsRead + " for " + + NPC._pirateNames.size() + " mobBases"); + + } catch (SQLException e) { + Logger.error(e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + } + + public void LOAD_RUNES_FOR_FIDELITY_NPC(NPC npc) { + + + + + + prepareCallable("SELECT static_zone_npc.npcID,static_zone_npc.loadNum, static_zone_npc.classID, static_zone_npc.professionID, static_zone_npc.extraRune, static_zone_npc.extraRune2 FROM static_zone_npc WHERE static_zone_npc.loadNum = ? AND static_zone_npc.npcID = ?"); + setInt(1,npc.getParentZoneID()); + setInt(2, npc.getFidalityID()); + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + + + int classID = rs.getInt("classID"); + int professionID = rs.getInt("professionID"); + int extraRune = rs.getInt("extraRune"); + int extraRune2 = rs.getInt("extraRune2"); + + npc.classID = classID; + npc.professionID = professionID; + npc.extraRune = extraRune; + npc.extraRune2 = extraRune2; + + + + } + + rs.close(); + + + + } catch (SQLException e) { + Logger.error( e.toString()); + } finally { + closeCallable(); + + } + } + + public boolean ADD_TO_PRODUCTION_LIST(final long ID,final long npcUID, final long itemBaseID, DateTime dateTime, String prefix, String suffix, String name, boolean isRandom, int playerID) { + prepareCallable("INSERT INTO `dyn_npc_production` (`ID`,`npcUID`, `itemBaseID`,`dateToUpgrade`, `isRandom`, `prefix`, `suffix`, `name`,`playerID`) VALUES (?,?,?,?,?,?,?,?,?)"); + setLong(1,ID); + setLong(2, npcUID); + setLong(3, itemBaseID); + setTimeStamp(4, dateTime.getMillis()); + setBoolean(5, isRandom); + setString(6, prefix); + setString(7, suffix); + setString(8, name); + setInt(9,playerID); + return (executeUpdate() > 0); + } + + public boolean REMOVE_FROM_PRODUCTION_LIST(final long ID,final long npcUID) { + prepareCallable("DELETE FROM `dyn_npc_production` WHERE `ID`=? AND `npcUID`=?;"); + setLong(1,ID); + setLong(2, npcUID); + return (executeUpdate() > 0); + } + + public boolean UPDATE_ITEM_TO_INVENTORY(final long ID,final long npcUID) { + prepareCallable("UPDATE `dyn_npc_production` SET `inForge`=? WHERE `ID`=? AND `npcUID`=?;"); + setByte(1, (byte)0); + setLong(2, ID); + setLong(3, npcUID); + return (executeUpdate() > 0); + } + + public boolean UPDATE_ITEM_PRICE(final long ID,final long npcUID, int value) { + prepareCallable("UPDATE `dyn_npc_production` SET `value`=? WHERE `ID`=? AND `npcUID`=?;"); + setInt(1, value); + setLong(2, ID); + setLong(3, npcUID); + + return (executeUpdate() > 0); + } + + public boolean UPDATE_ITEM_ID(final long ID,final long npcUID,final long value) { + prepareCallable("UPDATE `dyn_npc_production` SET `ID`=? WHERE `ID`=? AND `npcUID`=? LIMIT 1;"); + setLong(1, value); + setLong(2, ID); + setLong(3, npcUID); + + return (executeUpdate() > 0); + } + + public void LOAD_ALL_ITEMS_TO_PRODUCE(NPC npc) { + + if (npc == null) + return; + + prepareCallable("SELECT * FROM `dyn_npc_production` WHERE `npcUID` = ?"); + setInt(1,npc.getObjectUUID()); + + try { + ResultSet rs = executeQuery(); + + //shrines cached in rs for easy cache on creation. + while (rs.next()) { + ProducedItem producedItem = new ProducedItem(rs); + npc.forgedItems.add(producedItem); + } + + } catch (SQLException e) { + Logger.error(e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + } + + public boolean UPDATE_PROFITS(NPC npc,ProfitType profitType, float value){ + prepareCallable("UPDATE `dyn_npc_profits` SET `" + profitType.dbField + "` = ? WHERE `npcUID`=?"); + setFloat(1, value); + setInt(2, npc.getObjectUUID()); + return (executeUpdate() > 0); + } + + public void LOAD_NPC_PROFITS() { + + HashMap> regions; + NPCProfits npcProfit; + + + prepareCallable("SELECT * FROM dyn_npc_profits"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + + npcProfit = new NPCProfits(rs); + NPCProfits.ProfitCache.put(npcProfit.npcUID, npcProfit); + } + + } catch (SQLException e) { + Logger.error(": " + e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + } + + public boolean CREATE_PROFITS(NPC npc){ + prepareCallable("INSERT INTO `dyn_npc_profits` (`npcUID`) VALUES (?)"); + setLong(1,npc.getObjectUUID()); + return (executeUpdate() > 0); + } +} diff --git a/src/engine/db/handlers/dbPlayerCharacterHandler.java b/src/engine/db/handlers/dbPlayerCharacterHandler.java new file mode 100644 index 00000000..1fb8e394 --- /dev/null +++ b/src/engine/db/handlers/dbPlayerCharacterHandler.java @@ -0,0 +1,402 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.Enum; +import engine.gameManager.DbManager; +import engine.objects.AbstractWorldObject; +import engine.objects.Heraldry; +import engine.objects.PlayerCharacter; +import engine.objects.PlayerFriends; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; + +public class dbPlayerCharacterHandler extends dbHandlerBase { + + public dbPlayerCharacterHandler() { + this.localClass = PlayerCharacter.class; + this.localObjectType = Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public PlayerCharacter ADD_PLAYER_CHARACTER(final PlayerCharacter toAdd) { + if (toAdd.getAccount() == null) { + return null; + } + prepareCallable("CALL `character_CREATE`(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); + setLong(1, toAdd.getAccount().getObjectUUID()); + setString(2, toAdd.getFirstName()); + setString(3, toAdd.getLastName()); + setInt(4, toAdd.getRace().getRaceRuneID()); + setInt(5, toAdd.getBaseClass().getObjectUUID()); + setInt(6, toAdd.getStrMod()); + setInt(7, toAdd.getDexMod()); + setInt(8, toAdd.getConMod()); + setInt(9, toAdd.getIntMod()); + setInt(10, toAdd.getSpiMod()); + setInt(11, toAdd.getExp()); + setInt(12, toAdd.getSkinColor()); + setInt(13, toAdd.getHairColor()); + setByte(14, toAdd.getHairStyle()); + setInt(15, toAdd.getBeardColor()); + setByte(16, toAdd.getBeardStyle()); + + int objectUUID = (int) getUUID(); + if (objectUUID > 0) { + return GET_PLAYER_CHARACTER(objectUUID); + } + return null; + } + + public boolean SET_IGNORE_LIST(int sourceID, int targetID, boolean toIgnore, String charName) { + if (toIgnore) { + //Add to ignore list + prepareCallable("INSERT INTO `dyn_character_ignore` (`accountUID`, `ignoringUID`, `characterName`) VALUES (?, ?, ?)"); + setLong(1, (long) sourceID); + setLong(2, (long) targetID); + setString(3, charName); + return (executeUpdate() > 0); + } else { + //delete from ignore list + prepareCallable("DELETE FROM `dyn_character_ignore` WHERE `accountUID` = ? && `ignoringUID` = ?"); + setLong(1, (long) sourceID); + setLong(2, (long) targetID); + return (executeUpdate() > 0); + } + } + + public static boolean DELETE_CHARACTER_IGNORE(final PlayerCharacter pc, final ArrayList toDelete) { + + return false; + } + + public ArrayList GET_ALL_PLAYERCHARACTERS() { + prepareCallable("SELECT * FROM `obj_character`"); + return getObjectList(); + } + + public ArrayList GET_CHARACTERS_FOR_ACCOUNT(final int id, boolean forceFromDB) { + prepareCallable("SELECT `obj_character`.*, `object`.`parent` FROM `object` INNER JOIN `obj_character` ON `obj_character`.`UID` = `object`.`UID` WHERE `object`.`parent`=? && `obj_character`.`char_isActive`='1';"); + setLong(1, (long) id); + return getObjectList(10, forceFromDB); + } + + public ArrayList GET_CHARACTERS_FOR_ACCOUNT(final int id) { + prepareCallable("SELECT `obj_character`.*, `object`.`parent` FROM `object` INNER JOIN `obj_character` ON `obj_character`.`UID` = `object`.`UID` WHERE `object`.`parent`=? && `obj_character`.`char_isActive`='1';"); + setLong(1, (long) id); + return getObjectList(); + } + + public ArrayList GET_ALL_CHARACTERS() { + prepareCallable("SELECT `obj_character`.*, `object`.`parent` FROM `object` INNER JOIN `obj_character` ON `obj_character`.`UID` = `object`.`UID` WHERE `obj_character`.`char_isActive`='1';"); + return getObjectList(); + } + + /** + * + * getFirstName looks up the first name of a PlayerCharacter by + * first checking the GOM cache and then querying the database. + * PlayerCharacter objects that are not already cached won't be instantiated + * and cached. + * + */ + public String GET_FIRST_NAME(final int objectUUID) { + prepareCallable("SELECT `char_firstname` from `obj_character` WHERE `UID` = ? LIMIT 1"); + setLong(1, (long) objectUUID); + String firstName = ""; + try { + ResultSet rs = executeQuery(); + if (rs.next()) { + firstName = rs.getString("char_firstname"); + } + } catch (SQLException e) { + Logger.error( e); + } finally { + closeCallable(); + } + return firstName; + } + + public ConcurrentHashMap GET_IGNORE_LIST(final int objectUUID, final boolean skipActiveCheck) { + ConcurrentHashMap out = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + prepareCallable("SELECT * FROM `dyn_character_ignore` WHERE `accountUID` = ?;"); + setLong(1, (long) objectUUID); + try { + ResultSet rs = executeQuery(); + while (rs.next()) { + int ignoreCharacterID = rs.getInt("ignoringUID"); + if (ignoreCharacterID == 0) { + continue; + } + String name = rs.getString("characterName"); + out.put(ignoreCharacterID, name); + } + rs.close(); + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode()); + return out; // null to explicitly indicate a problem and prevent data loss + } finally { + closeCallable(); + } + return out; + } + + public PlayerCharacter GET_PLAYER_CHARACTER(final int objectUUID) { + + if (objectUUID == 0) + return null; + + PlayerCharacter pc = (PlayerCharacter) DbManager.getFromCache(Enum.GameObjectType.PlayerCharacter, objectUUID); + if (pc != null) + return pc; + prepareCallable("SELECT `obj_character`.*, `object`.`parent` FROM `object` INNER JOIN `obj_character` ON `obj_character`.`UID` = `object`.`UID` WHERE `object`.`UID` = ?"); + setLong(1, (long) objectUUID); + return (PlayerCharacter) getObjectSingle(objectUUID); + } + + public boolean INSERT_CHARACTER_IGNORE(final PlayerCharacter pc, final ArrayList toAdd) { + boolean allWorked = true; + prepareCallable("INSERT INTO `dyn_character_ignore` (`characterUID`, `ignoringUID`) VALUES (?, ?)"); + setLong(1, (long) pc.getObjectUUID()); + for (int id : toAdd) { + setLong(2, (long) id); + if (executeUpdate(false) == 0) { + allWorked = false; + } + } + closeCallable(); + return allWorked; + } + + public boolean IS_CHARACTER_NAME_UNIQUE(final String firstName) { + boolean unique = true; + prepareCallable("SELECT `char_firstname` FROM `obj_character` WHERE `char_isActive`=1 && `char_firstname`=?"); + setString(1, firstName); + try { + ResultSet rs = executeQuery(); + if (rs.next()) { + unique = false; + } + rs.close(); + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getMessage()); + unique = false; + } finally { + closeCallable(); + } + return unique; + } + + public boolean UPDATE_NAME(String oldFirstName, String newFirstName, String newLastName) { + prepareCallable("UPDATE `obj_character` SET `char_firstname`=?, `char_lastname`=? WHERE `char_firstname`=? AND `char_isActive`='1'"); + setString(1, newFirstName); + setString(2, newLastName); + setString(3, oldFirstName); + return (executeUpdate() != 0); + } + + public boolean SET_DELETED(final PlayerCharacter pc) { + prepareCallable("UPDATE `obj_character` SET `char_isActive`=? WHERE `UID` = ?"); + setBoolean(1, !pc.isDeleted()); + setLong(2, (long) pc.getObjectUUID()); + return (executeUpdate() != 0); + } + public boolean SET_ACTIVE(final PlayerCharacter pc, boolean status) { + prepareCallable("UPDATE `obj_character` SET `char_isActive`=? WHERE `UID` = ?"); + setBoolean(1, status); + setLong(2, (long) pc.getObjectUUID()); + return (executeUpdate() != 0); + } + public boolean SET_BIND_BUILDING(final PlayerCharacter pc, int bindBuildingID) { + prepareCallable("UPDATE `obj_character` SET `char_bindBuilding`=? WHERE `UID` = ?"); + setInt(1, bindBuildingID); + setLong(2, (long) pc.getObjectUUID()); + return (executeUpdate() != 0); + } + + public boolean SET_ANNIVERSERY(final PlayerCharacter pc, boolean flag) { + prepareCallable("UPDATE `obj_character` SET `anniversery`=? WHERE `UID` = ?"); + setBoolean(1, flag); + setLong(2, (long) pc.getObjectUUID()); + return (executeUpdate() != 0); + } + + + public boolean UPDATE_CHARACTER_EXPERIENCE(final PlayerCharacter pc) { + prepareCallable("UPDATE `obj_character` SET `char_experience`=? WHERE `UID` = ?"); + setInt(1, pc.getExp()); + setLong(2, (long) pc.getObjectUUID()); + return (executeUpdate() != 0); + } + + public boolean UPDATE_GUILD(final PlayerCharacter pc, int guildUUID) { + prepareCallable("UPDATE `obj_character` SET `guildUID`=? WHERE `UID` = ?"); + setInt(1, guildUUID); + setLong(2, (long) pc.getObjectUUID()); + return (executeUpdate() != 0); + } + + public boolean UPDATE_CHARACTER_STAT(final PlayerCharacter pc, String stat, short amount) { + prepareCallable("UPDATE `obj_character` SET `" + stat + "`=? WHERE `UID`=?"); + setInt(1, pc.getExp()); + setLong(2, (long) pc.getObjectUUID()); + return (executeUpdate() != 0); + } + + public boolean UPDATE_CHARACTER_STATS(final PlayerCharacter pc) { + prepareCallable("UPDATE `obj_character` SET `char_strMod`=?, `char_dexMod`=?, `char_conMod`=?, `char_intMod`=?, `char_spiMod`=? WHERE `UID`=?"); + setInt(1, pc.getStrMod()); + setInt(2, pc.getDexMod()); + setInt(3, pc.getConMod()); + setInt(4, pc.getIntMod()); + setInt(5, pc.getSpiMod()); + setLong(6, (long) pc.getObjectUUID()); + return (executeUpdate() != 0); + } + + public String SET_PROPERTY(final PlayerCharacter c, String name, Object new_value) { + prepareCallable("CALL character_SETPROP(?,?,?)"); + setLong(1, (long) c.getObjectUUID()); + setString(2, name); + setString(3, String.valueOf(new_value)); + return getResult(); + } + + public String SET_PROPERTY(final PlayerCharacter c, String name, Object new_value, Object old_value) { + prepareCallable("CALL character_GETSETPROP(?,?,?,?)"); + setLong(1, (long) c.getObjectUUID()); + setString(2, name); + setString(3, String.valueOf(new_value)); + setString(4, String.valueOf(old_value)); + return getResult(); + } + + public boolean SET_PROMOTION_CLASS(PlayerCharacter player, int promotionClassID) { + prepareCallable("UPDATE `obj_character` SET `char_promotionClassID`=? WHERE `UID`=?;"); + setInt(1,promotionClassID); + setInt(2, player.getObjectUUID()); + return (executeUpdate() != 0); + } + + public boolean SET_INNERCOUNCIL(PlayerCharacter player, boolean isInnerCouncil) { + prepareCallable("UPDATE `obj_character` SET `guild_isInnerCouncil`=? WHERE `UID`=?;"); + setBoolean(1,isInnerCouncil); + setInt(2, player.getObjectUUID()); + return (executeUpdate() != 0); + } + + public boolean SET_FULL_MEMBER(PlayerCharacter player, boolean isFullMember) { + prepareCallable("UPDATE `obj_character` SET `guild_isFullMember`=? WHERE `UID`=?;"); + setBoolean(1,isFullMember); + setInt(2, player.getObjectUUID()); + return (executeUpdate() != 0); + } + + public boolean SET_TAX_COLLECTOR(PlayerCharacter player, boolean isTaxCollector) { + prepareCallable("UPDATE `obj_character` SET `guild_isTaxCollector`=? WHERE `UID`=?;"); + setBoolean(1,isTaxCollector); + setInt(2, player.getObjectUUID()); + return (executeUpdate() != 0); + } + + public boolean SET_RECRUITER(PlayerCharacter player, boolean isRecruiter) { + prepareCallable("UPDATE `obj_character` SET `guild_isRecruiter`=? WHERE `UID`=?;"); + setBoolean(1,isRecruiter); + setInt(2, player.getObjectUUID()); + return (executeUpdate() != 0); + } + + public boolean SET_GUILD_TITLE(PlayerCharacter player, int title) { + prepareCallable("UPDATE `obj_character` SET `guild_title`=? WHERE `UID`=?;"); + setInt(1,title); + setInt(2, player.getObjectUUID()); + return (executeUpdate() != 0); + } + + + + public boolean ADD_FRIEND(int source, long friend){ + prepareCallable("INSERT INTO `dyn_character_friends` (`playerUID`, `friendUID`) VALUES (?, ?)"); + setLong(1, (long) source); + setLong(2, (long)friend); + return (executeUpdate() != 0); + } + + public boolean REMOVE_FRIEND(int source, int friend){ + prepareCallable("DELETE FROM `dyn_character_friends` WHERE (`playerUID`=?) AND (`friendUID`=?)"); + setLong(1, (long) source); + setLong(2, (long)friend); + return (executeUpdate() != 0); + } + + public void LOAD_PLAYER_FRIENDS() { + + PlayerFriends playerFriend; + + + prepareCallable("SELECT * FROM dyn_character_friends"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + playerFriend = new PlayerFriends(rs); + } + + + } catch (SQLException e) { + Logger.error("LoadMeshBounds: " + e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + } + + public boolean ADD_HERALDY(int source, AbstractWorldObject character){ + prepareCallable("INSERT INTO `dyn_character_heraldy` (`playerUID`, `characterUID`,`characterType`) VALUES (?, ?,?)"); + setLong(1, (long) source); + setLong(2, (long)character.getObjectUUID()); + setInt(3, character.getObjectType().ordinal()); + return (executeUpdate() != 0); + } + + public boolean REMOVE_HERALDY(int source, int characterUID){ + prepareCallable("DELETE FROM `dyn_character_heraldy` WHERE (`playerUID`=?) AND (`characterUID`=?)"); + setLong(1, (long) source); + setLong(2, (long)characterUID); + return (executeUpdate() != 0); + } + + public void LOAD_HERALDY() { + + Heraldry heraldy; + + + prepareCallable("SELECT * FROM dyn_character_heraldy"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + heraldy = new Heraldry(rs); + } + + + } catch (SQLException e) { + Logger.error("LoadHeraldy: " + e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + } + +} diff --git a/src/engine/db/handlers/dbPromotionClassHandler.java b/src/engine/db/handlers/dbPromotionClassHandler.java new file mode 100644 index 00000000..230d3f15 --- /dev/null +++ b/src/engine/db/handlers/dbPromotionClassHandler.java @@ -0,0 +1,54 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.objects.PromotionClass; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; + +public class dbPromotionClassHandler extends dbHandlerBase { + + public dbPromotionClassHandler() { + this.localClass = PromotionClass.class; + this.localObjectType = engine.Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public ArrayList GET_ALLOWED_RUNES(final PromotionClass pc) { + ArrayList runes = new ArrayList<>(); + prepareCallable("SELECT * FROM `static_rune_promotionrunereq` WHERE `promoID`=?"); + setInt(1, pc.getObjectUUID()); + try { + ResultSet rs = executeQuery(); + while (rs.next()) { + runes.add(rs.getInt("runereqID")); + } + } catch (SQLException e) { + Logger.error("Failed to retrieve Allowed Runes for PromotionClass " + pc.getObjectUUID() + ". Error number: " + e.getErrorCode(), e); + return null; + } finally { + closeCallable(); + } + return runes; + } + + public PromotionClass GET_PROMOTION_CLASS(final int objectUUID) { + prepareCallable("SELECT * FROM `static_rune_promotion` WHERE `ID` = ?"); + setInt(1, objectUUID); + return (PromotionClass) getObjectSingle(objectUUID); + } + + public ArrayList GET_ALL_PROMOTIONS() { + prepareCallable("SELECT * FROM `static_rune_promotion`"); + return getObjectList(); + } +} diff --git a/src/engine/db/handlers/dbRaceHandler.java b/src/engine/db/handlers/dbRaceHandler.java new file mode 100644 index 00000000..df83d381 --- /dev/null +++ b/src/engine/db/handlers/dbRaceHandler.java @@ -0,0 +1,85 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.objects.Race; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashSet; +import java.util.concurrent.ConcurrentHashMap; + +public class dbRaceHandler extends dbHandlerBase { + + public dbRaceHandler() { + } + + public HashSet BEARD_COLORS_FOR_RACE(final int id) { + prepareCallable("SELECT `color` FROM `static_rune_racebeardcolor` WHERE `RaceID` = ?"); + setInt(1, id); + return getIntegerList(1); + } + + public HashSet BEARD_STYLES_FOR_RACE(final int id) { + prepareCallable("SELECT `beardStyle` FROM `static_rune_racebeardstyle` WHERE `RaceID` = ?"); + setInt(1, id); + return getIntegerList(1); + } + + public HashSet HAIR_COLORS_FOR_RACE(final int id) { + prepareCallable("SELECT `color` FROM `static_rune_racehaircolor` WHERE `RaceID` = ?"); + setInt(1, id); + return getIntegerList(1); + } + + public HashSet HAIR_STYLES_FOR_RACE(final int id) { + prepareCallable("SELECT `hairStyle` FROM `static_rune_racehairstyle` WHERE `RaceID` = ?"); + setInt(1, id); + return getIntegerList(1); + } + + public HashSet SKIN_COLOR_FOR_RACE(final int id) { + prepareCallable("SELECT `color` FROM `static_rune_raceskincolor` WHERE `RaceID` = ?"); + setInt(1, id); + return getIntegerList(1); + } + + public ConcurrentHashMap LOAD_ALL_RACES() { + + ConcurrentHashMap races; + Race thisRace; + + races = new ConcurrentHashMap<>(); + int recordsRead = 0; + + prepareCallable("SELECT * FROM static_rune_race"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + recordsRead++; + thisRace = new Race(rs); + + races.put(thisRace.getRaceRuneID(), thisRace); + } + + Logger.info("read: " + recordsRead + " cached: " + races.size()); + + } catch (SQLException e) { + Logger.error( e.toString()); + } finally { + closeCallable(); + } + return races; + } +} diff --git a/src/engine/db/handlers/dbRealmHandler.java b/src/engine/db/handlers/dbRealmHandler.java new file mode 100644 index 00000000..932d6425 --- /dev/null +++ b/src/engine/db/handlers/dbRealmHandler.java @@ -0,0 +1,73 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.objects.Realm; +import org.pmw.tinylog.Logger; + +import java.net.UnknownHostException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; + +public class dbRealmHandler extends dbHandlerBase { + + public dbRealmHandler() { + + } + + public ConcurrentHashMap LOAD_ALL_REALMS() { + + ConcurrentHashMap realmList; + Realm thisRealm; + + realmList = new ConcurrentHashMap<>(); + int recordsRead = 0; + + prepareCallable("SELECT * FROM obj_realm"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + recordsRead++; + thisRealm = new Realm(rs); + realmList.put(thisRealm.getRealmID(), thisRealm); + } + + Logger.info( "read: " + recordsRead + " cached: " + realmList.size()); + + } catch (SQLException e) { + Logger.error(e.getErrorCode() + ' ' + e.getMessage(), e); + } catch (UnknownHostException ex) { + java.util.logging.Logger.getLogger(dbRealmHandler.class.getName()).log(Level.SEVERE, null, ex); + } finally { + closeCallable(); + } + return realmList; + } + + public void REALM_UPDATE(Realm realm) { + + prepareCallable("CALL realm_UPDATE(?,?,?,?)"); + + setInt(1, realm.getRealmID()); + setInt(2, (realm.getRulingCity() == null) ? 0 : realm.getRulingCity().getObjectUUID()); + setInt(3, realm.getCharterType()); + if (realm.ruledSince != null) + setLocalDateTime(4, realm.ruledSince); + else + setNULL(4, java.sql.Types.DATE); + + executeUpdate(); + } +} diff --git a/src/engine/db/handlers/dbResistHandler.java b/src/engine/db/handlers/dbResistHandler.java new file mode 100644 index 00000000..0eccf2e2 --- /dev/null +++ b/src/engine/db/handlers/dbResistHandler.java @@ -0,0 +1,37 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.objects.Resists; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class dbResistHandler extends dbHandlerBase { + + public dbResistHandler() { + + } + + public Resists GET_RESISTS_FOR_MOB(int resistID) { + prepareCallable("SELECT * FROM `static_npc_mob_resists` WHERE `ID` = ?;"); + setInt(1, resistID); + try { + ResultSet rs = executeQuery(); + if (rs.next()) { + return new Resists(rs); + } + } catch (SQLException e) { + } finally { + closeCallable(); + } + return null; + } +} diff --git a/src/engine/db/handlers/dbRuneBaseAttributeHandler.java b/src/engine/db/handlers/dbRuneBaseAttributeHandler.java new file mode 100644 index 00000000..ce880b6a --- /dev/null +++ b/src/engine/db/handlers/dbRuneBaseAttributeHandler.java @@ -0,0 +1,35 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.objects.RuneBaseAttribute; + +import java.util.ArrayList; + +public class dbRuneBaseAttributeHandler extends dbHandlerBase { + + public dbRuneBaseAttributeHandler() { + this.localClass = RuneBaseAttribute.class; + this.localObjectType = engine.Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public ArrayList GET_ATTRIBUTES_FOR_RUNEBASE(int id) { + prepareCallable("SELECT * FROM `static_rune_runebaseattribute` WHERE `RuneBaseID`=?"); + setInt(1, id); + return getObjectList(); + } + + public ArrayList GET_ATTRIBUTES_FOR_RUNEBASE() { + prepareCallable("SELECT * FROM `static_rune_runebaseattribute`"); + return getObjectList(); + } + + +} diff --git a/src/engine/db/handlers/dbRuneBaseEffectHandler.java b/src/engine/db/handlers/dbRuneBaseEffectHandler.java new file mode 100644 index 00000000..0e84c8d0 --- /dev/null +++ b/src/engine/db/handlers/dbRuneBaseEffectHandler.java @@ -0,0 +1,73 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.Enum.GameObjectType; +import engine.gameManager.DbManager; +import engine.objects.AbstractGameObject; +import engine.objects.RuneBaseEffect; + +import java.util.ArrayList; +import java.util.HashMap; + +public class dbRuneBaseEffectHandler extends dbHandlerBase { + + public dbRuneBaseEffectHandler() { + this.localClass = RuneBaseEffect.class; + this.localObjectType = engine.Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public ArrayList GET_EFFECTS_FOR_RUNEBASE(int id) { + prepareCallable("SELECT * FROM `static_rune_baseeffect` WHERE `runeID`=?"); + setInt(1, id); + return getObjectList(); + } + + public RuneBaseEffect GET_RUNEBASE_EFFECT(int id) { + + if (id == 0) + return null; + RuneBaseEffect runeBaseEffect = (RuneBaseEffect) DbManager.getFromCache(GameObjectType.RuneBaseEffect, id); + if (runeBaseEffect != null) + return runeBaseEffect; + prepareCallable("SELECT * FROM `static_rune_baseeffect` WHERE `ID` = ?"); + setInt(1, id); + return (RuneBaseEffect) getObjectSingle(id); + } + + public ArrayList GET_ALL_RUNEBASE_EFFECTS(){ + prepareCallable("SELECT * FROM `static_rune_baseeffect`;"); + return getObjectList(); + } + + //This calls from cache only. Call this AFTER caching all runebase effects; + public HashMap> LOAD_BASEEFFECTS_FOR_RUNEBASE() { + HashMap> runeBaseEffectSet; + runeBaseEffectSet = new HashMap<>(); + + + for (AbstractGameObject runeBaseEffect:DbManager.getList(GameObjectType.RuneBaseEffect)){ + + int runeBaseID = ((RuneBaseEffect)runeBaseEffect).getRuneBaseID(); + if (runeBaseEffectSet.get(runeBaseID) == null){ + ArrayList runeBaseEffectList = new ArrayList<>(); + runeBaseEffectList.add((RuneBaseEffect)runeBaseEffect); + runeBaseEffectSet.put(runeBaseID, runeBaseEffectList); + } + else{ + ArrayListruneBaseEffectList = runeBaseEffectSet.get(runeBaseID); + runeBaseEffectList.add((RuneBaseEffect)runeBaseEffect); + runeBaseEffectSet.put(runeBaseID, runeBaseEffectList); + } + } + return runeBaseEffectSet; + } + +} diff --git a/src/engine/db/handlers/dbRuneBaseHandler.java b/src/engine/db/handlers/dbRuneBaseHandler.java new file mode 100644 index 00000000..44e58179 --- /dev/null +++ b/src/engine/db/handlers/dbRuneBaseHandler.java @@ -0,0 +1,173 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.objects.RuneBase; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; + +public class dbRuneBaseHandler extends dbHandlerBase { + + public dbRuneBaseHandler() { + this.localClass = RuneBase.class; + this.localObjectType = engine.Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public void GET_RUNE_REQS(final RuneBase rb) { + prepareCallable("SELECT * FROM `static_rune_runereq` WHERE `runeID` = ?"); + setInt(1, rb.getObjectUUID()); + try { + + ResultSet rs = executeQuery(); + + while (rs.next()) { + int type = rs.getInt("type"); + + switch (type) { + case 1: + rb.getRace().put(rs.getInt("requiredRuneID"), rs.getBoolean("isAllowed")); + break; + case 2: + rb.getBaseClass().put(rs.getInt("requiredRuneID"), rs.getBoolean("isAllowed")); + break; + case 3: + rb.getPromotionClass().put(rs.getInt("requiredRuneID"), rs.getBoolean("isAllowed")); + break; + case 4: + rb.getDiscipline().put(rs.getInt("requiredRuneID"), rs.getBoolean("isAllowed")); + break; + case 5: + rb.getOverwrite().add(rs.getInt("requiredRuneID")); + break; + case 6: + rb.setLevelRequired(rs.getInt("requiredRuneID")); + break; + } + } + rs.close(); + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode()); + } finally { + closeCallable(); + } + } + + public RuneBase GET_RUNEBASE(final int id) { + prepareCallable("SELECT * FROM `static_rune_runebase` WHERE `ID` = ?"); + setInt(1, id); + return (RuneBase) getObjectSingle(id); + } + + public ArrayList LOAD_ALL_RUNEBASES() { + prepareCallable("SELECT * FROM `static_rune_runebase`;"); + return getObjectList(); + } + + public HashMap> LOAD_ALLOWED_STARTING_RUNES_FOR_BASECLASS() { + + HashMap> runeSets; + + runeSets = new HashMap<>(); + int recordsRead = 0; + + prepareCallable("SELECT * FROM static_rune_baseclassrune"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + recordsRead++; + + int baseClassID = rs.getInt("BaseClassesID"); + int runeBaseID = rs.getInt("RuneBaseID"); + + if (runeSets.get(baseClassID) == null){ + ArrayList runeList = new ArrayList<>(); + runeList.add(runeBaseID); + runeSets.put(baseClassID, runeList); + } + else{ + ArrayListruneList = runeSets.get(baseClassID); + runeList.add(runeBaseID); + runeSets.put(baseClassID, runeList); + } + } + + Logger.info("read: " + recordsRead + " cached: " + runeSets.size()); + + } catch (SQLException e) { + Logger.error(e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + return runeSets; + } + + public HashMap> LOAD_ALLOWED_STARTING_RUNES_FOR_RACE() { + + HashMap> runeSets; + + runeSets = new HashMap<>(); + int recordsRead = 0; + + prepareCallable("SELECT * FROM static_rune_racerune"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + recordsRead++; + + int raceID = rs.getInt("RaceID"); + int runeBaseID = rs.getInt("RuneBaseID"); + + if (runeSets.get(raceID) == null){ + ArrayList runeList = new ArrayList<>(); + runeList.add(runeBaseID); + runeSets.put(raceID, runeList); + } + else{ + ArrayListruneList = runeSets.get(raceID); + runeList.add(runeBaseID); + runeSets.put(raceID, runeList); + } + } + + Logger.info( "read: " + recordsRead + " cached: " + runeSets.size()); + + } catch (SQLException e) { + Logger.error(e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + return runeSets; + } + + public ArrayList GET_RUNEBASE_FOR_BASECLASS(final int id) { + prepareCallable("SELECT rb.* FROM static_rune_baseclassrune bcr, static_rune_runebase rb WHERE bcr.RuneBaseID = rb.ID " + + "&& ( bcr.BaseClassesID = 111111 || bcr.BaseClassesID = ? )"); + setInt(1, id); + return getObjectList(); + } + + public HashSet GET_RUNEBASE_FOR_RACE(final int id) { + prepareCallable("SELECT rb.* FROM static_rune_racerune rr, static_rune_runebase rb" + + " WHERE rr.RuneBaseID = rb.ID && ( rr.RaceID = 111111 || rr.RaceID = ?)"); + setInt(1, id); + return new HashSet<>(getObjectList()); + } +} diff --git a/src/engine/db/handlers/dbShrineHandler.java b/src/engine/db/handlers/dbShrineHandler.java new file mode 100644 index 00000000..3d5eb30b --- /dev/null +++ b/src/engine/db/handlers/dbShrineHandler.java @@ -0,0 +1,147 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.Enum.ProtectionState; +import engine.gameManager.DbManager; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractGameObject; +import engine.objects.Building; +import engine.objects.Shrine; +import org.joda.time.DateTime; +import org.pmw.tinylog.Logger; + +import java.net.UnknownHostException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; + +public class dbShrineHandler extends dbHandlerBase { + + public dbShrineHandler() { + this.localClass = Shrine.class; + this.localObjectType = engine.Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public ArrayList CREATE_SHRINE( int parentZoneID, int OwnerUUID, String name, int meshUUID, + Vector3fImmutable location, float meshScale, int currentHP, + ProtectionState protectionState, int currentGold, int rank, + DateTime upgradeDate, int blueprintUUID, float w, float rotY, String shrineType) { + + prepareCallable("CALL `shrine_CREATE`(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ,? ,? ,?, ?,?);"); + + + setInt(1, parentZoneID); + setInt(2, OwnerUUID); + setString(3, name); + setInt(4, meshUUID); + setFloat(5, location.x); + setFloat(6, location.y); + setFloat(7, location.z); + setFloat(8, meshScale); + setInt(9, currentHP); + setString(10, protectionState.name()); + setInt(11, currentGold); + setInt(12, rank); + + if (upgradeDate != null) { + setTimeStamp(13, upgradeDate.getMillis()); + } else { + setNULL(13, java.sql.Types.DATE); + } + + setInt(14, blueprintUUID); + setFloat(15, w); + setFloat(16, rotY); + setString(17, shrineType); + + ArrayList list = new ArrayList<>(); + //System.out.println(this.cs.get().toString()); + try { + boolean work = execute(); + if (work) { + ResultSet rs = this.cs.get().getResultSet(); + while (rs.next()) { + addObject(list, rs); + } + rs.close(); + } else { + Logger.info("Shrine Creation Failed: " + this.cs.get().toString()); + return list; //city creation failure + } + while (this.cs.get().getMoreResults()) { + ResultSet rs = this.cs.get().getResultSet(); + while (rs.next()) { + addObject(list, rs); + } + rs.close(); + } + } catch (SQLException e) { + Logger.info("Shrine Creation Failed, SQLException: " + this.cs.get().toString() + e.toString()); + return list; //city creation failure + } catch (UnknownHostException e) { + Logger.info("Shrine Creation Failed, UnknownHostException: " + this.cs.get().toString()); + return list; //city creation failure + } finally { + closeCallable(); + } + return list; + + } + + public boolean updateFavors(Shrine shrine, int amount, int oldAmount) { + + prepareCallable("UPDATE `obj_shrine` SET `shrine_favors`=? WHERE `UID` = ? AND `shrine_favors` = ?"); + setInt(1, amount); + setLong(2, (long) shrine.getObjectUUID()); + setInt(3, oldAmount); + return (executeUpdate() != 0); + } + + public static void addObject(ArrayList list, ResultSet rs) throws SQLException, UnknownHostException { + String type = rs.getString("type"); + switch (type) { + case "building": + Building building = new Building(rs); + DbManager.addToCache(building); + list.add(building); + break; + case "shrine": + Shrine shrine = new Shrine(rs); + DbManager.addToCache(shrine); + list.add(shrine); + break; + } + } + + public void LOAD_ALL_SHRINES() { + + Shrine thisShrine; + + prepareCallable("SELECT `obj_shrine`.*, `object`.`parent`, `object`.`type` FROM `object` LEFT JOIN `obj_shrine` ON `object`.`UID` = `obj_shrine`.`UID` WHERE `object`.`type` = 'shrine';"); + + try { + ResultSet rs = executeQuery(); + + //shrines cached in rs for easy cache on creation. + while (rs.next()) { + thisShrine = new Shrine(rs); + thisShrine.getShrineType().addShrineToServerList(thisShrine); + } + + } catch (SQLException e) { + Logger.error( e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + + } + +} diff --git a/src/engine/db/handlers/dbSkillBaseHandler.java b/src/engine/db/handlers/dbSkillBaseHandler.java new file mode 100644 index 00000000..f35b7be4 --- /dev/null +++ b/src/engine/db/handlers/dbSkillBaseHandler.java @@ -0,0 +1,143 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.gameManager.DbManager; +import engine.objects.AbstractGameObject; +import engine.objects.MaxSkills; +import engine.objects.SkillsBase; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; + +public class dbSkillBaseHandler extends dbHandlerBase { + + public dbSkillBaseHandler() { + this.localClass = SkillsBase.class; + this.localObjectType = Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public SkillsBase GET_BASE(final int objectUUID) { + + SkillsBase skillsBase = (SkillsBase) DbManager.getFromCache(GameObjectType.SkillsBase, objectUUID); + if (skillsBase != null) + return skillsBase; + prepareCallable("SELECT * FROM static_skill_skillsbase WHERE ID = ?"); + setInt(1, objectUUID); + SkillsBase sb; + sb = (SkillsBase) getObjectSingle(objectUUID); + SkillsBase.putInCache(sb); + return sb; + } + + public SkillsBase GET_BASE_BY_NAME(String name) { + SkillsBase sb = SkillsBase.getFromCache(name); + if (sb != null) { + return sb; + } + prepareCallable("SELECT * FROM static_skill_skillsbase WHERE name = ?"); + setString(1, name); + ArrayList result = getObjectList(); + if (result.size() > 0) { + sb = (SkillsBase) result.get(0); + SkillsBase.putInCache(sb); + return sb; + } else { + return null; + } + } + + public SkillsBase GET_BASE_BY_TOKEN(final int token) { + SkillsBase sb = SkillsBase.getFromCache(token); + if (sb != null) { + return sb; + } + + prepareCallable("SELECT * FROM static_skill_skillsbase WHERE token = ?"); + setInt(1, token); + ArrayList result = getObjectList(); + if (result.size() > 0) { + sb = (SkillsBase) result.get(0); + SkillsBase.putInCache(sb); + return sb; + } else { + return null; + } + } + + public void LOAD_ALL_MAX_SKILLS_FOR_CONTRACT() { + + prepareCallable("SELECT * FROM `static_rune_maxskills`"); + + + try { + ResultSet rs = executeQuery(); + + //shrines cached in rs for easy cache on creation. + while (rs.next()) { + + MaxSkills maxSKills = new MaxSkills(rs); + if (MaxSkills.MaxSkillsSet.get(maxSKills.getRuneID()) == null){ + ArrayList newMaxSkillsList = new ArrayList<>(); + newMaxSkillsList.add(maxSKills); + MaxSkills.MaxSkillsSet.put(maxSKills.getRuneID(), newMaxSkillsList); + }else + MaxSkills.MaxSkillsSet.get(maxSKills.getRuneID()).add(maxSKills); + + } + + + + } catch (SQLException e) { + Logger.error( e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + + } + + public void LOAD_ALL_RUNE_SKILLS() { + + prepareCallable("SELECT * FROM `static_skill_skillsgranted`"); + + + try { + ResultSet rs = executeQuery(); + + //shrines cached in rs for easy cache on creation. + while (rs.next()) { + + int runeID = rs.getInt("runeID"); + int token = rs.getInt("token"); + int amount = rs.getInt("amount"); + + if (SkillsBase.runeSkillsCache.get(runeID) == null) + SkillsBase.runeSkillsCache.put(runeID, new HashMap<>()); + + SkillsBase.runeSkillsCache.get(runeID).put(token, amount); + } + + + + } catch (SQLException e) { + Logger.error( e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + + } + + +} diff --git a/src/engine/db/handlers/dbSkillReqHandler.java b/src/engine/db/handlers/dbSkillReqHandler.java new file mode 100644 index 00000000..b0e16f90 --- /dev/null +++ b/src/engine/db/handlers/dbSkillReqHandler.java @@ -0,0 +1,35 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.objects.SkillReq; + +import java.util.ArrayList; + +public class dbSkillReqHandler extends dbHandlerBase { + + public dbSkillReqHandler() { + this.localClass = SkillReq.class; + this.localObjectType = engine.Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public ArrayList GET_REQS_FOR_RUNE(final int objectUUID) { + prepareCallable("SELECT * FROM `static_skill_skillreq` WHERE `runeID`=?"); + setInt(1, objectUUID); + return getObjectList(); + } + + public SkillReq GET_REQS_BY_SKILLID(int skillID) { + prepareCallable("SELECT * FROM `static_skill_skillreq` WHERE `skillID` = ?"); + setInt(1,skillID); + int objectUUID = (int) getUUID(); + return (SkillReq) this.getObjectSingle(objectUUID); + } +} diff --git a/src/engine/db/handlers/dbSpecialLootHandler.java b/src/engine/db/handlers/dbSpecialLootHandler.java new file mode 100644 index 00000000..76aa68dd --- /dev/null +++ b/src/engine/db/handlers/dbSpecialLootHandler.java @@ -0,0 +1,75 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.objects.SpecialLoot; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; + +public class dbSpecialLootHandler extends dbHandlerBase { + + public dbSpecialLootHandler() { + this.localClass = SpecialLoot.class; + this.localObjectType = engine.Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public ArrayList GET_SPECIALLOOT(int mobbaseID) { + + prepareCallable("SELECT * FROM `static_npc_mob_specialloot` WHERE `mobbaseID`=?"); + setInt(1, mobbaseID); + return getObjectList(); + } + + public void GenerateSpecialLoot(){ + HashMap> lootSets; + SpecialLoot lootSetEntry; + int lootSetID; + + lootSets = new HashMap<>(); + int recordsRead = 0; + + prepareCallable("SELECT * FROM static_zone_npc_specialloot"); + + try { + ResultSet rs = executeQuery(); + + while (rs.next()) { + + recordsRead++; + + lootSetID = rs.getInt("lootSet"); + lootSetEntry = new SpecialLoot(rs,true); + + if (lootSets.get(lootSetID) == null){ + ArrayList lootList = new ArrayList<>(); + lootList.add(lootSetEntry); + lootSets.put(lootSetID, lootList); + } + else{ + ArrayListlootList = lootSets.get(lootSetID); + lootList.add(lootSetEntry); + lootSets.put(lootSetID, lootList); + } + } + + Logger.info( "read: " + recordsRead + " cached: " + lootSets.size()); + + } catch (SQLException e) { + Logger.error(e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + SpecialLoot.LootMap = lootSets; + } +} diff --git a/src/engine/db/handlers/dbVendorDialogHandler.java b/src/engine/db/handlers/dbVendorDialogHandler.java new file mode 100644 index 00000000..def139f2 --- /dev/null +++ b/src/engine/db/handlers/dbVendorDialogHandler.java @@ -0,0 +1,31 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.Enum; +import engine.gameManager.DbManager; +import engine.objects.VendorDialog; + +public class dbVendorDialogHandler extends dbHandlerBase { + + public dbVendorDialogHandler() { + this.localClass = VendorDialog.class; + this.localObjectType = Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public VendorDialog GET_VENDORDIALOG(final int objectUUID) { + VendorDialog vd = (VendorDialog) DbManager.getFromCache(Enum.GameObjectType.VendorDialog, objectUUID); + if (vd != null) + return vd; + prepareCallable("SELECT * FROM `static_npc_vendordialog` WHERE `ID`=?"); + setInt(1, objectUUID); + return (VendorDialog) getObjectSingle(objectUUID); + } +} diff --git a/src/engine/db/handlers/dbWarehouseHandler.java b/src/engine/db/handlers/dbWarehouseHandler.java new file mode 100644 index 00000000..f859cfaa --- /dev/null +++ b/src/engine/db/handlers/dbWarehouseHandler.java @@ -0,0 +1,449 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.Enum.GameObjectType; +import engine.Enum.ProtectionState; +import engine.Enum.TransactionType; +import engine.gameManager.DbManager; +import engine.math.Vector3fImmutable; +import engine.objects.*; +import engine.server.MBServerStatics; +import org.joda.time.DateTime; +import org.pmw.tinylog.Logger; + +import java.net.UnknownHostException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; + +public class dbWarehouseHandler extends dbHandlerBase { + + private static final ConcurrentHashMap columns = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + + public dbWarehouseHandler() { + this.localClass = Warehouse.class; + this.localObjectType = engine.Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + + if (columns.isEmpty()) { + createColumns(); + } + } + + public Warehouse CREATE_WAREHOUSE(Warehouse wh) { + try { + wh = this.addWarehouse(wh); + } catch (Exception e) { + Logger.error(e); + wh = null; + + } + return wh; + } + + public ArrayList CREATE_WAREHOUSE( int parentZoneID, int OwnerUUID, String name, int meshUUID, + Vector3fImmutable location, float meshScale, int currentHP, + ProtectionState protectionState, int currentGold, int rank, + DateTime upgradeDate, int blueprintUUID, float w, float rotY) { + + prepareCallable("CALL `WAREHOUSE_CREATE`(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ,? ,? ,?, ?);"); + + setInt(1, parentZoneID); + setInt(2, OwnerUUID); + setString(3, name); + setInt(4, meshUUID); + setFloat(5, location.x); + setFloat(6, location.y); + setFloat(7, location.z); + setFloat(8, meshScale); + setInt(9, currentHP); + setString(10, protectionState.name()); + setInt(11, currentGold); + setInt(12, rank); + + if (upgradeDate != null) { + setTimeStamp(13, upgradeDate.getMillis()); + } else { + setNULL(13, java.sql.Types.DATE); + } + + setInt(14, blueprintUUID); + setFloat(15, w); + setFloat(16, rotY); + + ArrayList list = new ArrayList<>(); + //System.out.println(this.cs.get().toString()); + try { + boolean work = execute(); + if (work) { + ResultSet rs = this.cs.get().getResultSet(); + while (rs.next()) { + addObject(list, rs); + } + rs.close(); + } else { + Logger.info("Warehouse Creation Failed: " + this.cs.get().toString()); + return list; //city creation failure + } + while (this.cs.get().getMoreResults()) { + ResultSet rs = this.cs.get().getResultSet(); + while (rs.next()) { + addObject(list, rs); + } + rs.close(); + } + } catch (SQLException e) { + Logger.info("Warehouse Creation Failed, SQLException: " + this.cs.get().toString() + e.toString()); + return list; //city creation failure + } catch (UnknownHostException e) { + Logger.info("Warehouse Creation Failed, UnknownHostException: " + this.cs.get().toString()); + return list; //city creation failure + } finally { + closeCallable(); + } + return list; + + } + + //Don't call yet, not ready in DB. - + public boolean WAREHOUSE_ADD(Item item, Warehouse warehouse, ItemBase ib, int amount) { + if (item == null || warehouse == null || ib == null || !(dbWarehouseHandler.columns.containsKey(ib.getUUID()))) { + return false; + } + if ((item.getNumOfItems() - amount) < 0) { + return false; + } + if (!warehouse.getResources().containsKey(ib)) { + return false; + } + + prepareCallable("CALL `warehouse_ADD`(?,?,?,?,?,?,?);"); + setLong(1, (long) warehouse.getObjectUUID()); + setInt(2, warehouse.getResources().get(ib)); + setLong(3, (long) item.getObjectUUID()); + setInt(4, item.getNumOfItems()); + setInt(5, amount); + setString(6, dbWarehouseHandler.columns.get(ib.getUUID())); + setInt(7, ib.getUUID()); + String result = getResult(); + + return (result != null && result.equals("success")); + } + + private Warehouse addWarehouse(Warehouse toAdd) { + prepareCallable("CALL `warehouse_CREATE`(?);"); + setInt(1, toAdd.getUID()); + int objectUUID = (int) getUUID(); + if (objectUUID > 0) { + return GET_WAREHOUSE(objectUUID); + } + return null; + } + + public Warehouse GET_WAREHOUSE(int objectUUID) { + Warehouse warehouse = (Warehouse) DbManager.getFromCache(GameObjectType.Warehouse, objectUUID); + if (warehouse != null) + return warehouse; + prepareCallable("SELECT * FROM `obj_warehouse` WHERE `UID` = ?"); + setInt(1, objectUUID); + return (Warehouse) getObjectSingle(objectUUID); + } + + public boolean updateLocks(final Warehouse wh, long locks) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_locks`=? WHERE `UID` = ?"); + setLong(1, locks); + setInt(2, wh.getUID()); + return (executeUpdate() != 0); + } + + public boolean updateGold(final Warehouse wh, int amount ) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_gold`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + return (executeUpdate() != 0); + } + + public boolean updateStone(final Warehouse wh, int amount) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_stone`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + public boolean updateTruesteel(final Warehouse wh, int amount) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_truesteel`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + public boolean updateIron(final Warehouse wh, int amount) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_iron`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + public boolean updateAdamant(final Warehouse wh, int amount) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_adamant`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + public boolean updateLumber(final Warehouse wh, int amount) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_lumber`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + public boolean updateOak(final Warehouse wh, int amount) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_oak`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + public boolean updateBronzewood(final Warehouse wh, int amount) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_bronzewood`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + public boolean updateMandrake(final Warehouse wh, int amount) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_mandrake`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + public boolean updateCoal(final Warehouse wh, int amount) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_coal`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + public boolean updateAgate(final Warehouse wh, int amount) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_agate`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + public boolean updateDiamond(final Warehouse wh, int amount) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_diamond`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + public boolean updateOnyx(final Warehouse wh, int amount) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_onyx`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + public boolean updateAzoth(final Warehouse wh, int amount) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_azoth`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + public boolean updateOrichalk(final Warehouse wh, int amount) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_orichalk`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + public boolean updateAntimony(final Warehouse wh, int amount) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_antimony`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + public boolean updateSulfur(final Warehouse wh, int amount) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_sulfur`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + public boolean updateQuicksilver(final Warehouse wh, int amount) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_quicksilver`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + public boolean updateGalvor(final Warehouse wh, int amount) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_galvor`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + public boolean updateWormwood(final Warehouse wh, int amount ) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_wormwood`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + public boolean updateObsidian(final Warehouse wh, int amount) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_obsidian`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + public boolean updateBloodstone(final Warehouse wh, int amount) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_bloodstone`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + public boolean updateMithril(final Warehouse wh, int amount) { + prepareCallable("UPDATE `obj_warehouse` SET `warehouse_mithril`=? WHERE `UID` = ?"); + setInt(1, amount); + setInt(2, wh.getUID()); + + return (executeUpdate() != 0); + } + + private static void createColumns() { + columns.put(1580000, "warehouse_stone"); + columns.put(1580001, "warehouse_truesteel"); + columns.put(1580002, "warehouse_iron"); + columns.put(1580003, "warehouse_adamant"); + columns.put(1580004, "warehouse_lumber"); + columns.put(1580005, "warehouse_oak"); + columns.put(1580006, "warehouse_bronzewood"); + columns.put(1580007, "warehouse_mandrake"); + columns.put(1580008, "warehouse_coal"); + columns.put(1580009, "warehouse_agate"); + columns.put(1580010, "warehouse_diamond"); + columns.put(1580011, "warehouse_onyx"); + columns.put(1580012, "warehouse_azoth"); + columns.put(1580013, "warehouse_orichalk"); + columns.put(1580014, "warehouse_antimony"); + columns.put(1580015, "warehouse_sulfur"); + columns.put(1580016, "warehouse_quicksilver"); + columns.put(1580017, "warehouse_galvor"); + columns.put(1580018, "warehouse_wormwood"); + columns.put(1580019, "warehouse_obsidian"); + columns.put(1580020, "warehouse_bloodstone"); + columns.put(1580021, "warehouse_mithril"); + columns.put(7, "warehouse_gold"); + } + + public boolean CREATE_TRANSACTION(int warehouseBuildingID, GameObjectType targetType, int targetUUID, TransactionType transactionType,Resource resource, int amount,DateTime date){ + Transaction transactions = null; + prepareCallable("INSERT INTO `dyn_warehouse_transactions` (`warehouseUID`, `targetType`,`targetUID`, `type`,`resource`,`amount`,`date` ) VALUES (?,?,?,?,?,?,?)"); + setLong(1, warehouseBuildingID); + setString(2, targetType.name()); + setLong(3, targetUUID); + setString(4, transactionType.name()); + setString(5, resource.name()); + setInt(6,amount); + setTimeStamp(7,date.getMillis()); + return (executeUpdate() != 0); + } + + + + public static void addObject(ArrayList list, ResultSet rs) throws SQLException, UnknownHostException { + String type = rs.getString("type"); + switch (type) { + case "building": + Building building = new Building(rs); + DbManager.addToCache(building); + list.add(building); + break; + case "warehouse": + Warehouse warehouse = new Warehouse(rs); + DbManager.addToCache(warehouse); + list.add(warehouse); + break; + } + } + + public ArrayList GET_TRANSACTIONS_FOR_WAREHOUSE(final int warehouseUUID) { + ArrayList transactionsList = new ArrayList<>(); + prepareCallable("SELECT * FROM dyn_warehouse_transactions WHERE `warehouseUID` = ?;"); + setInt(1, warehouseUUID); + try { + ResultSet rs = executeQuery(); + + //shrines cached in rs for easy cache on creation. + while (rs.next()) { + Transaction transactions = new Transaction(rs); + transactionsList.add(transactions); + } + + } catch (SQLException e) { + Logger.error( e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + return transactionsList; + } + + public void LOAD_ALL_WAREHOUSES() { + + Warehouse thisWarehouse; + + prepareCallable("SELECT `obj_warehouse`.*, `object`.`parent`, `object`.`type` FROM `object` LEFT JOIN `obj_warehouse` ON `object`.`UID` = `obj_warehouse`.`UID` WHERE `object`.`type` = 'warehouse';"); + + try { + ResultSet rs = executeQuery(); + while (rs.next()) { + thisWarehouse = new Warehouse(rs); + thisWarehouse.runAfterLoad(); + thisWarehouse.loadAllTransactions(); + } + + } catch (SQLException e) { + Logger.error(e.getErrorCode() + ' ' + e.getMessage(), e); + } finally { + closeCallable(); + } + + } +} diff --git a/src/engine/db/handlers/dbZoneHandler.java b/src/engine/db/handlers/dbZoneHandler.java new file mode 100644 index 00000000..6a6990ae --- /dev/null +++ b/src/engine/db/handlers/dbZoneHandler.java @@ -0,0 +1,93 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.db.handlers; + +import engine.Enum; +import engine.gameManager.DbManager; +import engine.objects.Zone; + +import java.sql.ResultSet; +import java.util.ArrayList; + +public class dbZoneHandler extends dbHandlerBase { + + public dbZoneHandler() { + this.localClass = Zone.class; + this.localObjectType = Enum.GameObjectType.valueOf(this.localClass.getSimpleName()); + } + + public ArrayList GET_ALL_NODES(Zone zone) { + ArrayList wsmList = new ArrayList<>(); + wsmList.addAll(zone.getNodes()); + if (zone.absX == 0.0f) { + zone.absX = zone.getXCoord(); + } + if (zone.absY == 0.0f) { + zone.absY = zone.getYCoord(); + } + if (zone.absZ == 0.0f) { + zone.absZ = zone.getZCoord(); + } + for (Zone child : zone.getNodes()) { + child.absX = child.getXCoord() + zone.absX; + child.absY = child.getYCoord() + zone.absY; + child.absZ = child.getZCoord() + zone.absZ; + wsmList.addAll(this.GET_ALL_NODES(child)); + } + return wsmList; + } + + public Zone GET_BY_UID(long ID) { + + Zone zone = (Zone) DbManager.getFromCache(Enum.GameObjectType.Zone, (int)ID); + if (zone != null) + return zone; + prepareCallable("SELECT `obj_zone`.*, `object`.`parent` FROM `object` INNER JOIN `obj_zone` ON `obj_zone`.`UID` = `object`.`UID` WHERE `object`.`UID` = ?;"); + setLong(1, ID); + return (Zone) getObjectSingle((int) ID); + } + + public ArrayList GET_MAP_NODES(final int objectUUID) { + prepareCallable("SELECT `obj_zone`.*, `object`.`parent` FROM `object` INNER JOIN `obj_zone` ON `obj_zone`.`UID` = `object`.`UID` WHERE `object`.`parent` = ?;"); + setLong(1, (long) objectUUID); + return getObjectList(); + } + + public ResultSet GET_ZONE_EXTENTS(final int loadNum) { + prepareCallable("SELECT * FROM `static_zone_size` WHERE `loadNum`=?;"); + setInt(1, loadNum); + return executeQuery(); + } + + public String SET_PROPERTY(final Zone z, String name, Object new_value) { + prepareCallable("CALL zone_SETPROP(?,?,?)"); + setLong(1, (long) z.getObjectUUID()); + setString(2, name); + setString(3, String.valueOf(new_value)); + return getResult(); + } + + public String SET_PROPERTY(final Zone z, String name, Object new_value, Object old_value) { + prepareCallable("CALL zone_GETSETPROP(?,?,?,?)"); + setLong(1, (long) z.getObjectUUID()); + setString(2, name); + setString(3, String.valueOf(new_value)); + setString(4, String.valueOf(old_value)); + return getResult(); + } + + public boolean DELETE_ZONE(final Zone zone) { + + prepareCallable("DELETE FROM `object` WHERE `UID` = ? AND `type` = 'zone'"); + setInt(1, zone.getObjectUUID()); + return (executeUpdate() != 0); + } + +} diff --git a/src/engine/devcmd/AbstractDevCmd.java b/src/engine/devcmd/AbstractDevCmd.java new file mode 100644 index 00000000..75a54683 --- /dev/null +++ b/src/engine/devcmd/AbstractDevCmd.java @@ -0,0 +1,181 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd; + +import engine.Enum.GameObjectType; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.objects.*; + +import java.util.ArrayList; + +public abstract class AbstractDevCmd { + + protected final ArrayList cmdStrings; + private AbstractGameObject tr; + private String rsult; + + public AbstractDevCmd(String cmdString) { + super(); + this.cmdStrings = new ArrayList<>(); + this.addCmdString(cmdString); + this.rsult = ""; + } + + /** + * This function is called by the DevCmdManager. Method splits argString + * into a String array and then calls the subclass specific _doCmd method. + */ + + public void doCmd(PlayerCharacter pcSender, String argString, + AbstractGameObject target) { + String[] args = argString.split(" "); + + if (pcSender == null) { + return; + } + + if (args.length > 0 && args[0].equalsIgnoreCase("?")) { + this.sendHelp(pcSender); + this.sendUsage(pcSender); + } else { + this.tr = target; + this._doCmd(pcSender, args, target); + } + } + + protected abstract void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target); + + /** + * Returns the string sent to the client that displays how to use this + * command. + */ + + public final String getUsageString() { + return "Usage: " + this._getUsageString(); + } + + protected abstract String _getUsageString(); + + /** + * Returns the string sent to the client that displays what this command + * does. + */ + + public final String getHelpString() { + return this.getMainCmdString() + ": " + this._getHelpString(); + } + + protected abstract String _getHelpString(); + + public final ArrayList getCmdStrings() { + return cmdStrings; + } + + public final String getMainCmdString() { + return this.cmdStrings.get(0); + } + + protected void addCmdString(String cmdString) { + String lowercase = cmdString.toLowerCase(); + this.cmdStrings.add(lowercase); + } + + public void setTarget(AbstractGameObject ago) { + this.tr = ago; + } + + public AbstractGameObject getTarget() { + return this.tr; + } + + public void setResult(String result) { + this.rsult = result; + } + + public String getResult() { + return this.rsult; + } + + /* + * Helper functions + */ + protected void sendUsage(PlayerCharacter pc) { + this.throwbackError(pc, this.getUsageString()); + } + + protected void sendHelp(PlayerCharacter pc) { + this.throwbackError(pc, this.getHelpString()); + } + + protected void throwbackError(PlayerCharacter pc, String msgText) { + ChatManager.chatSystemError(pc, msgText); + } + + protected void throwbackInfo(PlayerCharacter pc, String msgText) { + ChatManager.chatSystemInfo(pc, msgText); + } + + /* + * Misc tools/helpers + */ + protected static Building getTargetAsBuilding(PlayerCharacter pc) { + int targetType = pc.getLastTargetType().ordinal(); + int targetID = pc.getLastTargetID(); + if (targetType == GameObjectType.Building.ordinal()) { + Building b = (Building) DbManager + .getFromCache(GameObjectType.Building, targetID); + if (b == null) { + ChatManager.chatSystemError( + pc, + "Command Failed. Could not find building of ID " + + targetID); + return null; + } + return b; + } else { + return null; + } + } + + protected static Mob getTargetAsMob(PlayerCharacter pc) { + int targetType = pc.getLastTargetType().ordinal(); + int targetID = pc.getLastTargetID(); + if (targetType == GameObjectType.Mob.ordinal()) { + Mob b = Mob.getMob(targetID); + if (b == null) { + ChatManager.chatSystemError(pc, + "Command Failed. Could not find Mob of ID " + targetID); + return null; + } + return b; + } else { + return null; + } + } + + protected static NPC getTargetAsNPC(PlayerCharacter pc) { + int targetType = pc.getLastTargetType().ordinal(); + int targetID = pc.getLastTargetID(); + if (targetType == GameObjectType.NPC.ordinal()) { + NPC b = NPC.getFromCache(targetID); + if (b == null) { + ChatManager.chatSystemError(pc, + "Command Failed. Could not find NPC of ID " + targetID); + return null; + } + return b; + } else { + return null; + } + } + +} diff --git a/src/engine/devcmd/cmds/AddBuildingCmd.java b/src/engine/devcmd/cmds/AddBuildingCmd.java new file mode 100644 index 00000000..50e371ff --- /dev/null +++ b/src/engine/devcmd/cmds/AddBuildingCmd.java @@ -0,0 +1,127 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.Enum.ProtectionState; +import engine.InterestManagement.WorldGrid; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.gameManager.ZoneManager; +import engine.math.Vector3f; +import engine.math.Vector3fImmutable; +import engine.objects.*; +import engine.server.MBServerStatics; + +public class AddBuildingCmd extends AbstractDevCmd { + + public AddBuildingCmd() { + super("addbuilding"); +// super("addbuilding", MBServerStatics.ACCESS_GROUP_DESIGNER_UP, 0, false, true); + this.addCmdString("building"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + // Arg Count Check + if (words.length != 2) { + this.sendUsage(pc); + return; + } + + int ID; + int rank; + Blueprint blueprint; + + try { + ID = Integer.parseInt(words[0]); + rank = Integer.parseInt(words[1]); + } catch (Exception e) { + throwbackError(pc, "Invalid addBuilding Command. Need Building ID and rank."); + return; // NaN + } + if (ID < 1) { + throwbackError(pc, + "Invalid addBuilding Command. Invalid Building ID."); + return; + } + Vector3f rot = new Vector3f(0.0f, 0.0f, 0.0f); + float w = 1f; + Zone zone = ZoneManager.findSmallestZone(pc.getLoc()); + + if (zone == null) { + throwbackError(pc, "Failed to find zone to place building in."); + return; + } + + blueprint = Blueprint.getBlueprint(ID); + + if ((blueprint != null) && (rank > blueprint.getMaxRank())) { + throwbackError(pc, rank + " is not a valid rank for this building"); + return; + } + + Building likeBuilding = DbManager.BuildingQueries.GET_BUILDING_BY_MESH(ID); + + if (likeBuilding != null) { + rot = likeBuilding.getRot(); + w = likeBuilding.getw(); + } + + String buildingName = ""; + int blueprintUUID = 0; + + Vector3fImmutable localLoc = ZoneManager.worldToLocal(pc.getLoc(), zone); + + if (localLoc == null) + return; + + if (blueprint != null) { + buildingName = blueprint.getName(); + blueprintUUID = blueprint.getBlueprintUUID(); + } + + Building building = DbManager.BuildingQueries. + CREATE_BUILDING( + zone.getObjectUUID(), 0, buildingName, ID, + localLoc, 1.0f, 0, ProtectionState.PROTECTED, 0, rank, + null, blueprintUUID, w, rot.y); + + + if (building == null) { + throwbackError(pc, "Failed to add building."); + return; + } + + building.setRot(rot); + building.setw(w); + + building.setObjectTypeMask(MBServerStatics.MASK_BUILDING); + WorldGrid.addObject(building, pc); + ChatManager.chatSayInfo(pc, + "Building with ID " + building.getObjectUUID() + " added"); + + this.setResult(String.valueOf(building.getObjectUUID())); + + } + + @Override + protected String _getHelpString() { + return "Creates a building of type 'buildingID' at the location your character is standing."; + } + + @Override + protected String _getUsageString() { + return "' /addbuilding buildingID rank' || ' /building buildingID rank'"; + } + +} diff --git a/src/engine/devcmd/cmds/AddGoldCmd.java b/src/engine/devcmd/cmds/AddGoldCmd.java new file mode 100644 index 00000000..2d339821 --- /dev/null +++ b/src/engine/devcmd/cmds/AddGoldCmd.java @@ -0,0 +1,77 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ChatManager; +import engine.objects.AbstractGameObject; +import engine.objects.Item; +import engine.objects.PlayerCharacter; + +/** + * @author Eighty + * + */ +public class AddGoldCmd extends AbstractDevCmd { + + public AddGoldCmd() { + super("addgold"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + if (words.length != 1) { + this.sendUsage(pc); + return; + } + + Item gold = pc.getCharItemManager().getGoldInventory(); + int curAmt; + if (gold == null) + curAmt = 0; + else + curAmt = gold.getNumOfItems(); + + int amt; + try { + amt = Integer.parseInt(words[0]); + } catch (NumberFormatException e) { + throwbackError(pc, "Quantity must be a number, " + words[0] + " is invalid"); + return; + } + if (amt < 1 || amt > 10000000) { + throwbackError(pc, "Quantity must be between 1 and 10000000 (10 million)"); + return; + } else if ((curAmt + amt) > 10000000) { + throwbackError(pc, "This would place your inventory over 10,000,000 gold."); + return; + } + + if (!pc.getCharItemManager().addGoldToInventory(amt, true)) { + throwbackError(pc, "Failed to add gold to inventory"); + return; + } + + ChatManager.chatSayInfo(pc, amt + " gold added to inventory"); + pc.getCharItemManager().updateInventory(); + } + + @Override + protected String _getHelpString() { + return "adds gold to inventory"; + } + + @Override + protected String _getUsageString() { + return "' /addGold quantity'"; + } + +} diff --git a/src/engine/devcmd/cmds/AddMobCmd.java b/src/engine/devcmd/cmds/AddMobCmd.java new file mode 100644 index 00000000..edbceaf7 --- /dev/null +++ b/src/engine/devcmd/cmds/AddMobCmd.java @@ -0,0 +1,111 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.Enum.GameObjectType; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.gameManager.ZoneManager; +import engine.math.Vector3fImmutable; +import engine.objects.*; +import org.pmw.tinylog.Logger; + +/** + * @author Eighty + * + */ +public class AddMobCmd extends AbstractDevCmd { + + public AddMobCmd() { + super("mob"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + if (words.length != 1) { + this.sendUsage(pc); + return; + } + + Zone zone = ZoneManager.findSmallestZone(pc.getLoc()); + + if (words[0].equals("all")){ + + for (AbstractGameObject mobbaseAGO: DbManager.getList(GameObjectType.MobBase)){ + MobBase mb = (MobBase)mobbaseAGO; + int loadID = mb.getObjectUUID(); + Mob mob = Mob.createMob( loadID, Vector3fImmutable.getRandomPointInCircle(pc.getLoc(), 100), + null, true, zone, null,0); + if (mob != null) { + mob.updateDatabase(); + this.setResult(String.valueOf(mob.getDBID())); + } else { + throwbackError(pc, "Failed to create mob of type " + loadID); + Logger.error( "Failed to create mob of type " + + loadID); + } + } + return; + } + + + int loadID; + try { + loadID = Integer.parseInt(words[0]); + } catch (NumberFormatException e) { + throwbackError(pc, "Supplied type " + words[0] + + " failed to parse to an Integer"); + return; + } catch (Exception e) { + throwbackError(pc, + "An unknown exception occurred when trying to use mob command for type " + + words[0]); + return; // NaN + } + + + if (zone == null) { + throwbackError(pc, "Failed to find zone to place mob in."); + return; + } + + if (zone.isPlayerCity()) { + throwbackError(pc, "Cannot use ./mob on Player cities. Try ./servermob instead."); + return; + } + + + Mob mob = Mob.createMob( loadID, pc.getLoc(), + null, true, zone, null,0); + if (mob != null) { + mob.updateDatabase(); + ChatManager.chatSayInfo(pc, + "Mob with ID " + mob.getDBID() + " added"); + this.setResult(String.valueOf(mob.getDBID())); + } else { + throwbackError(pc, "Failed to create mob of type " + loadID); + Logger.error("Failed to create mob of type " + + loadID); + } + } + + @Override + protected String _getHelpString() { + return "Creates a Mob of type 'mobID' at the location your character is standing"; + } + + @Override + protected String _getUsageString() { + return "' /mob mobID'"; + } + +} diff --git a/src/engine/devcmd/cmds/AddMobPowerCmd.java b/src/engine/devcmd/cmds/AddMobPowerCmd.java new file mode 100644 index 00000000..2b553b42 --- /dev/null +++ b/src/engine/devcmd/cmds/AddMobPowerCmd.java @@ -0,0 +1,98 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.Enum.GameObjectType; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.DbManager; +import engine.gameManager.PowersManager; +import engine.objects.AbstractGameObject; +import engine.objects.Mob; +import engine.objects.PlayerCharacter; +import engine.powers.PowersBase; + +/** + * + * @author Eighty + * + */ +public class AddMobPowerCmd extends AbstractDevCmd { + + public AddMobPowerCmd() { + super("addmobpower"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + + + if(args.length != 2){ + this.sendUsage(pcSender); + return; + } + + if (target.getObjectType() != GameObjectType.Mob){ + this.throwbackError(pcSender, "Target is not a valid Mob."); + return; + } + Mob mobTarget = (Mob)target; + + + int rank = 0; + String idString = args[0]; + + try{ + rank = Integer.valueOf(args[1]); + }catch(Exception e){ + this.throwbackInfo(pcSender, "Failed to Parse an Integer."); + return; + } + + PowersBase pb = PowersManager.getPowerByIDString(idString); + if (pb == null){ + this.throwbackError(pcSender, "not a valid Effect. IDString is Case Sensitive."); + return; + } + + if (!DbManager.MobBaseQueries.ADD_MOBBASE_POWER(mobTarget.getMobBaseID(), pb.getToken(), rank)){ + this.throwbackError(pcSender, "Failed to update Database"); + } + + mobTarget.getMobBase().updatePowers(); + + this.throwbackInfo(pcSender, "Successfuly added Power " + pb.getIDString() + " to Mobbase with UID " + mobTarget.getMobBaseID()); + + + } + + @Override + protected String _getUsageString() { + return "' /addmobpower poweridstring rank"; + } + + @Override + protected String _getHelpString() { + return "Temporarily add visual effects to Character"; + } + +} diff --git a/src/engine/devcmd/cmds/AddMobRuneCmd.java b/src/engine/devcmd/cmds/AddMobRuneCmd.java new file mode 100644 index 00000000..54a4ceef --- /dev/null +++ b/src/engine/devcmd/cmds/AddMobRuneCmd.java @@ -0,0 +1,98 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.Enum.GameObjectType; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.DbManager; +import engine.objects.AbstractGameObject; +import engine.objects.Mob; +import engine.objects.PlayerCharacter; +import engine.objects.RuneBase; + +/** + * + * @author Eighty + * + */ +public class AddMobRuneCmd extends AbstractDevCmd { + + public AddMobRuneCmd() { + super("addmobrune"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + + + if(args.length != 1){ + this.sendUsage(pcSender); + return; + } + + if (target.getObjectType() != GameObjectType.Mob){ + this.throwbackError(pcSender, "Target is not a valid Mob."); + return; + } + Mob mobTarget = (Mob)target; + + + int runeID = 0; + try{ + runeID = Integer.valueOf(args[0]); + }catch(Exception e){ + this.throwbackInfo(pcSender, "Failed to Parse an Integer."); + return; + } + + RuneBase rune = RuneBase.getRuneBase(runeID); + if (rune == null){ + this.throwbackError(pcSender, "Invalid Rune ID"); + return; + } + + + if (!DbManager.MobBaseQueries.ADD_MOBBASE_RUNE(mobTarget.getMobBaseID(), runeID)){ + this.throwbackError(pcSender, "Failed to update Database"); + return; + } + + mobTarget.getMobBase().updateRunes(); + + this.throwbackInfo(pcSender, "Successfuly added rune " + rune.getName() + " to Mobbase with UID " + mobTarget.getMobBaseID()); + + + + } + + @Override + protected String _getUsageString() { + return "' /visualeffect visualeffectID"; + } + + @Override + protected String _getHelpString() { + return "Temporarily add visual effects to Character"; + } + +} diff --git a/src/engine/devcmd/cmds/AddNPCCmd.java b/src/engine/devcmd/cmds/AddNPCCmd.java new file mode 100644 index 00000000..4d3f5716 --- /dev/null +++ b/src/engine/devcmd/cmds/AddNPCCmd.java @@ -0,0 +1,111 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.Enum.GameObjectType; +import engine.InterestManagement.WorldGrid; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.BuildingManager; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.gameManager.ZoneManager; +import engine.objects.*; +import org.pmw.tinylog.Logger; + +/** + * @author Eighty + * + */ +public class AddNPCCmd extends AbstractDevCmd { + + public AddNPCCmd() { + super("npc"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + int contractID; + String name = ""; + int level = 0; + + if (words.length < 2) { + this.sendUsage(pc); + return; + } + try { + contractID = Integer.parseInt(words[0]); + level = Integer.parseInt(words[1]); + + for (int i = 2; i < words.length; i++) { + name += words[i]; + if (i + 1 < words.length) + name += ""; + } + + } catch (NumberFormatException e) { + throwbackError(pc, + "Failed to parse supplied contractID or level to an Integer."); + return; // NaN + } + + Contract contract = DbManager.ContractQueries.GET_CONTRACT(contractID); + + if (contract == null || level < 1 || level > 75) { + throwbackError(pc, + "Invalid addNPC Command. Need contract ID, and level"); + return; // NaN + } + + // Pick a random name + if (name.isEmpty()) + name = NPC.getPirateName(contract.getMobbaseID()); + + Zone zone = ZoneManager.findSmallestZone(pc.getLoc()); + + if (zone == null) { + throwbackError(pc, "Failed to find zone to place npc in."); + return; + } + + if (target != null) + if (target.getObjectType() == GameObjectType.Building){ + Building parentBuilding = (Building)target; + BuildingManager.addHirelingForWorld(parentBuilding, pc, parentBuilding.getLoc(), parentBuilding.getParentZone(), contract, level); + return; + } + + NPC npc = NPC.createNPC(name, contractID, + pc.getLoc(), null, true, zone, (short)level, true, null); + + if (npc != null) { + WorldGrid.addObject(npc, pc); + ChatManager.chatSayInfo(pc, + "NPC with ID " + npc.getDBID() + " added"); + this.setResult(String.valueOf(npc.getDBID())); + } else { + throwbackError(pc, "Failed to create npc of contract type " + + contractID); + Logger.error( + "Failed to create npc of contract type " + contractID); + } + } + + @Override + protected String _getHelpString() { + return "Creates an NPC of type 'npcID' at the location your character is standing"; + } + + @Override + protected String _getUsageString() { + return "' /npc npcID level name'"; + } + +} diff --git a/src/engine/devcmd/cmds/ApplyBonusCmd.java b/src/engine/devcmd/cmds/ApplyBonusCmd.java new file mode 100644 index 00000000..76234e1c --- /dev/null +++ b/src/engine/devcmd/cmds/ApplyBonusCmd.java @@ -0,0 +1,158 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.Enum.PowerActionType; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ChatManager; +import engine.gameManager.PowersManager; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; +import engine.powers.effectmodifiers.AbstractEffectModifier; +import engine.util.ThreadUtils; +import org.pmw.tinylog.Logger; + +import java.util.HashMap; +import java.util.HashSet; + +public class ApplyBonusCmd extends AbstractDevCmd { + + public ApplyBonusCmd() { + super("applybonus"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] words, + AbstractGameObject target) { + + String action = words[0]; + + PowerActionType actionType = null; + + HashMap> appliedMods = new HashMap<>(); + + try{ + + if (action.equals("all") == false) + for (PowerActionType powerActionType : PowerActionType.values()){ + if (powerActionType.name().equalsIgnoreCase(action) == false) + continue; + actionType = powerActionType; + break; + } + + }catch(Exception e){ + this.throwbackError(pcSender, "Invalid power Action type for " + action); + this.throwbackInfo(pcSender, "Valid Types : " + this.getActionTypes()); + return; + } + if (action.equals("all") == false) + if (actionType == null){ + this.throwbackError(pcSender, "Invalid power Action type for " + action); + this.throwbackInfo(pcSender, "Valid Types : " + this.getActionTypes()); + return; + } + + + for (PowersBase pb : PowersManager.powersBaseByIDString.values()){ + if (pb.getActions() == null || pb.getActions().isEmpty()) + continue; + + for (ActionsBase ab: pb.getActions()){ + if (ab.getPowerAction() == null) + continue; + if (action.equals("all") == false) + if (ab.getPowerAction().getType().equalsIgnoreCase(action) == false) + continue; + String effect1 = ""; + String effect2 = ""; + ChatManager.chatSystemInfo(pcSender,"Applying Power " + pb.getName() + " : " +pb.getDescription()); + if (ab.getPowerAction().getEffectsBase() == null){ + + try { + PowersManager.runPowerAction(pcSender, pcSender, pcSender.getLoc(), ab, 1, pb); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + ThreadUtils.sleep(500); + continue; + } + + + if (ab.getPowerAction().getEffectsBase().getModifiers() == null || ab.getPowerAction().getEffectsBase().getModifiers().isEmpty()){ + try { + PowersManager.runPowerAction(pcSender, pcSender, pcSender.getLoc(), ab, 1, pb); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + continue; + } + + boolean run = true; + for (AbstractEffectModifier mod : ab.getPowerAction().getEffectsBase().getModifiers()){ + if (appliedMods.containsKey(mod.modType.name()) == false){ + appliedMods.put(mod.modType.name(), new HashSet<>()); + } + +// if (appliedMods.get(mod.modType.name()).contains(mod.sourceType.name())){ +// continue; +// } + + appliedMods.get(mod.modType.name()).add(mod.sourceType.name()); + try{ + try { + PowersManager.runPowerAction(pcSender, pcSender, pcSender.getLoc(), ab, 1, pb); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + }catch(Exception e){ + Logger.error(e); + } + break; + + + + + } + + } + } + } + + + @Override + protected String _getUsageString() { + return "' /bounds'"; + } + + @Override + protected String _getHelpString() { + return "Audits all the mobs in a zone."; + + } + + private String getActionTypes(){ + String output = ""; + + for (PowerActionType actionType : PowerActionType.values()){ + output += actionType.name() + " | "; + + } + + return output.substring(0, output.length() -3); + } + +} diff --git a/src/engine/devcmd/cmds/ApplyStatModCmd.java b/src/engine/devcmd/cmds/ApplyStatModCmd.java new file mode 100644 index 00000000..21bf52ab --- /dev/null +++ b/src/engine/devcmd/cmds/ApplyStatModCmd.java @@ -0,0 +1,149 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ChatManager; +import engine.gameManager.PowersManager; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; +import engine.powers.PowersBase; + +import java.util.ArrayList; + +/** + * + * @author Eighty + * + */ +public class ApplyStatModCmd extends AbstractDevCmd { + + public ApplyStatModCmd() { + super("applystatmod"); + } + + private static int cnt = 0; + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + if(args.length < 1) { + // if(args.length < 2) { + this.sendUsage(pcSender); + return; + } + + if(!(target instanceof AbstractCharacter)) { + this.sendHelp(pcSender); + return; + } + + this.setTarget(pcSender); //for logging + + int spellID; + int powerAction = 0; + if (args[0].toLowerCase().contains("all")){ + + int amount = 0; + if (args.length == 1) { + amount = ApplyStatModCmd.cnt; + ApplyStatModCmd.cnt++; + } else { + amount = Integer.valueOf(args[1]); + ApplyStatModCmd.cnt = amount+1; + } + + + ArrayList pbList = new ArrayList<>(); + pbList.add(PowersManager.getPowerByToken(429047968)); + pbList.add(PowersManager.getPowerByToken(429768864)); + pbList.add(PowersManager.getPowerByToken(428458144)); + pbList.add(PowersManager.getPowerByToken(428677994)); + pbList.add(PowersManager.getPowerByToken(431874079)); + pbList.add(PowersManager.getPowerByToken(431081336)); + + + + + + for (PowersBase pb:pbList){ + if(amount <= 0) { + if (pb.getToken() ==428677994) + powerAction = 1; + PowersManager.removeEffect(pcSender, pb.getActions().get(powerAction), false, false); + continue; + } else if(amount > 9999 || amount < 21) { + ChatManager.chatSystemInfo(pcSender, "Amount must be between 21 and 9999 inclusive."); + return; + } + + if (pb.getToken() ==428677994){ + PowersManager.removeEffect(pcSender, pb.getActions().get(powerAction), false, false); + PowersManager.runPowerAction(pcSender, pcSender, Vector3fImmutable.ZERO, pb.getActions().get(powerAction), amount - 20, pb); + } + if (pb.getToken() ==428677994) + powerAction = 1; + PowersManager.removeEffect(pcSender, pb.getActions().get(powerAction), false, false); + PowersManager.runPowerAction(pcSender, pcSender, Vector3fImmutable.ZERO, pb.getActions().get(powerAction), amount - 20, pb); + } + return; + } + if(args[0].toLowerCase().contains("con")) { + spellID = 429047968; //Blessing of Health + } else if(args[0].toLowerCase().contains("str")) { + spellID = 429768864; //Blessing of Might + } else if(args[0].toLowerCase().contains("dex")) { + spellID = 428458144; //Blessing of Dexterity + } else if(args[0].toLowerCase().contains("int")) { + spellID = 428677994; //Bard Spi - TODO + powerAction = 1; + } else if(args[0].toLowerCase().contains("spi")) { + spellID = 428677994; //Bard Spi + } else{ + ChatManager.chatSystemInfo(pcSender, "No valid stat found."); + return; + } + + PowersBase pb = PowersManager.getPowerByToken(spellID); + + int amount = 0; + if (args.length == 1) { + amount = ApplyStatModCmd.cnt; + ApplyStatModCmd.cnt++; + } else { + amount = Integer.valueOf(args[1]); + ApplyStatModCmd.cnt = amount+1; + } + // int amount = Integer.valueOf(args[1]); + if(amount <= 0) { + PowersManager.removeEffect(pcSender, pb.getActions().get(powerAction), false, false); + return; + } else if(amount > 9999 || amount < 21) { + ChatManager.chatSystemInfo(pcSender, "Amount must be between 21 and 9999 inclusive."); + return; + } + + PowersManager.removeEffect(pcSender, pb.getActions().get(powerAction), false, false); + PowersManager.runPowerAction(pcSender, pcSender, Vector3fImmutable.ZERO, pb.getActions().get(powerAction), amount - 20, pb); + } + + @Override + protected String _getUsageString() { + return "' /applystatmod [trains]'"; + } + + @Override + protected String _getHelpString() { + return "You must be targeting a player!"; + } + +} diff --git a/src/engine/devcmd/cmds/AuditFailedItemsCmd.java b/src/engine/devcmd/cmds/AuditFailedItemsCmd.java new file mode 100644 index 00000000..216e05fb --- /dev/null +++ b/src/engine/devcmd/cmds/AuditFailedItemsCmd.java @@ -0,0 +1,130 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.Enum.ModType; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.PowersManager; +import engine.net.ItemProductionManager; +import engine.objects.*; +import engine.powers.effectmodifiers.AbstractEffectModifier; +import engine.powers.poweractions.AbstractPowerAction; +import org.pmw.tinylog.Logger; + +public class AuditFailedItemsCmd extends AbstractDevCmd { + + public AuditFailedItemsCmd() { + super("faileditems"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] words, + AbstractGameObject target) { + + + + if (ItemProductionManager.FailedItems.isEmpty()) + return; + + Logger.info("Auditing Item production Failed Items"); + + String newLine = "\r\n"; + String auditFailedItem = "Failed Item Name | Prefix | Suffix | NPC | Contract | Player | "; + + for (ProducedItem failedItem: ItemProductionManager.FailedItems){ + + String npcName = ""; + String playerName =""; + String contractName = ""; + + String prefix = ""; + String suffix = ""; + String itemName = ""; + NPC npc = NPC.getFromCache(failedItem.getNpcUID()); + + if (npc == null){ + npcName = "null"; + contractName = "null"; + }else{ + npcName = npc.getName(); + if (npc.getContract() != null) + contractName = npc.getContract().getName(); + } + + PlayerCharacter roller = PlayerCharacter.getFromCache(failedItem.getPlayerID()); + + if (roller == null) + playerName = "null"; + else + playerName = roller.getName(); + + ItemBase ib = ItemBase.getItemBase(failedItem.getItemBaseID()); + + if (ib != null){ + itemName = ib.getName(); + } + + if (failedItem.isRandom() == false){ + if (failedItem.getPrefix().isEmpty() == false){ + AbstractPowerAction pa = PowersManager.getPowerActionByIDString(failedItem.getPrefix()); + if (pa != null){ + for (AbstractEffectModifier aem : pa.getEffectsBase().getModifiers()){ + if (aem.modType.equals(ModType.ItemName)){ + prefix = aem.getString1(); + break; + } + } + } + + } + + if (failedItem.getSuffix().isEmpty() == false){ + AbstractPowerAction pa = PowersManager.getPowerActionByIDString(failedItem.getSuffix()); + if (pa != null){ + for (AbstractEffectModifier aem : pa.getEffectsBase().getModifiers()){ + if (aem.modType.equals(ModType.ItemName)){ + suffix = aem.getString1(); + break; + } + } + } + + } + + }else{ + prefix = "random"; + } + + + + + auditFailedItem += newLine; + auditFailedItem += itemName + " | "+prefix + " | "+suffix + " | "+ failedItem.getNpcUID() + ":" +npcName + " | "+contractName + " | "+failedItem.getPlayerID() + ":" +playerName; + + } + Logger.info(auditFailedItem); + ItemProductionManager.FailedItems.clear(); + + + } + + + @Override + protected String _getUsageString() { + return "' /bounds'"; + } + + @Override + protected String _getHelpString() { + return "Audits all the mobs in a zone."; + + } + +} diff --git a/src/engine/devcmd/cmds/AuditHeightMapCmd.java b/src/engine/devcmd/cmds/AuditHeightMapCmd.java new file mode 100644 index 00000000..74e9005a --- /dev/null +++ b/src/engine/devcmd/cmds/AuditHeightMapCmd.java @@ -0,0 +1,71 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.InterestManagement.HeightMap; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ZoneManager; +import engine.math.Vector2f; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; +import engine.objects.Zone; + +public class AuditHeightMapCmd extends AbstractDevCmd { + + public AuditHeightMapCmd() { + super("auditheightmap"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] words, + AbstractGameObject target) { + + int count = Integer.parseInt(words[0]); + long start = System.currentTimeMillis(); + for (int i = 0; i= count){ + plusplus++; + throwbackInfo(pcSender, zoneMicro.getName() + " at location " + zoneMicro.getLoc().toString() + " has " + size + " mobs. "); + System.out.println(zoneMicro.getName() + " at location " + zoneMicro.getLoc().toString() + " has " + size + " mobs. "); + } + + + } + throwbackInfo(pcSender," there are " +plusplus + " zones with at least " + count + " mobs in each."); + } + return; + } + if (words.length > 1) { + this.sendUsage(pcSender); + return; + } else if (words.length == 1) { + int uuid; + try { + uuid = Integer.parseInt(words[0]); + zone = ZoneManager.getZoneByUUID(uuid); + } catch (NumberFormatException e) { + zone = ZoneManager.findSmallestZone(pcSender.getLoc()); + } + } else + zone = ZoneManager.findSmallestZone(pcSender.getLoc()); + + if (zone == null) { + throwbackError(pcSender, "Unable to find the zone"); + return; + } + + //get list of mobs for zone + + if (zone.zoneMobSet.isEmpty()) { + throwbackError(pcSender, "No mobs found for this zone."); + return; + } + + // ConcurrentHashMap spawnMap = Mob.getSpawnMap(); + //ConcurrentHashMap respawnMap = Mob.getRespawnMap(); + // ConcurrentHashMap despawnMap = Mob.getDespawnMap(); + + throwbackInfo(pcSender, zone.getName() + ", numMobs: " + zone.zoneMobSet.size()); + throwbackInfo(pcSender, "UUID, dbID, inRespawnMap, isAlive, activeAI, Loc"); + + + + //mob not found in spawn map, check respawn + boolean inRespawn = false; + + + + } + + + @Override + protected String _getUsageString() { + return "' /auditmobs [zone.UUID]'"; + } + + @Override + protected String _getHelpString() { + return "Audits all the mobs in a zone."; + + } + +} diff --git a/src/engine/devcmd/cmds/BoundsCmd.java b/src/engine/devcmd/cmds/BoundsCmd.java new file mode 100644 index 00000000..dc5e6835 --- /dev/null +++ b/src/engine/devcmd/cmds/BoundsCmd.java @@ -0,0 +1,77 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.Enum.GameObjectType; +import engine.devcmd.AbstractDevCmd; +import engine.objects.AbstractGameObject; +import engine.objects.Building; +import engine.objects.PlayerCharacter; + +public class BoundsCmd extends AbstractDevCmd { + + public BoundsCmd() { + super("bounds"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] words, + AbstractGameObject target) { + if (target == null || !target.getObjectType().equals(GameObjectType.Building)){ + this.throwbackError(pcSender, "No Building Selected"); + return; + } + + Building building = (Building)target; + + if (building.getBounds() == null){ + this.throwbackInfo(pcSender, "No valid Bounds for building UUID " + building.getObjectUUID()); + return; + } + float topLeftX = building.getLoc().x - building.getBounds().getHalfExtents().x; + float topLeftY = building.getLoc().z - building.getBounds().getHalfExtents().y; + + float topRightX = building.getLoc().x + building.getBounds().getHalfExtents().x; + float topRightY = building.getLoc().z - building.getBounds().getHalfExtents().y; + + float bottomLeftX = building.getLoc().x - building.getBounds().getHalfExtents().x; + float bottomLeftY = building.getLoc().z + building.getBounds().getHalfExtents().y; + + float bottomRightX = building.getLoc().x + building.getBounds().getHalfExtents().x; + float bottomRightY = building.getLoc().z + building.getBounds().getHalfExtents().y; + + String newLine = "\r\n "; + + String output = "Bounds Information for Building UUID " + building.getObjectUUID(); + output += newLine; + + output+= "Top Left : " + topLeftX + " , " + topLeftY + newLine; + output+= "Top Right : " + topRightX + " , " + topRightY + newLine; + output+= "Bottom Left : " + bottomLeftX + " , " + bottomLeftY + newLine; + output+= "Bottom Right : " + bottomRightX + " , " + bottomRightY + newLine; + + this.throwbackInfo(pcSender, output); + + + } + + + @Override + protected String _getUsageString() { + return "' /bounds'"; + } + + @Override + protected String _getHelpString() { + return "Audits all the mobs in a zone."; + + } + +} diff --git a/src/engine/devcmd/cmds/ChangeNameCmd.java b/src/engine/devcmd/cmds/ChangeNameCmd.java new file mode 100644 index 00000000..3ef3e095 --- /dev/null +++ b/src/engine/devcmd/cmds/ChangeNameCmd.java @@ -0,0 +1,114 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.DbManager; +import engine.gameManager.SessionManager; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; +import engine.util.MiscUtils; + +public class ChangeNameCmd extends AbstractDevCmd { + + public ChangeNameCmd() { + super("changename"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + Vector3fImmutable loc = null; + + // Arg Count Check + if (words.length < 2) { + this.sendUsage(pc); + return; + } + + String oldFirst = words[0]; + String newFirst = words[1]; + String newLast = ""; + if (words.length > 2) { + newLast = words[2]; + for (int i=3; i 50) { + this.throwbackError(pc, "Error: Last name is incorrect length. Must be no more than 50 characters"); + return; + } + + // Check if firstname is valid + if (MiscUtils.checkIfFirstNameInvalid(newFirst)) { + this.throwbackError(pc, "Error: First name is not allowed"); + return; + } + + + //get the world ID we're modifying for + + //test if first name is unique, unless new and old first name are equal. + if (!(oldFirst.equals(newFirst))) { + if (!DbManager.PlayerCharacterQueries.IS_CHARACTER_NAME_UNIQUE(newFirst)) { + this.throwbackError(pc, "Error: First name is not unique."); + return; + } + } + + //tests passed, update name in database + if (!DbManager.PlayerCharacterQueries.UPDATE_NAME(oldFirst, newFirst, newLast)) { + this.throwbackError(pc, "Error: Database failed to update the name."); + return; + } + + //Finally update player ingame + PlayerCharacter pcTar = null; + try { + pcTar = SessionManager + .getPlayerCharacterByLowerCaseName(words[0]); + pcTar.setFirstName(newFirst); + pcTar.setLastName(newLast); + this.setTarget(pcTar); //for logging + + //specify if last name is ascii characters only + String lastAscii = newLast.replaceAll("[^\\p{ASCII}]", ""); + pcTar.setAsciiLastName(lastAscii.equals(newLast)); + } catch (Exception e) { + this.throwbackError(pc, "Database was updated but ingame character failed to update."); + return; + } + + String out = oldFirst + " was changed to " + newFirst + (newLast.isEmpty() ? "." : (' ' + newLast + '.')); + this.throwbackInfo(pc, out); + + + } + + @Override + protected String _getHelpString() { + return "Changes the name of a player"; + } + + @Override + protected String _getUsageString() { + return "'./changename oldFirstName newFirstName newLastName'"; + } + +} diff --git a/src/engine/devcmd/cmds/CombatMessageCmd.java b/src/engine/devcmd/cmds/CombatMessageCmd.java new file mode 100644 index 00000000..f1847562 --- /dev/null +++ b/src/engine/devcmd/cmds/CombatMessageCmd.java @@ -0,0 +1,70 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.net.client.msg.TargetedActionMsg; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; + +/** + * + * @author Eighty + * + */ +public class CombatMessageCmd extends AbstractDevCmd { + + public CombatMessageCmd() { + super("cm"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + if (pcSender == null) + return; + if (args.length != 1) { + this.sendUsage(pcSender); + return; + } + int num = 0; + try { + num = Integer.parseInt(args[0]); + } catch (NumberFormatException e) { + throwbackError(pcSender, "Supplied message number " + args[0] + " failed to parse to an Integer"); + return; + } + TargetedActionMsg.un2cnt = num; + throwbackInfo(pcSender, "CombatMessage set to " + num); + } + + @Override + protected String _getUsageString() { + return "' /cm [cmNumber]'"; + } + + @Override + protected String _getHelpString() { + return "Sets the combat message to the supplied integer value"; + } + +} diff --git a/src/engine/devcmd/cmds/CopyMobCmd.java b/src/engine/devcmd/cmds/CopyMobCmd.java new file mode 100644 index 00000000..fa68b709 --- /dev/null +++ b/src/engine/devcmd/cmds/CopyMobCmd.java @@ -0,0 +1,84 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ChatManager; +import engine.objects.AbstractGameObject; +import engine.objects.MobBase; +import engine.objects.PlayerCharacter; + +/** + * @author Eighty + * + */ +public class CopyMobCmd extends AbstractDevCmd { + + public CopyMobCmd() { + super("copymob"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + if (words.length < 1) { + this.sendUsage(pc); + return; + } + + int loadID = 0; + String name = ""; + try { + loadID = Integer.parseInt(words[0]); + if (words.length > 1) { + name = words[1]; + for (int i=2; i '"; + } + +} diff --git a/src/engine/devcmd/cmds/DebugCmd.java b/src/engine/devcmd/cmds/DebugCmd.java new file mode 100644 index 00000000..3a3f0110 --- /dev/null +++ b/src/engine/devcmd/cmds/DebugCmd.java @@ -0,0 +1,123 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.jobs.DebugTimerJob; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; + +public class DebugCmd extends AbstractDevCmd { + + + + public DebugCmd() { + super("debug"); + // super("debug", MBServerStatics.ACCESS_GROUP_ALL_TEAM, 0, false, false); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + if (words.length < 2) { + this.sendUsage(pc); + return; + } + + if (pc == null) + return; + + //pc.setDebug must use bit sizes: 1, 2, 4, 8, 16, 32 + + String command = words[0].toLowerCase(); + boolean toggle = (words[1].toLowerCase().equals("on")) ? true : false; + + switch (command) { + case "magictrek": + pc.RUN_MAGICTREK = toggle; + + break; + case "combat": + pc.setDebug(64, toggle); + + break; + case "health": + toggleDebugTimer(pc, "Debug_Health", 1, 1000, toggle); + + break; + case "mana": + toggleDebugTimer(pc, "Debug_Mana", 2, 1000, toggle); + + break; + case "stamina": + toggleDebugTimer(pc, "Debug_Stamina", 3, 500, toggle); + + break; + case "spells": + pc.setDebug(16, toggle); + + break; + case "damageabsorber": + pc.setDebug(2, toggle); + + break; + case "recast": + case "recycle": + pc.setDebug(4, toggle); + + break; + case "seeinvis": + pc.setDebug(8, toggle); + + break; + case "movement": + pc.setDebug(1, toggle); + + break; + case "noaggro": + pc.setDebug(32, toggle); + + break; + // case "loot": + // MBServerStatics.debugLoot = toggle; + // break; + + default: + String output = "Debug for " + command + " not found."; + throwbackError(pc, output); + return; + } + + + setTarget(pc); //for logging + + String output = "Debug for " + command + " turned " + ((toggle) ? "on." : "off."); + throwbackInfo(pc, output); + } + + @Override + protected String _getHelpString() { + return "Runs debug commands"; + + } + + @Override + protected String _getUsageString() { + return "'./Debug command on/off'"; + } + + private static void toggleDebugTimer(PlayerCharacter pc, String name, int num, int duration, boolean toggle) { + if (toggle) { + DebugTimerJob dtj = new DebugTimerJob(pc, name, num, duration); + pc.renewTimer(name, dtj, duration); + } else + pc.cancelTimer(name); + } +} diff --git a/src/engine/devcmd/cmds/DebugMeleeSyncCmd.java b/src/engine/devcmd/cmds/DebugMeleeSyncCmd.java new file mode 100644 index 00000000..baa189dc --- /dev/null +++ b/src/engine/devcmd/cmds/DebugMeleeSyncCmd.java @@ -0,0 +1,58 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ChatManager; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; + +public class DebugMeleeSyncCmd extends AbstractDevCmd { + + public DebugMeleeSyncCmd() { + super("debugmeleesync"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + // Arg Count Check + if (words.length != 1) { + this.sendUsage(pc); + return; + } + + String s = words[0].toLowerCase(); + + if (s.equals("on")) { + pc.setDebug(64, true); + ChatManager.chatSayInfo(pc, "Melee Sync Debug ON"); + } else if (s.equals("off")) { + pc.setDebug(64, false); + ChatManager.chatSayInfo(pc, "Melee Sync Debug OFF"); + } else { + this.sendUsage(pc); + } + } + + @Override + protected String _getHelpString() { + return "Turns on/off melee sync debugging."; + + } + + @Override + protected String _getUsageString() { + return "'./debugmeleesync on|off'"; + + } + +} diff --git a/src/engine/devcmd/cmds/DecachePlayerCmd.java b/src/engine/devcmd/cmds/DecachePlayerCmd.java new file mode 100644 index 00000000..ef9caf4f --- /dev/null +++ b/src/engine/devcmd/cmds/DecachePlayerCmd.java @@ -0,0 +1,54 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.Enum; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.DbManager; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; + +/** + * + */ +public class DecachePlayerCmd extends AbstractDevCmd { + + public DecachePlayerCmd() { + super("decacheplayer"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + if(words.length < 1) { + this.sendUsage(pc); + } + + int objectUUID = Integer.parseInt(words[0]); + + if(DbManager.inCache(Enum.GameObjectType.PlayerCharacter, objectUUID)) { + this.setTarget(PlayerCharacter.getFromCache(objectUUID)); //for logging + PlayerCharacter.getFromCache(objectUUID).removeFromCache(); + } else { + this.sendHelp(pc); + } + } + + @Override + protected String _getHelpString() { + return "No player found. Please make sure the table ID is correct."; + } + + @Override + protected String _getUsageString() { + return "' /decacheplayer '"; + } + +} diff --git a/src/engine/devcmd/cmds/DespawnCmd.java b/src/engine/devcmd/cmds/DespawnCmd.java new file mode 100644 index 00000000..18d54437 --- /dev/null +++ b/src/engine/devcmd/cmds/DespawnCmd.java @@ -0,0 +1,73 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.Enum.GameObjectType; +import engine.devcmd.AbstractDevCmd; +import engine.objects.AbstractGameObject; +import engine.objects.Mob; +import engine.objects.PlayerCharacter; + +public class DespawnCmd extends AbstractDevCmd { + + public DespawnCmd() { + super("debugmob"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + + + + + if (pc == null) { + return; + } + + + + + + Mob mob = null; + + if (target != null && target.getObjectType().equals(GameObjectType.Mob)) + mob = (Mob)target; + + if (mob == null) + mob = Mob.getFromCache(Integer.parseInt(words[1])); + + if (mob == null) + return; + + if (words[0].equalsIgnoreCase("respawn")){ + mob.respawn(); + this.throwbackInfo(pc, "Mob with ID " + mob.getObjectUUID() + " Respawned"); + }else if (words[0].equalsIgnoreCase("despawn")){ + mob.despawn(); + this.throwbackInfo(pc, "Mob with ID " + mob.getObjectUUID() + " Despawned"); + } + } + + @Override + protected String _getHelpString() { + return "Gets distance from a target."; + + } + + @Override + protected String _getUsageString() { + return "' /distance'"; + + } + +} diff --git a/src/engine/devcmd/cmds/DistanceCmd.java b/src/engine/devcmd/cmds/DistanceCmd.java new file mode 100644 index 00000000..458531c2 --- /dev/null +++ b/src/engine/devcmd/cmds/DistanceCmd.java @@ -0,0 +1,68 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractGameObject; +import engine.objects.AbstractWorldObject; +import engine.objects.PlayerCharacter; + +public class DistanceCmd extends AbstractDevCmd { + + public DistanceCmd() { + super("distance"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + + + + // Arg Count Check + if (words.length != 1) { + this.sendUsage(pc); + return; + } + if (pc == null) { + return; + } + + if (target == null || !(target instanceof AbstractWorldObject)) { + throwbackError(pc, "No target found."); + return; + } + + AbstractWorldObject awoTarget = (AbstractWorldObject)target; + + Vector3fImmutable pcLoc = pc.getLoc(); + Vector3fImmutable tarLoc = awoTarget.getLoc(); + String out = "Distance: " + pcLoc.distance(tarLoc) + + "\r\nYour Loc: " + pcLoc.x + 'x' + pcLoc.y + 'x' + pcLoc.z + + "\r\nTarget Loc: " + tarLoc.x + 'x' + tarLoc.y + 'x' + tarLoc.z; + throwbackInfo(pc, out); + } + + @Override + protected String _getHelpString() { + return "Gets distance from a target."; + + } + + @Override + protected String _getUsageString() { + return "' /distance'"; + + } + +} diff --git a/src/engine/devcmd/cmds/EffectCmd.java b/src/engine/devcmd/cmds/EffectCmd.java new file mode 100644 index 00000000..74733945 --- /dev/null +++ b/src/engine/devcmd/cmds/EffectCmd.java @@ -0,0 +1,63 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ChatManager; +import engine.gameManager.PowersManager; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; +import engine.powers.EffectsBase; + +/** + * + * @author Eighty + * + */ +public class EffectCmd extends AbstractDevCmd { + + public EffectCmd() { + super("effect"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + int ID = 0; + int token = 0; + + if (args.length != 2) { + this.sendUsage(pcSender); + return; + } + ID = Integer.parseInt(args[0]); + token = Integer.parseInt(args[1]); + + EffectsBase eb = PowersManager.setEffectToken(ID, token); + if (eb == null) { + throwbackError(pcSender, "Unable to find EffectsBase " + ID + + " to modify."); + return; + } + ChatManager.chatSayInfo(pcSender, + "EffectsBase with ID " + ID + " changed token to " + token); + } + + @Override + protected String _getUsageString() { + return "' /effect EffectsBaseID Token'"; + } + + @Override + protected String _getHelpString() { + return "Temporarily places the effect token with the corresponding EffectsBase on the server"; + } + +} diff --git a/src/engine/devcmd/cmds/EnchantCmd.java b/src/engine/devcmd/cmds/EnchantCmd.java new file mode 100644 index 00000000..b6f2247c --- /dev/null +++ b/src/engine/devcmd/cmds/EnchantCmd.java @@ -0,0 +1,88 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.objects.*; + +public class EnchantCmd extends AbstractDevCmd { + + public EnchantCmd() { + super("enchant"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + int rank = 0; + if (words.length < 1) { + this.sendUsage(pc); + return; + } + + + try{ + rank = Integer.parseInt(words[0]); + }catch(Exception e){ + + } + + + + Item item; + if (target == null || target instanceof Item) + item = (Item) target; + else { + throwbackError(pc, "Must have an item targeted"); + return; + } + + CharacterItemManager cim = pc.getCharItemManager(); + if (cim == null) { + throwbackError(pc, "Unable to find the character item manager for player " + pc.getFirstName() + '.'); + return; + } + + if (words[0].equals("clear")) { + item.clearEnchantments(); + cim.updateInventory(); + this.setResult(String.valueOf(item.getObjectUUID())); + } else { + int cnt = words.length; + for (int i=1;i container = WorldGrid.getObjectsInRangePartial( + searchPt, range, MBServerStatics.MASK_BUILDING); + + s += "Found " + container.size(); + s += " buildings within " + range; + s += " units of [" + searchPt.toString() + ']'; + throwbackInfo(pc, s); + + int index = 0; + for (AbstractWorldObject awo : container) { + Building b = (Building) awo; + + s = index + ")"; + s += " ObjectID: " + awo.getObjectUUID() + ']'; + s += " -> Name: " + b.getSimpleName(); + if (b.getBlueprint() == null) { + s += " No Blueprint"; + } else { + s += " Blueprint UUID: " + b.getBlueprint().getMeshForRank(0); + } + s += "[" + ((Building) awo).getBlueprintUUID() + ']'; + + throwbackInfo(pc, s); + ++index; + } + + } catch (NumberFormatException e) { + this.throwbackError(pc, "Supplied data: '" + words + + "' failed to parse to a Float."); + } catch (Exception e) { + this.throwbackError(pc, + "An unknown exception occurred while attempting to findBuildings with data: '" + + words + '\''); + } + } + + @Override + protected String _getHelpString() { + return "Sets your character's Mana to 'amount'"; + } + + @Override + protected String _getUsageString() { + return "' /findBuildings [lat long] [range]'"; + } + +} diff --git a/src/engine/devcmd/cmds/FlashMsgCmd.java b/src/engine/devcmd/cmds/FlashMsgCmd.java new file mode 100644 index 00000000..e500ca74 --- /dev/null +++ b/src/engine/devcmd/cmds/FlashMsgCmd.java @@ -0,0 +1,68 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ChatManager; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; + +/** + * + * @author Eighty + * + */ +public class FlashMsgCmd extends AbstractDevCmd { + + public FlashMsgCmd() { + super("flash"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + if (args.length != 1 || args[0].isEmpty()) { + this.sendUsage(pcSender); + return; + } + ChatManager.chatSystemFlash(args[0]); + } + + /** + * This function is called by the DevCmdManager. Override to avoid splitting + * argString into String array, since flash displays full String as message, + * then calls the subclass specific _doCmd method. + * + */ + + @Override + public void doCmd(PlayerCharacter pcSender, String argString, + AbstractGameObject target) { + String[] args = new String[1]; + args[0] = argString; + + if (pcSender == null) { + return; + } + + this._doCmd(pcSender, args, target); + } + + @Override + protected String _getUsageString() { + return "' /flash message'"; + } + + @Override + protected String _getHelpString() { + return "Flashes system message to all players"; + } + +} diff --git a/src/engine/devcmd/cmds/GateInfoCmd.java b/src/engine/devcmd/cmds/GateInfoCmd.java new file mode 100644 index 00000000..44bfcf76 --- /dev/null +++ b/src/engine/devcmd/cmds/GateInfoCmd.java @@ -0,0 +1,87 @@ +package engine.devcmd.cmds; + +import engine.Enum.BuildingGroup; +import engine.Enum.GameObjectType; +import engine.Enum.RunegateType; +import engine.devcmd.AbstractDevCmd; +import engine.math.Vector3fImmutable; +import engine.objects.*; + +public class GateInfoCmd extends AbstractDevCmd { + + public GateInfoCmd() { + super("gateinfo"); + } + + // AbstractDevCmd Overridden methods + + @Override + protected void _doCmd(PlayerCharacter player, String[] args, + AbstractGameObject target) { + + Building targetBuilding; + String outString; + RunegateType runegateType; + Runegate runeGate; + Blueprint blueprint; + String newline = "\r\n "; + targetBuilding = (Building)target; + + if (targetBuilding.getObjectType() != GameObjectType.Building) { + throwbackInfo(player, "GateInfo: target must be a Building"); + throwbackInfo(player, "Found" + targetBuilding.getObjectType().toString()); + return; + } + + blueprint = Blueprint._meshLookup.get(targetBuilding.getMeshUUID()); + + if (blueprint == null || + (blueprint.getBuildingGroup() != BuildingGroup.RUNEGATE)){ + throwbackInfo(player, "showgate: target must be a Runegate"); + return; + } + + runegateType = RunegateType.getGateTypeFromUUID(targetBuilding.getObjectUUID()); + runeGate = Runegate.getRunegates()[runegateType.ordinal()]; + + outString = "RungateType: " + runegateType.name(); + outString += newline; + + outString += "Portal State:"; + outString += newline; + + for (Portal portal : runeGate.getPortals()) { + + outString += "Portal: " + portal.getPortalType().name(); + outString += " Active: " + portal.isActive(); + outString += " Dest: " + portal.getDestinationGateType().name(); + outString += newline; + outString += " Origin: " + portal.getPortalLocation().x + 'x'; + outString += " " + portal.getPortalLocation().y + 'y'; + outString += newline; + + Vector3fImmutable offset = portal.getPortalLocation().subtract(targetBuilding.getLoc()); + outString += " Offset: " + offset.x + "x " + offset.z + 'y'; + outString += newline; + outString += newline; + + } + outString += newline; + throwbackInfo(player, outString); + } + + @Override + protected String _getHelpString() { + return "Displays a runegate's gate status"; + } + + @Override + protected String _getUsageString() { + + + return "/gateinfo \n"; + } + + + +} diff --git a/src/engine/devcmd/cmds/GetBankCmd.java b/src/engine/devcmd/cmds/GetBankCmd.java new file mode 100644 index 00000000..55e1e871 --- /dev/null +++ b/src/engine/devcmd/cmds/GetBankCmd.java @@ -0,0 +1,52 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.SessionManager; +import engine.net.client.ClientConnection; +import engine.net.client.msg.VendorDialogMsg; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; + +/** + * + * @author Eighty + * + */ +public class GetBankCmd extends AbstractDevCmd { + + public GetBankCmd() { + super("getbank"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + if (pcSender == null) return; + + ClientConnection cc = SessionManager.getClientConnection(pcSender); + if (cc == null) return; + + VendorDialogMsg.getBank(pcSender, null, cc); + this.setTarget(pcSender); //for logging + } + + @Override + protected String _getUsageString() { + return "' /getbank'"; + } + + @Override + protected String _getHelpString() { + return "Opens bank window"; + } + +} diff --git a/src/engine/devcmd/cmds/GetCacheCountCmd.java b/src/engine/devcmd/cmds/GetCacheCountCmd.java new file mode 100644 index 00000000..29840336 --- /dev/null +++ b/src/engine/devcmd/cmds/GetCacheCountCmd.java @@ -0,0 +1,40 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.DbManager; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; + +public class GetCacheCountCmd extends AbstractDevCmd { + + public GetCacheCountCmd() { + super("getcachecount"); + this.addCmdString("getcachecount"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + DbManager.printCacheCount(pcSender); + } + + @Override + protected String _getUsageString() { + return "' /getcachecount'"; + } + + @Override + protected String _getHelpString() { + return "Get a count of the objects in the cache"; + } + +} diff --git a/src/engine/devcmd/cmds/GetDisciplineLocCmd.java b/src/engine/devcmd/cmds/GetDisciplineLocCmd.java new file mode 100644 index 00000000..c8d428c1 --- /dev/null +++ b/src/engine/devcmd/cmds/GetDisciplineLocCmd.java @@ -0,0 +1,64 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ZoneManager; +import engine.objects.*; + +import java.util.ArrayList; + +/** + * @author + * + */ +public class GetDisciplineLocCmd extends AbstractDevCmd { + + public GetDisciplineLocCmd() { + super("getdiscloc"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + System.out.println("MOB UUID , MOB NAME , MACRO ZONE NAME , MOB LOCATION, DROPPED ITEM, DROP CHANCE"); + + for (Zone zone: ZoneManager.getAllZones()){ + for (Mob mob: zone.zoneMobSet){ + + if (mob.getLevel() >= 80) + continue; + + ArrayList specialLootList = SpecialLoot.LootMap.get(mob.getLootSet()); + + + if (specialLootList != null) + for (SpecialLoot specialLoot: specialLootList){ + + + ItemBase itemBase = ItemBase.getItemBase(specialLoot.getItemID()); + System.out.println(mob.getObjectUUID() + " : " + mob.getName() + " : " + (mob.getParentZone().isMacroZone() ? mob.getParentZone().getName() : mob.getParentZone().getParent().getName()) + " , " + mob.getLoc().toString2D() + " , " + itemBase.getName() + " , " + specialLoot.getDropChance() + '%'); + } + } + } + } + + @Override + protected String _getHelpString() { + return "Enchants an item with a prefix and suffix"; + } + + @Override + protected String _getUsageString() { + return "' /enchant clear/Enchant1 Enchant2 Enchant3 ...'"; + } + +} diff --git a/src/engine/devcmd/cmds/GetHeightCmd.java b/src/engine/devcmd/cmds/GetHeightCmd.java new file mode 100644 index 00000000..6f94f457 --- /dev/null +++ b/src/engine/devcmd/cmds/GetHeightCmd.java @@ -0,0 +1,233 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.InterestManagement.HeightMap; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ZoneManager; +import engine.math.Vector2f; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; +import engine.objects.Zone; + +public class GetHeightCmd extends AbstractDevCmd { + + public GetHeightCmd() { + super("getHeight"); + this.addCmdString("height"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + boolean end = true; + + float height = HeightMap.getWorldHeight(pc); + + this.throwbackInfo(pc, "Altitude : " + height); + + this.throwbackInfo(pc, "Character Height: " + pc.getCharacterHeight()); + this.throwbackInfo(pc, "Character Height to start swimming: " + pc.centerHeight); + + Zone zone = ZoneManager.findSmallestZone(pc.getLoc()); + this.throwbackInfo(pc, "Water Level : " + zone.getSeaLevel()); + this.throwbackInfo(pc, "Character Water Level Above : " + (pc.getCharacterHeight() + height - zone.getSeaLevel()) ); + + if (end) + return; + + Vector2f gridSquare; + Vector2f gridOffset; + Vector2f parentGrid; + Vector2f parentLoc = new Vector2f(-1, -1); + + Zone currentZone = ZoneManager.findSmallestZone(pc.getLoc()); + + if (currentZone == null) + return; + + Zone parentZone = currentZone.getParent(); + + HeightMap heightMap = currentZone.getHeightMap(); + + + //find the next parents heightmap if the currentzone heightmap is null. + while (heightMap == null){ + + if (currentZone == ZoneManager.getSeaFloor()){ + this.throwbackInfo(pc, "Could not find a heightmap to get height."); + break; + } + + this.throwbackError(pc, "Heightmap does not exist for " + currentZone.getName()); + this.throwbackInfo(pc, "Using parent zone instead: "); + currentZone = currentZone.getParent(); + heightMap = currentZone.getHeightMap(); + } + + + if ( (heightMap == null) || (currentZone == ZoneManager.getSeaFloor()) ) { + this.throwbackInfo(pc, currentZone.getName() + " has no heightmap " ); + this.throwbackInfo(pc, "Current altitude: " + currentZone.absY ); + return; + } + + Vector2f zoneLoc = ZoneManager.worldToZoneSpace(pc.getLoc(), currentZone); + + Vector3fImmutable seaFloorLocalLoc = ZoneManager.worldToLocal(pc.getLoc(), ZoneManager.getSeaFloor()); + this.throwbackInfo(pc, "SeaFloor Local : " + seaFloorLocalLoc.x + " , " + seaFloorLocalLoc.y); + + + + + this.throwbackInfo(pc, "Local Zone Location : " + zoneLoc.x + " , " + zoneLoc.y); + Vector3fImmutable localLocFromCenter = ZoneManager.worldToLocal(pc.getLoc(), currentZone); + Vector3fImmutable parentLocFromCenter = ZoneManager.worldToLocal(pc.getLoc(), currentZone.getParent()); + this.throwbackInfo(pc, "Local Zone Location from center : " + localLocFromCenter); + this.throwbackInfo(pc, "parent Zone Location from center : " + parentLocFromCenter); + + Vector2f parentZoneLoc = ZoneManager.worldToZoneSpace(pc.getLoc(), currentZone.getParent()); + this.throwbackInfo(pc, "Parent Zone Location from Bottom Left : " + parentZoneLoc); + + if ((parentZone != null ) && (parentZone.getHeightMap() != null)) { + parentLoc = ZoneManager.worldToZoneSpace(pc.getLoc(), parentZone); + parentGrid = parentZone.getHeightMap().getGridSquare( parentLoc); + } else parentGrid = new Vector2f(-1,-1); + + gridSquare = heightMap.getGridSquare(zoneLoc); + gridOffset = HeightMap.getGridOffset(gridSquare); + + float interaltitude = currentZone.getHeightMap().getInterpolatedTerrainHeight(zoneLoc); + + this.throwbackInfo(pc, currentZone.getName()); + this.throwbackInfo(pc, "Current Grid Square: " + gridSquare.x + " , " + gridSquare.y ); + this.throwbackInfo(pc, "Grid Offset: " + gridOffset.x + " , " + gridOffset.y); + this.throwbackInfo(pc, "Parent Grid: " + parentGrid.x + " , " + parentGrid.y); + + if (parentGrid.x != -1) { + float parentAltitude = parentZone.getHeightMap().getInterpolatedTerrainHeight(parentLoc); + this.throwbackInfo(pc, "Parent ALTITUDE: " + (parentAltitude)); + this.throwbackInfo(pc, "Parent Interpolation: " + (parentAltitude + parentZone.getWorldAltitude())); + } + this.throwbackInfo(pc, "interpolated height: " + interaltitude); + + this.throwbackInfo(pc, "interpolated height with World: " + (interaltitude + currentZone.getWorldAltitude())); + + float realWorldAltitude = interaltitude + currentZone.getWorldAltitude(); + + //OUTSET + if (parentZone != null){ + float parentXRadius = currentZone.getBounds().getHalfExtents().x; + float parentZRadius = currentZone.getBounds().getHalfExtents().y; + + float offsetX = Math.abs((localLocFromCenter.x / parentXRadius)); + float offsetZ = Math.abs((localLocFromCenter.z / parentZRadius)); + + float bucketScaleX = 100/parentXRadius; + float bucketScaleZ = 200/parentZRadius; + + float outsideGridSizeX = 1 - bucketScaleX; //32/256 + float outsideGridSizeZ = 1 - bucketScaleZ; + float weight; + + double scale; + + + if (offsetX > outsideGridSizeX && offsetX > offsetZ){ + weight = (offsetX - outsideGridSizeX) / bucketScaleX; + scale = Math.atan2((.5 - weight) * 3.1415927, 1); + + float scaleChild = (float) ((scale + 1) * .5); + float scaleParent = 1 - scaleChild; + + + float parentAltitude = parentZone.getHeightMap().getInterpolatedTerrainHeight(parentLoc); + float parentCenterAltitude = parentZone.getHeightMap().getInterpolatedTerrainHeight(ZoneManager.worldToZoneSpace(currentZone.getLoc(), parentZone)); + + parentCenterAltitude += currentZone.getYCoord(); + parentCenterAltitude += interaltitude; + + float firstScale = parentAltitude * scaleParent; + float secondScale = parentCenterAltitude * scaleChild; + float outsetALt = firstScale + secondScale; + + outsetALt += currentZone.getParent().getAbsY(); + realWorldAltitude = outsetALt; + + }else if (offsetZ > outsideGridSizeZ){ + + weight = (offsetZ - outsideGridSizeZ) / bucketScaleZ; + scale = Math.atan2((.5 - weight) * 3.1415927, 1); + + float scaleChild = (float) ((scale + 1) * .5); + float scaleParent = 1 - scaleChild; + float parentAltitude = parentZone.getHeightMap().getInterpolatedTerrainHeight(parentLoc); + float parentCenterAltitude = parentZone.getHeightMap().getInterpolatedTerrainHeight(ZoneManager.worldToZoneSpace(currentZone.getLoc(), parentZone)); + + parentCenterAltitude += currentZone.getYCoord(); + parentCenterAltitude += interaltitude; + float firstScale = parentAltitude * scaleParent; + float secondScale = parentCenterAltitude * scaleChild; + float outsetALt = firstScale + secondScale; + + outsetALt += currentZone.getParent().getAbsY(); + realWorldAltitude = outsetALt; + + + + + } + } + + float strMod = pc.statStrBase - 40; + + strMod *= .00999999998f; + + strMod += 1f; + + float radius = 0; + switch (pc.getRaceID()){ + case 2017: + radius = 3.1415927f; + case 2000: + + + } + strMod *= 1.5707964f; + + strMod += 3.1415927f; + + strMod -= .5f; + + + + realWorldAltitude += strMod; + + this.throwbackInfo(pc, "interpolated height with World: " + realWorldAltitude); + + + + + } + + @Override + protected String _getHelpString() { + return "Temporarily Changes SubRace"; + } + + @Override + protected String _getUsageString() { + return "' /subrace mobBaseID"; + } + +} diff --git a/src/engine/devcmd/cmds/GetMemoryCmd.java b/src/engine/devcmd/cmds/GetMemoryCmd.java new file mode 100644 index 00000000..d6b1a0a8 --- /dev/null +++ b/src/engine/devcmd/cmds/GetMemoryCmd.java @@ -0,0 +1,57 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; + +public class GetMemoryCmd extends AbstractDevCmd { + + public GetMemoryCmd() { + super("getmemory"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] words, + AbstractGameObject target) { + if (pcSender == null) return; + + String hSize = getMemoryOutput(Runtime.getRuntime().totalMemory()); + String mhSize = getMemoryOutput(Runtime.getRuntime().maxMemory()); + String fhSize = getMemoryOutput(Runtime.getRuntime().freeMemory()); + + String out = "Heap Size: " + hSize + ", Max Heap Size: " + mhSize + ", Free Heap Size: " + fhSize; + throwbackInfo(pcSender, out); + } + + public static String getMemoryOutput(long memory) { + String out = ""; + if (memory > 1073741824) + return (memory / 1073741824) + "GB"; + else if (memory > 1048576) + return (memory / 1048576) + "MB"; + else if (memory > 1024) + return (memory / 1048576) + "KB"; + else + return memory + "B"; + } + + @Override + protected String _getUsageString() { + return "' /getmemory'"; + } + + @Override + protected String _getHelpString() { + return "lists memory usage"; + } + +} diff --git a/src/engine/devcmd/cmds/GetMobBaseLoot.java b/src/engine/devcmd/cmds/GetMobBaseLoot.java new file mode 100644 index 00000000..3ecce72f --- /dev/null +++ b/src/engine/devcmd/cmds/GetMobBaseLoot.java @@ -0,0 +1,59 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.Enum.GameObjectType; +import engine.devcmd.AbstractDevCmd; +import engine.objects.AbstractGameObject; +import engine.objects.Mob; +import engine.objects.MobLootBase; +import engine.objects.PlayerCharacter; + +/** + * @author Eighty + * + */ +public class GetMobBaseLoot extends AbstractDevCmd { + + public GetMobBaseLoot() { + super("mobbaseloot"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + if (target.getObjectType() != GameObjectType.Mob){ + this.throwbackError(pc, "Must be targeting a Valid Mob For this Command."); + return; + } + + + Mob mob = (Mob)target; + for (MobLootBase mlb : MobLootBase.MobLootSet.get(mob.getMobBase().getLoadID())){ + + this.throwbackInfo(pc, "LootTable11 = " + mlb.getLootTableID() + "\rn "); + this.throwbackInfo(pc, "Chance = " + mlb.getChance() + "\rn "); + + } + + + } + + @Override + protected String _getHelpString() { + return "Copies a Mob of type 'mobID' with optional new name"; + } + + @Override + protected String _getUsageString() { + return "' /mob mobID [name]'"; + } + +} diff --git a/src/engine/devcmd/cmds/GetOffsetCmd.java b/src/engine/devcmd/cmds/GetOffsetCmd.java new file mode 100644 index 00000000..515b5d3f --- /dev/null +++ b/src/engine/devcmd/cmds/GetOffsetCmd.java @@ -0,0 +1,63 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ZoneManager; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; +import engine.objects.Zone; + +public class GetOffsetCmd extends AbstractDevCmd { + + public GetOffsetCmd() { + super("getoffset"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] words, + AbstractGameObject target) { + if (pcSender == null) return; + + Zone zone = null; + try { + int loadID = Integer.parseInt(words[0]); + zone = ZoneManager.getZoneByZoneID(loadID); + if (zone == null) { + throwbackError(pcSender, "Error: can't find the zone of ID " + loadID + '.'); + return; + } + } catch (Exception e) { + zone = ZoneManager.findSmallestZone(pcSender.getLoc()); + } + + if (zone == null) { + throwbackError(pcSender, "Error: can't find the zone you're in."); + return; + } + + float difX = pcSender.getLoc().x - zone.absX; + float difY = pcSender.getLoc().y - zone.absY; + float difZ = pcSender.getLoc().z - zone.absZ; + + throwbackInfo(pcSender, zone.getName() + ": (x: " + difX + ", y: " + difY + ", z: " + difZ + ')'); + } + + @Override + protected String _getUsageString() { + return "'./getoffset [zoneID]'"; + } + + @Override + protected String _getHelpString() { + return "lists offset from center of zone"; + } + +} diff --git a/src/engine/devcmd/cmds/GetRuneDropRateCmd.java b/src/engine/devcmd/cmds/GetRuneDropRateCmd.java new file mode 100644 index 00000000..7dea4c5c --- /dev/null +++ b/src/engine/devcmd/cmds/GetRuneDropRateCmd.java @@ -0,0 +1,42 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; + + +public class GetRuneDropRateCmd extends AbstractDevCmd { + + public GetRuneDropRateCmd() { + super("getrunedroprate"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] words, + AbstractGameObject target) { + if (pcSender == null) return; + + String out = "Depracated"; + throwbackInfo(pcSender, out); + } + + @Override + protected String _getUsageString() { + return "' /getrunedroprate'"; + } + + @Override + protected String _getHelpString() { + return "lists drop rates for runes and contracts in non-hotzone."; + } + +} diff --git a/src/engine/devcmd/cmds/GetVaultCmd.java b/src/engine/devcmd/cmds/GetVaultCmd.java new file mode 100644 index 00000000..df50846e --- /dev/null +++ b/src/engine/devcmd/cmds/GetVaultCmd.java @@ -0,0 +1,47 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.SessionManager; +import engine.net.client.ClientConnection; +import engine.net.client.msg.VendorDialogMsg; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; + +public class GetVaultCmd extends AbstractDevCmd { + + public GetVaultCmd() { + super("getvault"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + if (pcSender == null) return; + + ClientConnection cc = SessionManager.getClientConnection(pcSender); + if (cc == null) return; + + VendorDialogMsg.getVault(pcSender, null, cc); + this.setTarget(pcSender); //for logging + } + + @Override + protected String _getUsageString() { + return "' /getvault'"; + } + + @Override + protected String _getHelpString() { + return "Opens account vault"; + } + +} diff --git a/src/engine/devcmd/cmds/GetZoneCmd.java b/src/engine/devcmd/cmds/GetZoneCmd.java new file mode 100644 index 00000000..bab3ae9d --- /dev/null +++ b/src/engine/devcmd/cmds/GetZoneCmd.java @@ -0,0 +1,66 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ZoneManager; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; +import engine.objects.Zone; + +import java.util.ArrayList; + +public class GetZoneCmd extends AbstractDevCmd { + + public GetZoneCmd() { + super("getzone"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] words, + AbstractGameObject target) { + if (pcSender == null) return; + + if (words.length != 1) { + this.sendUsage(pcSender); + return; + } + + ArrayList allIn = new ArrayList<>(); + switch (words[0].toLowerCase()) { + case "all": + throwbackInfo(pcSender, "All zones currently in"); + allIn = ZoneManager.getAllZonesIn(pcSender.getLoc()); + break; + case "smallest": + throwbackInfo(pcSender, "Smallest zone currently in"); + Zone zone = ZoneManager.findSmallestZone(pcSender.getLoc()); + allIn.add(zone); + break; + default: + this.sendUsage(pcSender); + return; + } + + for (Zone zone : allIn) + throwbackInfo(pcSender, zone.getName() + "; UUID: " + zone.getObjectUUID() + ", loadNum: " + zone.getLoadNum()); + } + + @Override + protected String _getUsageString() { + return "' /getzone smallest/all'"; + } + + @Override + protected String _getHelpString() { + return "lists what zones a player is in"; + } + +} diff --git a/src/engine/devcmd/cmds/GetZoneMobsCmd.java b/src/engine/devcmd/cmds/GetZoneMobsCmd.java new file mode 100644 index 00000000..f069ef20 --- /dev/null +++ b/src/engine/devcmd/cmds/GetZoneMobsCmd.java @@ -0,0 +1,82 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ZoneManager; +import engine.objects.AbstractGameObject; +import engine.objects.Mob; +import engine.objects.PlayerCharacter; +import engine.objects.Zone; + +public class GetZoneMobsCmd extends AbstractDevCmd { + + public GetZoneMobsCmd() { + super("getzonemobs"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] words, + AbstractGameObject target) { + if (pcSender == null) return; + + int loadID = 0; + if (words.length == 1) { + try { + loadID = Integer.parseInt(words[0]); + } catch (Exception e) {} + } + + //find the zone + Zone zone = null; + if (loadID != 0) { + zone = ZoneManager.getZoneByZoneID(loadID); + if (zone == null) + zone = ZoneManager.getZoneByUUID(loadID); + } else + zone = ZoneManager.findSmallestZone(pcSender.getLoc()); + + if (zone == null) { + if (loadID != 0) + throwbackError(pcSender, "Error: can't find the zone of ID " + loadID + '.'); + else + throwbackError(pcSender, "Error: can't find the zone you are in."); + return; + } + + //get all mobs for the zone + + throwbackInfo(pcSender, zone.getName() + " (" + zone.getLoadNum() + ") " + zone.getObjectUUID()); + + for (Mob m : zone.zoneMobSet) { + + if (m != null) { + String out = m.getName() + '(' + m.getDBID() + "): "; + if (m.isAlive()) + out += m.getLoc().x + "x" + m.getLoc().z + "; isAlive: " + m.isAlive(); + else + out += " isAlive: " + m.isAlive(); + throwbackInfo(pcSender, out); + } else + throwbackInfo(pcSender, "Unknown (" + m.getDBID() + "): not loaded"); + } + } + + @Override + protected String _getUsageString() { + return "' /getzonemobs [zoneID]'"; + } + + @Override + protected String _getHelpString() { + return "lists all mobs for a zone"; + } + +} diff --git a/src/engine/devcmd/cmds/GotoBoundsCmd.java b/src/engine/devcmd/cmds/GotoBoundsCmd.java new file mode 100644 index 00000000..ae10766b --- /dev/null +++ b/src/engine/devcmd/cmds/GotoBoundsCmd.java @@ -0,0 +1,108 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.Enum.GameObjectType; +import engine.devcmd.AbstractDevCmd; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractGameObject; +import engine.objects.Building; +import engine.objects.PlayerCharacter; + +public class GotoBoundsCmd extends AbstractDevCmd { + + public GotoBoundsCmd() { + super("gotobounds"); + } + + @Override + protected void _doCmd(PlayerCharacter player, String[] words, + AbstractGameObject target) { + + String corner = words[0]; + Vector3fImmutable targetLoc = Vector3fImmutable.ZERO; + + if (target == null || !target.getObjectType().equals(GameObjectType.Building)){ + this.throwbackError(player, "No Building Selected"); + return; + } + + Building building = (Building)target; + + if (building.getBounds() == null){ + this.throwbackInfo(player, "No valid Bounds for building UUID " + building.getObjectUUID()); + return; + } + float x = building.getBounds().getHalfExtents().x; + float z = building.getBounds().getHalfExtents().y; + + if (building.getBlueprint() != null){ + x = building.getBlueprint().getExtents().x; + z = building.getBlueprint().getExtents().y; + } + + float topLeftX = building.getLoc().x - x; + float topLeftY = building.getLoc().z -z; + + float topRightX = building.getLoc().x + x; + float topRightY = building.getLoc().z - z; + + float bottomLeftX = building.getLoc().x - x; + float bottomLeftY = building.getLoc().z + z; + + float bottomRightX = building.getLoc().x +x; + float bottomRightY = building.getLoc().z + z; + + + switch (corner){ + case "topleft": + targetLoc = new Vector3fImmutable(topLeftX, 0, topLeftY); + break; + case "topright": + targetLoc = new Vector3fImmutable(topRightX, 0, topRightY); + break; + case "bottomleft": + targetLoc = new Vector3fImmutable(bottomLeftX, 0, bottomLeftY); + break; + case "bottomright": + targetLoc = new Vector3fImmutable(bottomRightX, 0, bottomRightY); + break; + default: + this.throwbackInfo(player, "wrong corner name. use topleft , topright , bottomleft , bottomright"); + return; + + } + + targetLoc = Vector3fImmutable.transform(building.getLoc(),targetLoc , building.getBounds().getRotationDegrees()); + + // Teleport player + + if (targetLoc == Vector3fImmutable.ZERO) { + this.throwbackError(player, "Failed to locate UUID"); + return; + } + + player.teleport(targetLoc); + + } + + @Override + protected String _getHelpString() { + return "Teleports player to a UUID"; + } + + @Override + protected String _getUsageString() { + return "' /gotoobj '"; + + } + +} diff --git a/src/engine/devcmd/cmds/GotoCmd.java b/src/engine/devcmd/cmds/GotoCmd.java new file mode 100644 index 00000000..3825ae4a --- /dev/null +++ b/src/engine/devcmd/cmds/GotoCmd.java @@ -0,0 +1,182 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.Enum.GameObjectType; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.DbManager; +import engine.gameManager.SessionManager; +import engine.math.Vector3fImmutable; +import engine.objects.*; +import engine.server.MBServerStatics; + +import java.util.concurrent.ThreadLocalRandom; + +public class GotoCmd extends AbstractDevCmd { + + public GotoCmd() { + super("goto"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + Vector3fImmutable loc = null; + + // Arg Count Check + + + if (target != null && words[0].isEmpty()){ + AbstractWorldObject targetAgo = (AbstractWorldObject)target; + pc.teleport(targetAgo.getLoc()); + return; + } + + if (words[0].isEmpty()){ + this.sendUsage(pc); + return; + } + + if (words[0].equalsIgnoreCase("playground")){ + if (target instanceof AbstractCharacter){ + loc = new Vector3fImmutable(63276,0,-54718); + } + + if (loc != null) + pc.teleport(loc); + + return; + } + + if (words[0].equalsIgnoreCase("coc")){ + if (target instanceof AbstractCharacter){ + loc = new Vector3fImmutable(98561.656f,0,-13353.778f); + } + + if (loc != null) + pc.teleport(loc); + + return; + } + + String cityName = ""; + for (String partial: words){ + cityName += partial + ' '; + } + + cityName = cityName.substring(0, cityName.length() - 1); + + for (AbstractGameObject cityAgo: DbManager.getList(GameObjectType.City)){ + City city = (City)cityAgo; + if (city == null) + continue; + if (!city.getCityName().equalsIgnoreCase(cityName)) + continue; + Zone zone = city.getParent(); + if (zone != null){ + if (zone.isNPCCity() || zone.isPlayerCity()) + loc = Vector3fImmutable.getRandomPointOnCircle(zone.getLoc(), MBServerStatics.TREE_TELEPORT_RADIUS); + else + loc = zone.getLoc(); + + int random = ThreadLocalRandom.current().nextInt(5); + if (random == 1) + break; + } + } + + if (loc == null){ + for (AbstractGameObject zoneAgo: DbManager.getList(GameObjectType.Zone)){ + Zone zone = (Zone)zoneAgo; + if (zone == null) + continue; + if (!zone.getName().equalsIgnoreCase(cityName)) + continue; + if (zone != null){ + if (zone.isNPCCity() || zone.isPlayerCity()) + loc = Vector3fImmutable.getRandomPointOnCircle(zone.getLoc(), MBServerStatics.TREE_TELEPORT_RADIUS); + else + loc = zone.getLoc(); + + int random = ThreadLocalRandom.current().nextInt(5); + if (random == 1) + break; + } + } + } + if (loc == null && words.length == 1){ + + try { + PlayerCharacter pcDest = SessionManager + .getPlayerCharacterByLowerCaseName(words[0]); + if (pcDest == null){ + this.throwbackError(pc, "Player or Zone not found by name: " + + words[0]); + this.throwbackInfo(pc, "If you have spaces in the zone name, replace them with '_'"); + return; + } + + if (pcDest.getCombinedName().equals(pc.getCombinedName())) { + this + .throwbackError(pc, + "Cannot goto yourself. Well, you can, but you wont go anywhere."); + return; + } + + loc = pcDest.getLoc(); + } catch (Exception e) { + this.throwbackError(pc, + "An unknown exception occurred while attempting to goto a character named '" + + words[0] + '\''); + return; + } + + } + if (loc == null) { // lat lon mode + if (words.length != 2) { + throwbackError(pc, this.getUsageString()); + return; + } + float lat = 0.0f, lon = 0.0f; + String latLong = '\'' + words[0] + ", " + words[1] + '\''; + + try { + lat = Float.parseFloat(words[0]); + lon = Float.parseFloat(words[1]); + loc = new Vector3fImmutable(lat, 0f, -lon); + } catch (NumberFormatException e) { + this.throwbackError(pc, "Supplied LatLong: " + latLong + + " failed to parse to Floats"); + return; + + } catch (Exception e) { + this.throwbackError(pc, + "An unknown exception occurred while attempting to goto LatLong of " + + latLong); + return; + } + } + if (loc != null) { + pc.teleport(loc); + } + } + + @Override + protected String _getHelpString() { + return "Alters your characters position TO 'lat' and 'long', or TO the position of 'characterName'. This does not transport you BY 'lat' and 'long', but rather TO 'lat' and 'long' "; + } + + @Override + protected String _getUsageString() { + return "'[ /goto lat lon] || [ /goto characterName] || [/goto zoneName \replace spaces with `_`]`"; + } + +} diff --git a/src/engine/devcmd/cmds/GotoObj.java b/src/engine/devcmd/cmds/GotoObj.java new file mode 100644 index 00000000..2b535564 --- /dev/null +++ b/src/engine/devcmd/cmds/GotoObj.java @@ -0,0 +1,103 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.Enum; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.DbManager; +import engine.math.Vector3fImmutable; +import engine.objects.*; + +public class GotoObj extends AbstractDevCmd { + + public GotoObj() { + super("gotoobj"); + } + + @Override + protected void _doCmd(PlayerCharacter player, String[] words, + AbstractGameObject target) { + + int uuid; + Vector3fImmutable targetLoc = Vector3fImmutable.ZERO; + Enum.DbObjectType objectType; + + try { + uuid = Integer.parseInt(words[0]); + } catch (NumberFormatException e) { + this.throwbackError(player, "Failed to parse UUID" + e.toString()); + return; + } + + objectType = DbManager.BuildingQueries.GET_UID_ENUM(uuid); + + switch (objectType) { + + case NPC: + NPC npc = (NPC) DbManager.getFromCache(Enum.GameObjectType.NPC, uuid); + + if (npc != null) + targetLoc = npc.getLoc(); + break; + case MOB: + Mob mob = (Mob) DbManager.getFromCache(Enum.GameObjectType.Mob, uuid); + + if (mob != null) + targetLoc = mob.getLoc(); + break; + case CHARACTER: + PlayerCharacter playerCharacter = (PlayerCharacter) DbManager.getFromCache(Enum.GameObjectType.PlayerCharacter, uuid); + + if (playerCharacter != null) + targetLoc = playerCharacter.getLoc(); + break; + case BUILDING: + Building building = (Building) DbManager.getFromCache(Enum.GameObjectType.Building, uuid); + + if (building != null) + targetLoc = building.getLoc(); + break; + case ZONE: + Zone zone = (Zone) DbManager.getFromCache(Enum.GameObjectType.Zone, uuid); + + if (zone != null) + targetLoc = zone.getLoc(); + break; + case CITY: + City city = (City) DbManager.getFromCache(Enum.GameObjectType.City, uuid); + + if (city != null) + targetLoc = city.getLoc(); + break; + } + // Teleport player + + if (targetLoc == Vector3fImmutable.ZERO) { + this.throwbackError(player, "Failed to locate UUID"); + return; + } + + player.teleport(targetLoc); + + } + + @Override + protected String _getHelpString() { + return "Teleports player to a UUID"; + } + + @Override + protected String _getUsageString() { + return "' /gotoobj '"; + + } + +} diff --git a/src/engine/devcmd/cmds/GuildListCmd.java b/src/engine/devcmd/cmds/GuildListCmd.java new file mode 100644 index 00000000..71437576 --- /dev/null +++ b/src/engine/devcmd/cmds/GuildListCmd.java @@ -0,0 +1,105 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.DbManager; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; + + +/** + * @author + * Summary: Lists UID, Name and GL UID of either + * Player or NPC sovereign guilds + */ + +public class GuildListCmd extends AbstractDevCmd { + + // Instance variables + + private int _guildType; // 0 = Player : 1 = NPC sovereign guilds + private String outputStr = null; + + public GuildListCmd() { + super("guildlist"); + } + + + // AbstractDevCmd Overridden methods + + @Override + protected void _doCmd(PlayerCharacter pc, String[] args, + AbstractGameObject target) { + + if(validateUserInput(args) == false) { + this.sendUsage(pc); + return; + } + + parseUserInput(args); + + // Execute stored procedure + + outputStr = DbManager.GuildQueries.GET_GUILD_LIST(_guildType); + + // Send results to user + + throwbackInfo(pc, outputStr); + + } + + @Override + protected String _getHelpString() { + return "Lists guild info for sovereign guilds"; + } + + @Override + protected String _getUsageString() { + return "/guildlist npc|player"; + } + + // Class methods + + private static boolean validateUserInput(String[] userInput) { + + int stringIndex; + String commandSet = "npcplayer"; + + // incorrect number of arguments test + + if (userInput.length != 1) + return false; + + // Test of game object type argument + + stringIndex = commandSet.indexOf(userInput[0].toLowerCase()); + + return stringIndex != -1; + } + + private void parseUserInput(String[] userInput) { + + // Build mask from user input + + switch (userInput[0].toLowerCase()) { + case "npc": + _guildType = 1; + break; + case "player": + _guildType = 0; + break; + default: + break; + } + + } + +} diff --git a/src/engine/devcmd/cmds/HeartbeatCmd.java b/src/engine/devcmd/cmds/HeartbeatCmd.java new file mode 100644 index 00000000..c43c81a0 --- /dev/null +++ b/src/engine/devcmd/cmds/HeartbeatCmd.java @@ -0,0 +1,43 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.SimulationManager; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; + +public class HeartbeatCmd extends AbstractDevCmd { + + public HeartbeatCmd() { + super("heartbeat"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + this.throwbackInfo(pc, "Current Heartbeat : " + SimulationManager.currentHeartBeatDelta + " ms."); + this.throwbackInfo(pc, "Max Heartbeat : " + SimulationManager.HeartbeatDelta + " ms."); + + } + + @Override + protected String _getHelpString() { + return "Temporarily Changes SubRace"; + } + + @Override + protected String _getUsageString() { + return "' /subrace mobBaseID"; + } + +} diff --git a/src/engine/devcmd/cmds/HelpCmd.java b/src/engine/devcmd/cmds/HelpCmd.java new file mode 100644 index 00000000..4c3d49f0 --- /dev/null +++ b/src/engine/devcmd/cmds/HelpCmd.java @@ -0,0 +1,59 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.DevCmdManager; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; + +public class HelpCmd extends AbstractDevCmd { + + public HelpCmd() { + super("help"); + this.addCmdString("list"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + if (pcSender == null) + return; + if (pcSender.getAccount() == null) + return; + this.throwbackInfo( + pcSender, + "Type ' /command ?' for info about a command. A space is necessary before the slash."); + String commands = DevCmdManager.getCmdsForAccessLevel(); + this.throwbackInfo(pcSender, "Commands your account is eligible to use: "); + + int first = 0; + int last = 500; + int charLimit = 500; + while (commands.length() > charLimit) { + this.throwbackInfo(pcSender, commands.substring(first, last)); + first = charLimit; + charLimit += 500; + last = charLimit; + } + this.throwbackInfo(pcSender, commands.substring(first)); + } + + @Override + protected String _getUsageString() { + return "' /help' || ' /list'"; + } + + @Override + protected String _getHelpString() { + return "Displays help info and lists all commands accessible for the player's access level."; + } + +} diff --git a/src/engine/devcmd/cmds/HotzoneCmd.java b/src/engine/devcmd/cmds/HotzoneCmd.java new file mode 100644 index 00000000..ff206fe0 --- /dev/null +++ b/src/engine/devcmd/cmds/HotzoneCmd.java @@ -0,0 +1,114 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ZoneManager; +import engine.math.FastMath; +import engine.net.client.msg.HotzoneChangeMsg; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; +import engine.objects.Zone; +import engine.server.world.WorldServer; + +/** + * ./hotzone <- display the current hotzone & time remaining + * ./hotzone random <- change hotzone to random new zone + * ./hotzone name of a macrozone <- change hotzone to the zone name provided + * + */ +public class HotzoneCmd extends AbstractDevCmd { + + public HotzoneCmd() { + super("hotzone"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + StringBuilder data = new StringBuilder(); + for (String s : words) { + data.append(s); + data.append(' '); + } + String input = data.toString().trim(); + + if (input.length() == 0) { + throwbackInfo(pc, "Current hotzone: " + hotzoneInfo()); + return; + } + + Zone zone; + + if (input.equalsIgnoreCase("random")) { + throwbackInfo(pc, "Previous hotzone: " + hotzoneInfo()); + ZoneManager.generateAndSetRandomHotzone(); + zone = ZoneManager.getHotZone(); + } else { + zone = ZoneManager.findMacroZoneByName(input); + + if (zone == null) { + throwbackError(pc, "Cannot find a macrozone with that name."); + return; + } + + if (zone == ZoneManager.getHotZone()) { + throwbackInfo(pc, "That macrozone is already the Hotzone."); + return; + } + + if (ZoneManager.validHotZone(zone) == false) { + throwbackError(pc, "That macrozone cannot be set as the Hotzone."); + return; + } + + throwbackInfo(pc, "Previous hotzone: " + hotzoneInfo()); + ZoneManager.setHotZone(zone); + } + + throwbackInfo(pc, "New hotzone: " + hotzoneInfo()); + HotzoneChangeMsg hcm = new HotzoneChangeMsg(zone.getObjectType().ordinal(), zone.getObjectUUID()); + WorldServer.setLastHZChange(System.currentTimeMillis()); + } + + @Override + protected String _getHelpString() { + return "Use no arguments to see the current hotzone. Specify a macrozone name to change the hotzone, or \"random\" to change it randomly."; + } + + @Override + protected String _getUsageString() { + return "'./hotzone [random | ]"; + } + + private static String hotzoneInfo() { + final int hotzoneTimeLeft = FastMath.secondsUntilNextHour(); + final Zone hotzone = ZoneManager.getHotZone(); + String hotzoneInfo; + + if (hotzone == null) { + hotzoneInfo = "none"; + } else { + int hr = hotzoneTimeLeft/3600; + int rem = hotzoneTimeLeft%3600; + int mn = rem/60; + int sec = rem%60; + hotzoneInfo = hotzone.getName() + + " (" + (hr<10 ? "0" : "") + hr + ':' + + (mn<10 ? "0" : "") + mn + ':' + + (sec<10 ? "0" : "") + sec + + " remaining)"; + } + return hotzoneInfo; + } + +} diff --git a/src/engine/devcmd/cmds/InfoCmd.java b/src/engine/devcmd/cmds/InfoCmd.java new file mode 100644 index 00000000..354d4410 --- /dev/null +++ b/src/engine/devcmd/cmds/InfoCmd.java @@ -0,0 +1,514 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.Enum.BuildingGroup; +import engine.Enum.GameObjectType; +import engine.Enum.TargetColor; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.BuildingManager; +import engine.gameManager.SessionManager; +import engine.math.Vector3fImmutable; +import engine.objects.*; +import engine.util.StringUtils; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; + + +/** + * @author + * + */ +public class InfoCmd extends AbstractDevCmd { + + public InfoCmd() { + super("info"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + // Arg Count Check + if (words.length != 1) { + this.sendUsage(pc); + return; + } + if (pc == null) { + return; + } + + String newline = "\r\n "; + + try { + int targetID = Integer.parseInt(words[0]); + Building b = BuildingManager.getBuilding(targetID); + if (b == null) + throwbackError(pc, "Building with ID " + targetID + + " not found"); + else + target = b; + } catch (Exception e) { + } + + if (target == null) { + throwbackError(pc, "Target is unknown or of an invalid type." + + newline + "Type ID: 0x" + + pc.getLastTargetType().toString() + + " Table ID: " + pc.getLastTargetID()); + return; + } + + + GameObjectType objType = target.getObjectType(); + int objectUUID = target.getObjectUUID(); + String output; + + output = "Target Information:" + newline; + output += StringUtils.addWS("UUID: " + objectUUID, 20); + output += newline; + output += "Type: " + target.getClass().getSimpleName(); + output += " [0x" + objType.toString() + ']'; + + if (target instanceof AbstractWorldObject) { + AbstractWorldObject targetAWO = (AbstractWorldObject) target; + Vector3fImmutable targetLoc = targetAWO.getLoc(); + output += newline; + output += StringUtils.addWS("Lat: " + targetLoc.x, 20); + output += "Lon: " + -targetLoc.z; + output += newline; + output += StringUtils.addWS("Alt: " + targetLoc.y, 20); + output += newline; + output += "Rot: " + targetAWO.getRot().y; + output += newline; + double radian = 0; + + + if (targetAWO.getBounds() != null && targetAWO.getBounds().getQuaternion() != null) + radian = targetAWO.getBounds().getQuaternion().angleY; + int degrees = (int) Math.toDegrees(radian); + + + output += "Degrees: " + degrees; + output += newline; + } + + switch (objType) { + case Building: + Building targetBuilding = (Building) target; + output += StringUtils.addWS("Lac: " + + targetBuilding.getw(), 20); + output += "Blueprint : "; + output += targetBuilding.getBlueprintUUID(); + output += newline; + + output += " MeshUUID : "; + output += targetBuilding.getMeshUUID(); + output += newline; + + if (targetBuilding.getBlueprintUUID() != 0) + output += ' ' + targetBuilding.getBlueprint().getName(); + + output += newline; + output += targetBuilding.getBlueprint() != null ? targetBuilding.getBlueprint().getBuildingGroup().name(): " no building group"; + + output += newline; + output += "EffectFlags: " + targetBuilding.getEffectFlags(); + output += newline; + output += StringUtils.addWS("rank: " + targetBuilding.getRank(), + 20); + output += "HP: " + targetBuilding.getHealth() + '/' + + targetBuilding.getMaxHitPoints(); + output += newline; + output += "Scale: (" + targetBuilding.getMeshScale().getX(); + output += ", " + targetBuilding.getMeshScale().getY(); + output += ", " + targetBuilding.getMeshScale().getZ() + ')'; + output += newline; + output += "Owner UID: " + targetBuilding.getOwnerUUID(); + output += (targetBuilding.isOwnerIsNPC() ? " (NPC)" : " (PC)"); + output += newline; + output += "ProtectionState: " + targetBuilding.getProtectionState().name(); + output += newline; + + if (targetBuilding.getUpgradeDateTime() != null) { + output += targetBuilding.getUpgradeDateTime().toString(); + output += newline; + } + + Guild guild = targetBuilding.getGuild(); + Guild nation = null; + String guildId = "-1"; + String nationId = "-1"; + String gTag = ""; + String nTag = ""; + + if (guild != null) { + int id = guild.getObjectUUID(); + + if (id == 0) { + guildId = id + " [" + guild.hashCode() + ']'; + } else + guildId = Integer.toString(id); + + gTag = guild.getGuildTag().summarySentence(); + nation = guild.getNation(); + + if (nation != null) { + id = nation.getObjectUUID(); + if (id == 0) { + nationId = id + " [" + nation.hashCode() + ']'; + } else { + nationId = Integer.toString(id); + } + nTag = nation.getGuildTag().summarySentence(); + } + } + output += StringUtils.addWS("Guild UID: " + guildId, 20); + + if (gTag.length() > 0) + output += "Guild crest: " + gTag; + + output += newline; + output += StringUtils.addWS("Nation UID: " + nationId, 20); + + if (nTag.length() > 0) { + output += "Nation crest: " + nTag; + } + + output+= newline; + + + if (targetBuilding.getBlueprint() != null){ + + if(targetBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.MINE){ + Mine mine = Mine.getMineFromTower(targetBuilding.getObjectUUID()); + + if (mine != null){ + output+= newline; + output+= "Mine active: " + mine.getIsActive(); + output+= newline; + output+= "Mine Type: "+mine.getMineType().name; + output+= newline; + output+= "Expansion : " + mine.isExpansion(); + output+= newline; + output+= "Production type: " +mine.getProduction().name(); + + output+= newline; + output+= "Open Date: "+ ( mine.openDate).toString(); + + output+= newline; + output+= "Open Date: "+ (mine.openDate).toString(); + } + } + output += newline; + + if (targetBuilding.maintDateTime != null){ + output += targetBuilding.maintDateTime.toString(); + output+= newline; + } + } + + output += "Reserve : " + targetBuilding.reserve; + output+= newline; + output += "Strongbox : " + targetBuilding.getStrongboxValue(); + output+= newline; + + // List hirelings + + if (targetBuilding.getHirelings().isEmpty() == false) { + + output += newline; + output += "Hirelings List: name / slot / floor"; + + BuildingModelBase buildingModelBase = BuildingModelBase.getModelBase(targetBuilding.getMeshUUID()); + + for (AbstractCharacter npc : targetBuilding.getHirelings().keySet()) { + + if (npc.getObjectType() != GameObjectType.NPC) + continue; + output += newline + npc.getName() + " slot " + targetBuilding.getHirelings().get(npc); + output += newline + "location " + npc.getLoc(); + } + } + + ArrayList tempList = BuildingRegions._staticRegions.get(targetBuilding.getMeshUUID()); + output+= newline; + output+= "Building Regions: Size - " + tempList.size(); + output+= newline; + output+= "Building Regions from Bounds: Size - " + targetBuilding.getBounds().getRegions().size(); + output+= newline; + + for (Regions regions: targetBuilding.getBounds().getRegions()){ + //TODO ADD REGION INFO + } + + break; + case PlayerCharacter: + output += newline; + PlayerCharacter targetPC = (PlayerCharacter) target; + output += StringUtils.addWS("Name: " + targetPC.getName(), 20); + output += newline; + output += "InSession : " + SessionManager.getPlayerCharacterByID(target.getObjectUUID()) != null ? " true " : " false"; + output += newline; + output += "RaceType: " + targetPC.getRace().getRaceType().name(); + output += newline; + output += "Race: " + targetPC.getRace().getName(); + output += newline; + output += "Safe:" + targetPC.inSafeZone(); + output+= newline; + output+= "Experience : " + targetPC.getExp(); + output += newline; + output += "OverFlowExperience : " + targetPC.getOverFlowEXP(); + output += newline; + output += StringUtils.addWS("Level: " + + targetPC.getLevel() + " (" + + TargetColor.getCon(targetPC, pc).toString() + ')', 20); + + Account acpc = SessionManager.getAccount(pc); + Account ac = SessionManager.getAccount(targetPC); + + if (acpc != null && ac != null) { + output += "Account ID: " + ac.getObjectUUID(); + output += newline; + output += "Access Level: " + ac.status.name(); + } else + output += "Account ID: UNKNOWN"; + + output += newline; + output += "Inventory Weight:" + (targetPC.getCharItemManager().getInventoryWeight() + targetPC.getCharItemManager().getEquipWeight()); + output += newline; + output += "Max Inventory Weight:" + ((int) targetPC.statStrBase * 3); + output += newline; + output += "ALTITUDE :"+ targetPC.getAltitude(); + output += newline; + output += "BuildingID :"+ targetPC.getInBuildingID(); + output += newline; + output += "inBuilding :"+ targetPC.getInBuilding(); + output += newline; + output += "inFloor :"+ targetPC.getInFloorID(); + output += newline; + + BaseClass baseClass = targetPC.getBaseClass(); + + if (baseClass != null) + output += StringUtils.addWS("Class: " + baseClass.getName(), 20); + else + output += StringUtils.addWS("", 20); + + PromotionClass promotionClass = targetPC.getPromotionClass(); + if (promotionClass != null) { + output += "Pro. Class: " + promotionClass.getName(); + } else { + output += "Pro. Class: "; + } + + output += newline; + output += "====Guild Info===="; + output += newline; + + if (targetPC.getGuild() != null){ + output += "Name: " + targetPC.getGuild().getName(); + output += newline; + output += "State: " + targetPC.getGuild().getGuildState(); + output += newline; + output += "Realms Owned:" +targetPC.getGuild().getRealmsOwned(); + output += newline; + output += "====Nation===="; + output += newline; + output += "Nation Name: " + targetPC.getGuild().getNation().getName(); + output += newline; + output += "Nation State: " + targetPC.getGuild().getNation().getGuildState(); + output += newline; + output += "Realms Owned:" +targetPC.getGuild().getNation().getRealmsOwned(); + output += newline; + output += "Guild Rank:" +(GuildStatusController.getRank(targetPC.getGuildStatus()) + targetPC.getGuild().getRealmsOwnedFlag()); + } + + output += newline; + output += "Movement State: " + targetPC.getMovementState().name(); + output += newline; + output += "Movement Speed: " + targetPC.getSpeed(); + + output += "Altitude : " + targetPC.getLoc().y; + + output += "Swimming : " + targetPC.isSwimming(); + output += newline; + output += "isMoving : " + targetPC.isMoving(); + + break; + + case NPC: + NPC targetNPC = (NPC) target; + output += "databaseID: " + targetNPC.getDBID() + newline; + output += "Name: " + targetNPC.getName(); + output += newline; + output += StringUtils.addWS("Level: " + targetNPC.getLevel(), 20); + MobBase mobBase = targetNPC.getMobBase(); + + if (mobBase != null) + output += "RaceID: " + mobBase.getObjectUUID(); + else + output += "RaceID: " + targetNPC.getLoadID(); + + output += newline; + output += "Flags: " + targetNPC.getMobBase().getFlags().toString(); + output += newline; + output += "Spawn: (" + targetNPC.getBindLoc().getX(); + output += ", " + targetNPC.getBindLoc().getY(); + output += ", " + targetNPC.getBindLoc().getZ() + ')'; + output += newline; + output += "ContractID: " + targetNPC.getContractID(); + output += newline; + output += "InventorySet: " + targetNPC.getContract().inventorySet; + output += newline; + output += targetNPC.getContract().getAllowedBuildings().toString(); + output += newline; + output += "Extra Rune: " + targetNPC.getContract().getExtraRune(); + + output += newline; + output += "isTrainer: " + targetNPC.getContract().isTrainer(); + output += newline; + output += "Buy Cost: " + targetNPC.getBuyPercent(); + output += "\tSell Cost: " + targetNPC.getSellPercent(); + output += newline; + output += "fromInit: " + targetNPC.isStatic(); + output += newline; + if (mobBase != null) { + output += newline; + output += "Slottable: " + targetNPC.getContract().getAllowedBuildings().toString(); + output += newline; + output += "Fidelity ID: " + targetNPC.getFidalityID(); + output += newline; + output += "EquipSet: " + targetNPC.getEquipmentSetID(); + output += newline; + output += "Parent Zone LoadNum : " + targetNPC.getParentZone().getLoadNum(); + + } + + if (targetNPC.getRegion() != null){ + output += newline; + output += "BuildingID : " + targetNPC.getRegion().parentBuildingID; + output += "building level : " + targetNPC.getRegion().level; + output += "building room : " + targetNPC.getRegion().room; + }else{ + output += newline; + output += "No building found."; + } + + + + break; + + case Mob: + Mob targetMob = (Mob) target; + output += "databaseID: " + targetMob.getDBID() + newline; + output += "Name: " + targetMob.getName(); + output += newline; + output += StringUtils.addWS("Level: " + targetMob.getLevel(), 20); + mobBase = targetMob.getMobBase(); + if (mobBase != null) + output += "RaceID: " + mobBase.getObjectUUID(); + else + output += "RaceID: " + targetMob.getLoadID(); + output += newline; + output += "NoAggro: " + mobBase.getNoAggro().toString(); + output += newline; + output += "Spawn: (" + targetMob.getBindLoc().getX(); + output += ", " + targetMob.getBindLoc().getY(); + output += ", " + targetMob.getBindLoc().getZ() + ')'; + output += newline; + if (targetMob.isPet()) { + output += "isPet: true"; + output+= newline; + if (targetMob.isSummonedPet()) + output += "isSummonedPet: true"; + else output += "isSummonedPet: false"; + PlayerCharacter owner = targetMob.getOwner(); + if (owner != null) + output += " owner: " + owner.getObjectUUID(); + output += newline; + output += "assist: " + targetMob.assist() + " resting: " + targetMob.isSit(); + output += newline; + } + if (targetMob.getMobBase() != null) { + output += "Mobbase: " + targetMob.getMobBase().getObjectUUID(); + output += newline; + output += "Flags: " + targetMob.getMobBase().getFlags().toString(); + output += newline; + + } + if (targetMob.isMob()) { + output += "SpawnRadius: " + targetMob.getSpawnRadius(); + output += newline; + output += "Spawn Timer: " + targetMob.getSpawnTimeAsString(); + output += newline; + } + output += StringUtils.addWS("isAlive: " + + targetMob.isAlive(), 20); + output += newline; + output += "Mob State: " +targetMob.getState().name(); + + output += newline; + output += "Speed : " + targetMob.getSpeed(); + output += newline; + output += "Fidelity ID: " + targetMob.getFidalityID(); + output += newline; + output += "EquipSet: " + targetMob.getEquipmentSetID(); + output += newline; + output += "Parent Zone LoadNum : " + targetMob.getParentZone().getLoadNum(); + output += newline; + output += "isMoving : " + targetMob.isMoving(); + break; + case Item: //intentional passthrough + case MobLoot: + Item item = (Item) target; + ItemBase itemBase = item.getItemBase(); + output += StringUtils.addWS("ItemBase: " + itemBase.getUUID(), 20); + output += "Weight: " + itemBase.getWeight(); + output += newline; + DecimalFormat df = new DecimalFormat("###,###,###,###,##0"); + output += StringUtils.addWS("Qty: " + + df.format(item.getNumOfItems()), 20); + output += "Charges: " + item.getChargesRemaining() + + '/' + item.getChargesMax(); + output += newline; + output += "Name: " + itemBase.getName(); + output += newline; + output += item.getContainerInfo(); + + throwbackInfo(pc, output); + + output = "Effects:" + newline; + ConcurrentHashMap effects = item.getEffects(); + for (String name : effects.keySet()) { + Effect eff = effects.get(name); + output+= eff.getEffectsBase().getIDString(); + output+= newline; + // output += eff.getEffectToken() + (eff.bakedInStat() ? " (baked in)" : "") + newline; + } + + break; + } + + throwbackInfo(pc, output); + } + + @Override + protected String _getHelpString() { + return "Gets information on an Object."; + } + + @Override + protected String _getUsageString() { + return "' /info targetID'"; + } + +} diff --git a/src/engine/devcmd/cmds/JumpCmd.java b/src/engine/devcmd/cmds/JumpCmd.java new file mode 100644 index 00000000..c85ff0a6 --- /dev/null +++ b/src/engine/devcmd/cmds/JumpCmd.java @@ -0,0 +1,85 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; + +public class JumpCmd extends AbstractDevCmd { + + public JumpCmd() { + super("jump"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + // Arg Count Check + if (words.length != 2) { + this.sendUsage(pc); + return; + } + + //test + + if (words[0].equalsIgnoreCase("face")){ + + try { + float range = Float.parseFloat(words[1]); + Vector3fImmutable newLoc = pc.getFaceDir().scaleAdd(range, pc.getLoc()); + pc.teleport(newLoc); + + + } catch (NumberFormatException e) { + this.throwbackError(pc, "" + + " failed to parse to Floats"); + return; + + } + return; + } + float lat = 0.0f, lon = 0.0f; + String latLong = '\'' + words[0] + ", " + words[1] + '\''; + + try { + lat = Float.parseFloat(words[0]); + lon = Float.parseFloat(words[1]); + + } catch (NumberFormatException e) { + this.throwbackError(pc, "Supplied LatLong: " + latLong + + " failed to parse to Floats"); + return; + + } catch (Exception e) { + this.throwbackError(pc, + "An unknown exception occurred while attempting to jump to LatLong of " + + latLong); + return; + } + + Vector3fImmutable loc = pc.getLoc(); + loc = loc.add(lat, 0f, -lon); + pc.teleport(loc); + } + + @Override + protected String _getHelpString() { + return "Alters your characters position by 'lat' and 'long'. This does not transport you TO 'lat' and 'long', but rather BY 'lat' and 'long' "; + + } + + @Override + protected String _getUsageString() { + return "' /jump lat long'"; + } + +} diff --git a/src/engine/devcmd/cmds/MBDropCmd.java b/src/engine/devcmd/cmds/MBDropCmd.java new file mode 100644 index 00000000..89957656 --- /dev/null +++ b/src/engine/devcmd/cmds/MBDropCmd.java @@ -0,0 +1,134 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.objects.AbstractGameObject; +import engine.objects.ItemBase; +import engine.objects.LootTable; +import engine.objects.PlayerCharacter; + +/** + * + * @author Eighty + * + */ +public class MBDropCmd extends AbstractDevCmd { + + public MBDropCmd() { + super("mbdrop"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + String newline = "\r\n "; + if (args.length != 1){ + this.sendUsage(pcSender); + this.sendHelp(pcSender); + return; + } + + String output = ""; + switch (args[0].toLowerCase()){ + case "clear": + + LootTable.contractCount = 0; + LootTable.dropCount = 0; + LootTable.glassCount = 0; + LootTable.runeCount = 0; + LootTable.rollCount = 0; + LootTable.resourceCount = 0; + + LootTable.contractDroppedMap.clear(); + LootTable.glassDroppedMap.clear(); + LootTable.itemsDroppedMap.clear(); + LootTable.resourceDroppedMap.clear(); + LootTable.runeDroppedMap.clear(); + break; + case "all": + output = LootTable.dropCount + " items - ITEM NAME : DROP COUNT" + newline; + for (ItemBase ib: LootTable.itemsDroppedMap.keySet()){ + + int dropCount = LootTable.itemsDroppedMap.get(ib); + output += ib.getName() + " : " + dropCount + newline; + + } + break; + case "resource": + output = LootTable.resourceCount + " Resources - ITEM NAME : DROP COUNT" + newline; + for (ItemBase ib: LootTable.resourceDroppedMap.keySet()){ + + int dropCount = LootTable.resourceDroppedMap.get(ib); + output += ib.getName() + " : " + dropCount + newline; + + } + + break; + case "rune": + + output = LootTable.runeCount + " Runes - ITEM NAME : DROP COUNT" + newline; + for (ItemBase ib: LootTable.runeDroppedMap.keySet()){ + + int dropCount = LootTable.runeDroppedMap.get(ib); + output += ib.getName() + " : " + dropCount + newline; + + } + break; + case "contract": + + output = LootTable.contractCount + " Contracts - ITEM NAME : DROP COUNT" + newline; + for (ItemBase ib: LootTable.contractDroppedMap.keySet()){ + + int dropCount = LootTable.contractDroppedMap.get(ib); + output += ib.getName() + " : " + dropCount + newline; + + + } + break; + + case "glass": + + output = LootTable.glassCount + " Glass - ITEM NAME : DROP COUNT" + newline; + for (ItemBase ib: LootTable.glassDroppedMap.keySet()){ + + int dropCount = LootTable.glassDroppedMap.get(ib); + output += ib.getName() + " : " + dropCount + newline; + } + break; + + case "chance": + float chance = (float)LootTable.dropCount/(float)LootTable.rollCount * 100; + output = LootTable.dropCount + " out of " + LootTable.rollCount + " items Dropped. chance = " + chance + '%'; + + break; + + default: + this.sendUsage(pcSender); + this.sendHelp(pcSender); + return; + } + + this.throwbackInfo(pcSender, output); + + + } + + @Override + protected String _getUsageString() { + return "' /mbdrop all/resource/rune/contract/glass/chance/clear"; + } + + @Override + protected String _getHelpString() { + return "Lists drops for server since a reboot. All lists all items and drops. chance is the overall chance items drop from mobs on server. (not including Equipment)"; + } + +} diff --git a/src/engine/devcmd/cmds/MakeBaneCmd.java b/src/engine/devcmd/cmds/MakeBaneCmd.java new file mode 100644 index 00000000..3b845d79 --- /dev/null +++ b/src/engine/devcmd/cmds/MakeBaneCmd.java @@ -0,0 +1,215 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.Enum.ProtectionState; +import engine.InterestManagement.WorldGrid; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.BuildingManager; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.gameManager.ZoneManager; +import engine.math.Vector3f; +import engine.math.Vector3fImmutable; +import engine.objects.*; +import org.pmw.tinylog.Logger; + +/** + * @author Eighty + * + */ +public class MakeBaneCmd extends AbstractDevCmd { + + public MakeBaneCmd() { + super("makebane"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + if (words.length < 1 || words.length > 2) { + this.sendUsage(pc); + return; + } + + int attackerID = 0; + int rank = 8; + + if (words.length == 2) { + try { + attackerID = Integer.parseInt(words[0]); + rank = Integer.parseInt(words[1]); + } catch (NumberFormatException e) { + throwbackError(pc, "AttackerGuildID must be a number, " + words[0] + " is invalid"); + return; + } + } else if (words.length == 1) { + if (target == null) { + throwbackError(pc, "No target specified"); + return; + } + + if (!(target instanceof PlayerCharacter)) { + throwbackError(pc, "Target is not a player"); + return; + } + attackerID = target.getObjectUUID(); + + try { + rank = Integer.parseInt(words[0]); + } catch (NumberFormatException e) { + throwbackError(pc, "Rank must be specified, 1 through 8"); + return; + } + } + + if (rank < 1 || rank > 8) { + throwbackError(pc, "Rank must be 1 through 8"); + return; + } + + PlayerCharacter player = PlayerCharacter.getPlayerCharacter(attackerID); + + + + + if (player.getGuild().isErrant()) { + throwbackError(pc, "Errant's can not place banes"); + return; + } + + AbstractCharacter attackerAGL = Guild.GetGL(player.getGuild()); + + if (attackerAGL == null) { + throwbackError(pc, "No guild leader found for attacking guild."); + return; + } + + if (!(attackerAGL instanceof PlayerCharacter)) { + throwbackError(pc, "Attacking guild leader is an NPC."); + return; + } + + if (player.getGuild().isNPCGuild()) { + throwbackError(pc, "The guild used is an npc guild. They can not bane."); + return; + } + + // if (player.getGuild().getOwnedCity() != null) { + // throwbackError(pc, "The attacking guild already has a city."); + // return; + // } + + Zone zone = ZoneManager.findSmallestZone(pc.getLoc()); + + if (zone == null) { + throwbackError(pc, "Unable to find the zone you're in."); + return; + } + + if (!zone.isPlayerCity()) { + throwbackError(pc, "This is not a player city."); + return; + } + + City city = City.getCity(zone.getPlayerCityUUID()); + if (city == null) { + throwbackError(pc, "Unable to find the city associated with this zone."); + return; + } + + if (city.getTOL() == null) { + throwbackError(pc, "Unable to find the tree of life for this city."); + return; + } + + if (city.getBane() != null) { + throwbackError(pc, "This city is already baned."); + return; + } + + if (Bane.getBaneByAttackerGuild(player.getGuild()) != null) { + throwbackError(pc, "This guild is already baning someone."); + return; + } + + Blueprint blueprint = Blueprint.getBlueprint(24300); + + if (blueprint == null) { + throwbackError(pc, "Unable to find building set for banestone."); + return; + } + + Vector3f rot = new Vector3f(0, 0, 0); + + // *** Refactor : Overlap test goes here + + //Let's drop a banestone! + Vector3fImmutable localLocation = ZoneManager.worldToLocal(pc.getLoc(), zone); + + if (localLocation == null){ + ChatManager.chatSystemError(pc, "Failed to convert world location to zone location. Contact a CCR."); + Logger.info("Failed to Convert World coordinates to local zone coordinates"); + return; + } + + Building stone = DbManager.BuildingQueries.CREATE_BUILDING( + zone.getObjectUUID(), pc.getObjectUUID(), blueprint.getName(), blueprint.getBlueprintUUID(), + localLocation, 1.0f, blueprint.getMaxHealth(rank), ProtectionState.PROTECTED, 0, rank, + null, blueprint.getBlueprintUUID(), 1, 0.0f); + + if (stone == null) { + ChatManager.chatSystemError(pc, "Failed to create banestone."); + return; + } + stone.addEffectBit((1 << 19)); + stone.setRank((byte) rank); + stone.setMaxHitPoints( blueprint.getMaxHealth(stone.getRank())); + stone.setCurrentHitPoints(stone.getMaxHitPoints()); + BuildingManager.setUpgradeDateTime(stone, null, 0); + + //Make the bane + + Bane bane = Bane.makeBane(player, city, stone); + + if (bane == null) { + + //delete bane stone, failed to make bane object + DbManager.BuildingQueries.DELETE_FROM_DATABASE(stone); + + throwbackError(pc, "Failed to create bane."); + return; + } + + WorldGrid.addObject(stone, pc); + + //Add baned effect to TOL + city.getTOL().addEffectBit((1 << 16)); + city.getTOL().updateEffects(); + + Vector3fImmutable movePlayerOutsideStone = player.getLoc(); + movePlayerOutsideStone = movePlayerOutsideStone.setX(movePlayerOutsideStone.x + 10); + movePlayerOutsideStone = movePlayerOutsideStone.setZ(movePlayerOutsideStone.z + 10); + player.teleport(movePlayerOutsideStone); + + throwbackInfo(pc, "The city has been succesfully baned."); + } + + @Override + protected String _getHelpString() { + return "Creates an bane."; + } + + @Override + protected String _getUsageString() { + return "'./makebane playerUUID baneRank'"; + } + +} diff --git a/src/engine/devcmd/cmds/MakeItemCmd.java b/src/engine/devcmd/cmds/MakeItemCmd.java new file mode 100644 index 00000000..3bdd2384 --- /dev/null +++ b/src/engine/devcmd/cmds/MakeItemCmd.java @@ -0,0 +1,256 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.Enum.ItemContainerType; +import engine.Enum.ItemType; +import engine.Enum.OwnerType; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.DbManager; +import engine.objects.*; +import engine.powers.EffectsBase; + +import java.util.ArrayList; + +/** + * @author Eighty + * + */ +public class MakeItemCmd extends AbstractDevCmd { + + public MakeItemCmd() { + super("makeitem"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + if (words[0].equals("resources")){ + for (int ibID : Warehouse.getMaxResources().keySet()){ + if (ibID == 7) + continue; + + ItemBase ib = ItemBase.getItemBase(ibID); + + short weight = ib.getWeight(); + if (!pc.getCharItemManager().hasRoomInventory(weight)) { + throwbackError(pc, "Not enough room in inventory for any more of this item"); + + pc.getCharItemManager().updateInventory(); + return; + } + + boolean worked = false; + Item item = new Item(ib, pc.getObjectUUID(), + OwnerType.PlayerCharacter, (byte)0, (byte)0, (short)ib.getDurability(), (short)ib.getDurability(), + true, false, ItemContainerType.INVENTORY, (byte) 0, + new ArrayList<>(),""); + + item.setNumOfItems(Warehouse.getMaxResources().get(ibID)); + + try { + item = DbManager.ItemQueries.ADD_ITEM(item); + worked = true; + } catch (Exception e) { + throwbackError(pc, "DB error 1: Unable to create item. " + e.getMessage()); + return; + } + + if (item == null || !worked) { + throwbackError(pc, "DB error 2: Unable to create item."); + return; + } + + + + //add item to inventory + pc.getCharItemManager().addItemToInventory(item); + } + return; + } + if (words.length < 3 || words.length > 5) { + this.sendUsage(pc); + return; + } + + int quantity = 1; + if (words.length > 3) { + try { + quantity = Integer.parseInt(words[3]); + } catch (NumberFormatException e) { + throwbackError(pc, "Quantity must be a number, " + words[3] + " is invalid"); + return; + } + if (quantity < 1 || quantity > 100) + quantity = 1; + } + + int numItems = 1; + if (words.length > 4) { + try { + numItems = Integer.parseInt(words[4]); + } catch (NumberFormatException e) { + throwbackError(pc, "numResources must be a number, " + words[4] + " is invalid"); + return; + } + numItems = (numItems < 1) ? 1 : numItems; + numItems = (numItems > 5000) ? 5000 : numItems; + } + + int itembaseID; + try { + itembaseID = Integer.parseInt(words[0]); + } catch (NumberFormatException e) { + itembaseID = ItemBase.getIDByName(words[0].toLowerCase()); + if (itembaseID == 0) { + throwbackError(pc, "Supplied type " + words[0] + + " failed to parse to an Integer"); + return; + } + } catch (Exception e) { + throwbackError(pc, + "An unknown exception occurred when trying to use createitem command for type " + + words[0]); + return; // NaN + } + + if (itembaseID == 7) { + this.throwbackInfo(pc, "use /addgold to add gold."); + return; + } + + String prefix = ""; + String suffix = ""; + + if (!(words[1].equals("0"))) { + prefix = words[1]; + if (!(prefix.substring(0, 4).equals("PRE-"))) + prefix = EffectsBase.getItemEffectsByName(prefix.toLowerCase()); + if (!(prefix.substring(0, 4).equals("PRE-"))) { + throwbackError(pc, "Invalid Prefix. Prefix must consist of PRE-001 to PRE-334 or 0 for no Prefix."); + return; + } + + boolean validInt = false; + try { + int num = Integer.parseInt(prefix.substring(4, 7)); + if (num > 0 && num < 335) + validInt = true; + } catch (Exception e) { + throwbackError(pc, "error parsing number " + prefix); + } + if (!validInt) { + throwbackError(pc, "Invalid Prefix. Prefix must consist of PRE-001 to PRE-334 or 0 for no Prefix."); + return; + } + } + + if (!(words[2].equals("0"))) { + suffix = words[2]; + + if (!(suffix.substring(0, 4).equals("SUF-"))) + suffix = EffectsBase.getItemEffectsByName(suffix.toLowerCase()); + if (!(suffix.substring(0, 4).equals("SUF-"))) { + throwbackError(pc, "Invalid Suffix. Suffix must consist of SUF-001 to SUF-328 or 0 for no Suffix."); + return; + } + + boolean validInt = false; + try { + int num = Integer.parseInt(suffix.substring(4, 7)); + if (num > 0 && num < 329) + validInt = true; + } catch (Exception e) { + throwbackError(pc, "error parsing number " + suffix); + } + if (!validInt) { + throwbackError(pc, "Invalid Suffix. Suffix must consist of SUF-001 to SUF-328 or 0 for no Suffix."); + return; + } + } + ItemBase ib = ItemBase.getItemBase(itembaseID); + if (ib == null) { + throwbackError(pc, "Unable to find itembase of ID " + itembaseID); + return; + } + + if ((numItems > 1) + && (ib.getType().equals(ItemType.RESOURCE) == false) + && (ib.getType().equals(ItemType.OFFERING)) == false) + numItems = 1; + + CharacterItemManager cim = pc.getCharItemManager(); + if (cim == null) { + throwbackError(pc, "Unable to find the character item manager for player " + pc.getFirstName() + '.'); + return; + } + + byte charges = (byte) ib.getNumCharges(); + short dur = (short) ib.getDurability(); + + String result = ""; + for (int i = 0; i < quantity; i++) { + short weight = ib.getWeight(); + if (!cim.hasRoomInventory(weight)) { + throwbackError(pc, "Not enough room in inventory for any more of this item. " + i + " produced."); + if (i > 0) + cim.updateInventory(); + return; + } + + boolean worked = false; + Item item = new Item(ib, pc.getObjectUUID(), + OwnerType.PlayerCharacter, charges, charges, dur, dur, + true, false, ItemContainerType.INVENTORY, (byte) 0, + new ArrayList<>(),""); + if (numItems > 1) + item.setNumOfItems(numItems); + + try { + item = DbManager.ItemQueries.ADD_ITEM(item); + worked = true; + } catch (Exception e) { + throwbackError(pc, "DB error 1: Unable to create item. " + e.getMessage()); + return; + } + + if (item == null || !worked) { + throwbackError(pc, "DB error 2: Unable to create item."); + return; + } + + //create prefix + if (!prefix.isEmpty()) + item.addPermanentEnchantmentForDev(prefix, 0); + + //create suffix + if (!suffix.isEmpty()) + item.addPermanentEnchantmentForDev(suffix, 0); + + //add item to inventory + cim.addItemToInventory(item); + result += " " + item.getObjectUUID(); + } + this.setResult(result); + cim.updateInventory(); + } + + @Override + protected String _getHelpString() { + return "Creates an item of type 'itembaseID' with a prefix and suffix"; + } + + @Override + protected String _getUsageString() { + return "'./makeitem itembaseID PrefixID SuffixID [quantity] [numResources]'"; + } + +} diff --git a/src/engine/devcmd/cmds/NetDebugCmd.java b/src/engine/devcmd/cmds/NetDebugCmd.java new file mode 100644 index 00000000..065ff18d --- /dev/null +++ b/src/engine/devcmd/cmds/NetDebugCmd.java @@ -0,0 +1,86 @@ +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; + +/** + * @author + * Summary: Devcmd to toggle logging of application protocol messages + * + */ + +public class NetDebugCmd extends AbstractDevCmd { + + // Instance variables + + + public NetDebugCmd() { + super("netdebug"); + } + + + // AbstractDevCmd Overridden methods + + @Override + protected void _doCmd(PlayerCharacter pc, String[] args, + AbstractGameObject target) { + + Boolean debugState = false; + + if(validateUserInput(args) == false) { + this.sendUsage(pc); + return; + } + + // Arguments have been validated use argument to set debug state + + switch (args[0]) { + case "on": + debugState = true; + break; + case "off": + debugState = false; + break; + default: + break; + } + + MBServerStatics.DEBUG_PROTOCOL = debugState; + + // Send results to user + throwbackInfo(pc, "Network debug state: " + debugState.toString()); + } + + @Override + protected String _getHelpString() { + return "Toggles sending network messages to log"; + } + + @Override + protected String _getUsageString() { + return "/netdebug on|off"; + } + + // Class methods + + private static boolean validateUserInput(String[] userInput) { + + int stringIndex; + String commandSet = "onoff"; + + // incorrect number of arguments test + + if (userInput.length != 1) + return false; + + // Validate arguments + + stringIndex = commandSet.indexOf(userInput[0].toLowerCase()); + + return stringIndex != -1; + } + + +} diff --git a/src/engine/devcmd/cmds/PrintBankCmd.java b/src/engine/devcmd/cmds/PrintBankCmd.java new file mode 100644 index 00000000..981d4014 --- /dev/null +++ b/src/engine/devcmd/cmds/PrintBankCmd.java @@ -0,0 +1,73 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.objects.*; + +import java.util.ArrayList; + +public class PrintBankCmd extends AbstractDevCmd { + + public PrintBankCmd() { + super("printbank"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + AbstractWorldObject tar; + String name; + String type = "PlayerCharacter"; + + if (target != null) { + if (target instanceof AbstractCharacter) { + tar = (AbstractCharacter) target; + name = ((AbstractCharacter) tar).getFirstName(); + } else { + tar = pc; + name = ((AbstractCharacter) tar).getFirstName(); + } + } else { + tar = pc; + name = ((AbstractCharacter) tar).getFirstName(); + } + + if (!(tar instanceof PlayerCharacter)) { + throwbackError(pc, "Must target player"); + return; + } + + + CharacterItemManager cim = ((AbstractCharacter)tar).getCharItemManager(); + ArrayList list = cim.getBank(); + throwbackInfo(pc, "Bank for " + type + ' ' + name + " (" + tar.getObjectUUID() + ')'); + for (Item item : list) { + throwbackInfo(pc, " " + item.getItemBase().getName() + ", count: " + item.getNumOfItems()); + } + Item gold = cim.getGoldBank(); + if (gold != null) + throwbackInfo(pc, " Gold, count: " + gold.getNumOfItems()); + else + throwbackInfo(pc, " NULL Gold"); + } + + @Override + protected String _getHelpString() { + return "Returns the player's current bank"; + } + + @Override + protected String _getUsageString() { + return "' /printbank'"; + } + +} diff --git a/src/engine/devcmd/cmds/PrintBonusesCmd.java b/src/engine/devcmd/cmds/PrintBonusesCmd.java new file mode 100644 index 00000000..b425ec45 --- /dev/null +++ b/src/engine/devcmd/cmds/PrintBonusesCmd.java @@ -0,0 +1,91 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.Enum.GameObjectType; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ChatManager; +import engine.objects.*; +import engine.powers.effectmodifiers.AbstractEffectModifier; + +public class PrintBonusesCmd extends AbstractDevCmd { + + public PrintBonusesCmd() { + super("printbonuses"); + // super("printbonuses", MBServerStatics.ACCESS_LEVEL_ADMIN); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + AbstractWorldObject tar; + String name; + String type = "PlayerCharacter"; + if (target != null) + if (target instanceof Item) { + type = "Item"; + tar = (Item) target; + name = ((Item) tar).getItemBase().getName(); + } else if (target instanceof AbstractCharacter) { + tar = (AbstractCharacter) target; + name = ((AbstractCharacter) tar).getFirstName(); + } else { + tar = pc; + name = ((AbstractCharacter) tar).getFirstName(); + } + else { + tar = pc; + name = ((AbstractCharacter) tar).getFirstName(); + } + + //Get name and type + if (tar instanceof Mob) { + Mob mob = (Mob) tar; + MobBase mb = mob.getMobBase(); + if (mb != null) + name = mb.getFirstName(); + type = "Mob"; + } else if (tar instanceof NPC) { + NPC npc = (NPC) tar; + Contract contract = npc.getContract(); + if (contract != null) + if (contract.isTrainer()) + name = tar.getName() + ", " + contract.getName(); + else + name = tar.getName() + " the " + contract.getName(); + type = "NPC"; + } + + if (tar.getObjectType() == GameObjectType.Item) { + Item targetItem = (Item) tar; + + if (targetItem.getBonuses() != null) + for (AbstractEffectModifier targetName : targetItem.getBonuses().keySet()) { + ChatManager.chatSystemInfo(pc, " " + targetName.modType.name() + "-" + targetName.sourceType.name() + ": " + targetItem.getBonuses().get(name)); + } + } else if (((AbstractCharacter)tar).getBonuses() != null) { + ((AbstractCharacter)tar).getBonuses().printBonusesToClient(pc); + } + else + throwbackInfo(pc, "Bonuses for " + type + ' ' + name + " not found"); + } + + @Override + protected String _getHelpString() { + return "Returns the player's current bonuses"; + } + + @Override + protected String _getUsageString() { + return "' /printbonuses'"; + } + +} diff --git a/src/engine/devcmd/cmds/PrintEquipCmd.java b/src/engine/devcmd/cmds/PrintEquipCmd.java new file mode 100644 index 00000000..284ff49e --- /dev/null +++ b/src/engine/devcmd/cmds/PrintEquipCmd.java @@ -0,0 +1,102 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.Enum.GameObjectType; +import engine.devcmd.AbstractDevCmd; +import engine.objects.*; + +import java.util.concurrent.ConcurrentHashMap; + +public class PrintEquipCmd extends AbstractDevCmd { + + public PrintEquipCmd() { + super("printequip"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + AbstractWorldObject tar; + String name; + String type = "PlayerCharacter"; + if (target != null) { + if (target instanceof AbstractCharacter) { + tar = (AbstractCharacter) target; + name = ((AbstractCharacter) tar).getFirstName(); + } else { + tar = pc; + name = ((AbstractCharacter) tar).getFirstName(); + } + } else { + tar = pc; + name = ((AbstractCharacter) tar).getFirstName(); + } + + //Get name and type + if (tar instanceof Mob) { + Mob mob = (Mob) tar; + MobBase mb = mob.getMobBase(); + if (mb != null) + name = mb.getFirstName(); + type = "Mob"; + } else if (tar instanceof NPC) { + NPC npc = (NPC) tar; + Contract contract = npc.getContract(); + if (contract != null) { + if (contract.isTrainer()) + name = tar.getName() + ", " + contract.getName(); + else + name = tar.getName() + " the " + contract.getName(); + } + type = "NPC"; + } + + if (tar.getObjectType() == GameObjectType.Mob){ + Mob tarMob = (Mob)tar; + throwbackInfo(pc, "Equip for " + type + ' ' + name + " (" + tar.getObjectUUID() + ')'); + for (int slot:tarMob.getEquip().keySet()){ + MobEquipment equip = tarMob.getEquip().get(slot); + throwbackInfo(pc, equip.getItemBase().getUUID() + " : " + equip.getItemBase().getName() + ", slot: " + slot); + } + return; + } + + if (tar.getObjectType() == GameObjectType.NPC){ + NPC tarMob = (NPC)tar; + throwbackInfo(pc, "Equip for " + type + ' ' + name + " (" + tar.getObjectUUID() + ')'); + for (int slot:tarMob.getEquip().keySet()){ + MobEquipment equip = tarMob.getEquip().get(slot); + throwbackInfo(pc,equip.getItemBase().getUUID() + " : " + equip.getItemBase().getName() + ", slot: " + slot); + } + return; + } + + CharacterItemManager cim = ((AbstractCharacter)tar).getCharItemManager(); + ConcurrentHashMap list = cim.getEquipped(); + throwbackInfo(pc, "Equip for " + type + ' ' + name + " (" + tar.getObjectUUID() + ')'); + for (Integer slot : list.keySet()) { + Item item = list.get(slot); + throwbackInfo(pc, " " + item.getItemBase().getName() + ", slot: " + slot); + } + } + + @Override + protected String _getHelpString() { + return "Returns the player's current equipment"; + } + + @Override + protected String _getUsageString() { + return "' /printequip'"; + } + +} diff --git a/src/engine/devcmd/cmds/PrintInventoryCmd.java b/src/engine/devcmd/cmds/PrintInventoryCmd.java new file mode 100644 index 00000000..c151b923 --- /dev/null +++ b/src/engine/devcmd/cmds/PrintInventoryCmd.java @@ -0,0 +1,109 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.Enum.ItemType; +import engine.devcmd.AbstractDevCmd; +import engine.objects.*; + +import java.text.DecimalFormat; +import java.util.ArrayList; + +public class PrintInventoryCmd extends AbstractDevCmd { + + public PrintInventoryCmd() { + super("printinventory"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + if (target == null || (!(target instanceof AbstractCharacter) && !(target instanceof Corpse))) { + target = pc; + } + + + String type = target.getClass().getSimpleName(); + String name = ""; + ArrayList inventory = null; + Item gold = null; + DecimalFormat df = new DecimalFormat("###,###,###,##0"); + + if (target instanceof AbstractCharacter) { + AbstractCharacter tar = (AbstractCharacter)target; + + name = tar.getFirstName(); + + if (tar instanceof Mob) { + Mob mob = (Mob) tar; + MobBase mb = mob.getMobBase(); + if (mb != null) { + name = mb.getFirstName(); + } + } else if (tar instanceof NPC) { + NPC npc = (NPC) tar; + Contract contract = npc.getContract(); + if (contract != null) { + if (contract.isTrainer()) { + name = tar.getName() + ", " + contract.getName(); + } else { + name = tar.getName() + " the " + contract.getName(); + } + } + } + + CharacterItemManager cim = tar.getCharItemManager(); + inventory = cim.getInventory(); //this list can contain gold when tar is a PC that is dead + gold = cim.getGoldInventory(); + throwbackInfo(pc, " Weight : " + (cim.getInventoryWeight() + cim.getEquipWeight())); + } else if (target instanceof Corpse) { + Corpse corpse = (Corpse) target; + name = "of " + corpse.getName(); + inventory = corpse.getInventory(); + } + + throwbackInfo(pc, "Inventory for " + type + ' ' + name + " (" + target.getObjectUUID() + ')'); + + int goldCount = 0; + + for (Item item : inventory) { + if (item.getItemBase().getType().equals(ItemType.GOLD) == false) { + String chargeInfo = ""; + byte chargeMax = item.getChargesMax(); + if (chargeMax > 0) { + byte charges = item.getChargesRemaining(); + chargeInfo = " charges: " + charges + '/' + chargeMax; + } + throwbackInfo(pc, " " + item.getItemBase().getName() + ", count: " + item.getNumOfItems() + chargeInfo); + } else goldCount += item.getNumOfItems(); + } + if (gold != null) { + goldCount += gold.getNumOfItems(); + } + + if (goldCount > 0 || gold != null) { + throwbackInfo(pc, " Gold, count: " + df.format(goldCount)); + } else { + throwbackInfo(pc, " NULL Gold"); + } + } + + @Override + protected String _getHelpString() { + return "Returns the player's current inventory"; + } + + @Override + protected String _getUsageString() { + return "' /printinventory'"; + } + +} diff --git a/src/engine/devcmd/cmds/PrintLocationCmd.java b/src/engine/devcmd/cmds/PrintLocationCmd.java new file mode 100644 index 00000000..4453c457 --- /dev/null +++ b/src/engine/devcmd/cmds/PrintLocationCmd.java @@ -0,0 +1,67 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; +import engine.objects.Regions; + +/** + * @author Eighty + * + */ +public class PrintLocationCmd extends AbstractDevCmd { + + public PrintLocationCmd() { + super("printloc"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + PlayerCharacter tar; + + if (target != null && target instanceof PlayerCharacter) + tar = (PlayerCharacter) target; + else + tar = pc; + + throwbackInfo(pc, "Server location for " + tar.getFirstName()); + if (tar.getLoc() != null) { + throwbackInfo(pc, "Lat: " + tar.getLoc().getX()); + throwbackInfo(pc, "Lon: " + -tar.getLoc().getZ()); + throwbackInfo(pc, "Alt: " + tar.getLoc().getY()); + if (pc.getRegion() != null) { + this.throwbackInfo(pc, "Player Region Slope Position : " + Regions.SlopeLerpPercent(pc, pc.getRegion())); + this.throwbackInfo(pc, "Region Slope Magnitude : " + Regions.GetMagnitudeOfRegionSlope(pc.getRegion())); + this.throwbackInfo(pc, "Player Region Slope Magnitude : " + Regions.GetMagnitudeOfPlayerOnRegionSlope(pc.getRegion(), pc)); + }else{ + this.throwbackInfo(pc, "No Region Found for player."); + } + + } else { + throwbackInfo(pc, "Server location for " + tar.getFirstName() + + " not found"); + } + } + + @Override + protected String _getHelpString() { + return "Returns the player's current location according to the server"; + } + + @Override + protected String _getUsageString() { + return "' /printloc'"; + } + +} diff --git a/src/engine/devcmd/cmds/PrintPowersCmd.java b/src/engine/devcmd/cmds/PrintPowersCmd.java new file mode 100644 index 00000000..8c9fa980 --- /dev/null +++ b/src/engine/devcmd/cmds/PrintPowersCmd.java @@ -0,0 +1,68 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.objects.AbstractGameObject; +import engine.objects.CharacterPower; +import engine.objects.PlayerCharacter; +import engine.powers.PowersBase; + +import java.util.concurrent.ConcurrentHashMap; + +public class PrintPowersCmd extends AbstractDevCmd { + + public PrintPowersCmd() { + super("printpowers"); + // super("printpowers", MBServerStatics.ACCESS_LEVEL_ADMIN); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + PlayerCharacter tar; + + if (target != null && target instanceof PlayerCharacter) + tar = (PlayerCharacter) target; + else + tar = pc; + + throwbackInfo(pc, "Server powers for " + tar.getFirstName()); + + ConcurrentHashMap powers = tar.getPowers(); + if (powers != null) { + throwbackInfo(pc, + "Power(token): Trains; TrainsGranted; MaxTrains"); + for (CharacterPower power : powers.values()) { + PowersBase pb = power.getPower(); + if (pb != null) { + throwbackInfo(pc, " " + pb.getName() + '(' + + pb.getToken() + "): " + + power.getTrains() + "; " + + power.getGrantedTrains() + "; " + + pb.getMaxTrains()); + } + } + } else + throwbackInfo(pc, "Powers not found for player"); + } + + @Override + protected String _getHelpString() { + return "Returns the player's current granted powers"; + } + + @Override + protected String _getUsageString() { + return "' /printpowers'"; + } + +} diff --git a/src/engine/devcmd/cmds/PrintResistsCmd.java b/src/engine/devcmd/cmds/PrintResistsCmd.java new file mode 100644 index 00000000..49a9e0e1 --- /dev/null +++ b/src/engine/devcmd/cmds/PrintResistsCmd.java @@ -0,0 +1,71 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.objects.*; + +public class PrintResistsCmd extends AbstractDevCmd { + + public PrintResistsCmd() { + super("printresists"); + // super("printresists", MBServerStatics.ACCESS_LEVEL_ADMIN); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + AbstractCharacter tar; + + if (target != null && target instanceof AbstractCharacter) { + tar = (AbstractCharacter) target; + } else + tar = pc; + + //Get name and type + String type = "PlayerCharacter"; + String name = tar.getFirstName(); + if (tar instanceof Mob) { + Mob mob = (Mob) tar; + MobBase mb = mob.getMobBase(); + if (mb != null) + name = mb.getFirstName(); + type = "Mob"; + } else if (tar instanceof NPC) { + NPC npc = (NPC) tar; + Contract contract = npc.getContract(); + if (contract != null) { + if (contract.isTrainer()) + name = tar.getName() + ", " + contract.getName(); + else + name = tar.getName() + " the " + contract.getName(); + } + type = "NPC"; + } + + throwbackInfo(pc, "Server resists for " + type + ' ' + name); + if (tar.getResists() != null) { + tar.getResists().printResistsToClient(pc); + } else + throwbackInfo(pc, "Resists for " + type + ' ' + name + " not found"); + } + + @Override + protected String _getHelpString() { + return "Returns the player's current resists"; + } + + @Override + protected String _getUsageString() { + return "' /printresists'"; + } + +} diff --git a/src/engine/devcmd/cmds/PrintSkillsCmd.java b/src/engine/devcmd/cmds/PrintSkillsCmd.java new file mode 100644 index 00000000..6ab5068a --- /dev/null +++ b/src/engine/devcmd/cmds/PrintSkillsCmd.java @@ -0,0 +1,66 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.Enum.GameObjectType; +import engine.devcmd.AbstractDevCmd; +import engine.objects.*; + +import java.util.concurrent.ConcurrentHashMap; + +public class PrintSkillsCmd extends AbstractDevCmd { + + public PrintSkillsCmd() { + super("printskills"); + // super("printskills", MBServerStatics.ACCESS_LEVEL_ADMIN); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + AbstractCharacter tar = null; + + if (target != null && target instanceof PlayerCharacter) + tar = (PlayerCharacter) target; + else if (target.getObjectType() == GameObjectType.Mob) + tar = (Mob) target; + else + tar = pc; + + throwbackInfo(pc, "Server skills for " + tar.getFirstName()); + ConcurrentHashMap skills = tar.getSkills(); + if (skills != null) { + throwbackInfo(pc, + "Skills Name: Trains; Base(Trains); ModBase(Trains)"); + for (CharacterSkill skill : skills.values()) { + throwbackInfo(pc, " " + skill.getName() + ": " + + skill.getNumTrains() + "; " + + skill.getBaseAmountBeforeMods() + " (" + + skill.getModifiedAmountBeforeMods() + "); " + + skill.getBaseAmount() + " (" + + skill.getModifiedAmount() + '(' + + skill.getTotalSkillPercet() + " )"); + } + } else + throwbackInfo(pc, "Skills not found for player"); + } + + @Override + protected String _getHelpString() { + return "Returns the player's current skills"; + } + + @Override + protected String _getUsageString() { + return "' /printskills'"; + } + +} diff --git a/src/engine/devcmd/cmds/PrintStatsCmd.java b/src/engine/devcmd/cmds/PrintStatsCmd.java new file mode 100644 index 00000000..aee35228 --- /dev/null +++ b/src/engine/devcmd/cmds/PrintStatsCmd.java @@ -0,0 +1,154 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.objects.*; + +import java.util.HashMap; + +/** + * + */ + +public class PrintStatsCmd extends AbstractDevCmd { + + public PrintStatsCmd() { + super("printstats"); + // super("printstats", MBServerStatics.ACCESS_LEVEL_ADMIN); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + AbstractCharacter tar; + + if (target != null && target instanceof AbstractCharacter) { + tar = (AbstractCharacter) target; + + if (tar instanceof PlayerCharacter) { + printStatsPlayer(pc, (PlayerCharacter) tar); + this.setTarget(tar); //for logging + } else if (tar instanceof Mob) + printStatsMob(pc, (Mob) tar); + else if (tar instanceof NPC) + printStatsNPC(pc, (NPC) tar); + } + } + + public void printStatsPlayer(PlayerCharacter pc, PlayerCharacter tar) { + String newline = "\r\n "; + String out = "Server stats for Player " + tar.getFirstName() + newline; + out += "Unused Stats: " + tar.getUnusedStatPoints() + newline; + out += "Stats Base (Modified)" + newline; + out += " Str: " + (int) tar.statStrBase + " (" + tar.getStatStrCurrent() + ')' + ", maxStr: " + tar.getStrMax() + newline; + out += " Dex: " + (int) tar.statDexBase + " (" + tar.getStatDexCurrent() + ')' + ", maxDex: " + tar.getDexMax() + newline; + out += " Con: " + (int) tar.statConBase + " (" + tar.getStatConCurrent() + ')' + ", maxCon: " + tar.getConMax() + newline; + out += " Int: " + (int) tar.statIntBase + " (" + tar.getStatIntCurrent() + ')' + ", maxInt: " + tar.getIntMax() + newline; + out += " Spi: " + (int) tar.statSpiBase + " (" + tar.getStatSpiCurrent() + ')' + ", maxSpi: " + tar.getSpiMax() + newline; + throwbackInfo(pc, out); + out = "Health: " + tar.getHealth() + ", maxHealth: " + tar.getHealthMax() + newline; + out += "Mana: " + tar.getMana() + ", maxMana: " + tar.getManaMax() + newline; + out += "Stamina: " + tar.getStamina() + ", maxStamina: " + tar.getStaminaMax() + newline; + out += "Defense: " + tar.getDefenseRating() + newline; + out += "Main Hand: atr: " + tar.getAtrHandOne() + ", damage: " + tar.getMinDamageHandOne() + " to " + tar.getMaxDamageHandOne() + ", speed: " + tar.getSpeedHandOne() + newline; + out += "Off Hand: atr: " + tar.getAtrHandTwo() + ", damage: " + tar.getMinDamageHandTwo() + " to " + tar.getMaxDamageHandTwo() + ", speed: " + tar.getSpeedHandTwo() + newline; + out += "isAlive: " + tar.isAlive() + ", Combat: " + tar.isCombat() + newline; + throwbackInfo(pc, out); + } + + public void printStatsMob(PlayerCharacter pc, Mob tar) { + MobBase mb = tar.getMobBase(); + if (mb == null) + return; + + + + String newline = "\r\n "; + String out = "Server stats for Mob " + mb.getFirstName() + newline; + out += "Stats Base (Modified)" + newline; + out += " Str: " + tar.getStatStrCurrent() + " (" + tar.getStatStrCurrent() + ')' + newline; + out += " Dex: " + tar.getStatDexCurrent() + " (" + tar.getStatDexCurrent() + ')' + ", maxDex: " + tar.getStatDexCurrent() + newline; + out += " Con: " + tar.getStatConCurrent() + " (" + tar.getStatConCurrent() + ')' + ", maxCon: " + tar.getStatConCurrent() + newline; + out += " Int: " + tar.getStatIntCurrent() + " (" + tar.getStatIntCurrent() + ')' + ", maxInt: " + tar.getStatIntCurrent() + newline; + out += " Spi: " + tar.getStatSpiCurrent() + " (" + tar.getStatSpiCurrent() + ')' + ", maxSpi: " + tar.getStatSpiCurrent() + newline; + + out += "Health: " + tar.getHealth() + ", maxHealth: " + tar.getHealthMax() + newline; + out += "Mana: " + tar.getMana() + ", maxMana: " + tar.getManaMax() + newline; + out += "Stamina: " + tar.getStamina() + ", maxStamina: " + tar.getStaminaMax() + newline; + out += "Defense: " + tar.getDefenseRating() + newline; + + //get weapons + HashMap equip = tar.getEquip(); + ItemBase main = null; + + if (equip != null) + main = getWeaponBase(1, equip); + ItemBase off = null; + + if (equip != null) + getWeaponBase(2, equip); + if (main == null && off == null) { + out += "Main Hand: atr: " + tar.getAtrHandOne() + ", damage: " + tar.getMinDamageHandOne() + " to " + tar.getMaxDamageHandOne() + ", speed: " + tar.getSpeedHandOne() + ", range: 6" + newline; + } else { + if (main != null) + out += "Main Hand: atr: " + tar.getAtrHandOne() + ", damage: " + tar.getMinDamageHandOne() + " to " + tar.getMaxDamageHandOne() + ", speed: " + tar.getSpeedHandOne() + ", range: " + main.getRange() + newline; + if (off != null) + out += "Main Hand: atr: " + tar.getAtrHandTwo() + ", damage: " + tar.getMinDamageHandTwo() + " to " + tar.getMaxDamageHandTwo() + ", speed: " + tar.getSpeedHandTwo() + ", range: " + off.getRange() + newline; + } + out += "isAlive: " + tar.isAlive() + ", Combat: " + tar.isCombat() + newline; + + throwbackInfo(pc, out); + } + + public void printStatsNPC(PlayerCharacter pc, NPC tar) { + Contract contract = tar.getContract(); + if (contract == null) + return; + + String newline = "\r\n "; + + String name; + if (contract != null) { + if (contract.isTrainer()) + name = tar.getName() + ", " + contract.getName(); + else + name = tar.getName() + " the " + contract.getName(); + } else + name = tar.getName(); + String out = "Server stats for NPC " + name + newline; + out += "Sell Percent: " + tar.getSellPercent() + ", Buy Percent: " + tar.getBuyPercent() + newline; + + throwbackInfo(pc, out); + } + + public static ItemBase getWeaponBase(int slot, HashMap equip) { + if (equip.containsKey(slot)) { + MobEquipment item = equip.get(slot); + if (item != null && item.getItemBase() != null) { + return item.getItemBase(); + } + } + return null; + } + + + @Override + protected String _getHelpString() { + return "Returns the player's current stats"; + } + + @Override + protected String _getUsageString() { + return "' /printstats'"; + } + +} diff --git a/src/engine/devcmd/cmds/PrintVaultCmd.java b/src/engine/devcmd/cmds/PrintVaultCmd.java new file mode 100644 index 00000000..7ddc411d --- /dev/null +++ b/src/engine/devcmd/cmds/PrintVaultCmd.java @@ -0,0 +1,72 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.objects.*; + +import java.util.ArrayList; + +public class PrintVaultCmd extends AbstractDevCmd { + + public PrintVaultCmd() { + super("printvault"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + AbstractWorldObject tar; + String name; + String type = "PlayerCharacter"; + if (target != null) { + if (target instanceof AbstractCharacter) { + tar = (AbstractCharacter) target; + name = ((AbstractCharacter) tar).getFirstName(); + } else { + tar = pc; + name = ((AbstractCharacter) tar).getFirstName(); + } + } else { + tar = pc; + name = ((AbstractCharacter) tar).getFirstName(); + } + + if (!(tar instanceof PlayerCharacter)) { + throwbackError(pc, "Must target player"); + return; + } + + + CharacterItemManager cim = ((AbstractCharacter)tar).getCharItemManager(); + ArrayList list = cim.getVault(); + throwbackInfo(pc, "Vault for " + type + ' ' + name + " (" + tar.getObjectUUID() + ')'); + for (Item item : list) { + throwbackInfo(pc, " " + item.getItemBase().getName() + ", count: " + item.getNumOfItems()); + } + Item gold = cim.getGoldVault(); + if (gold != null) + throwbackInfo(pc, " Gold, count: " + gold.getNumOfItems()); + else + throwbackInfo(pc, " NULL Gold"); + } + + @Override + protected String _getHelpString() { + return "Returns the player's current vault"; + } + + @Override + protected String _getUsageString() { + return "' /printvault'"; + } + +} diff --git a/src/engine/devcmd/cmds/PullCmd.java b/src/engine/devcmd/cmds/PullCmd.java new file mode 100644 index 00000000..16c4f4d4 --- /dev/null +++ b/src/engine/devcmd/cmds/PullCmd.java @@ -0,0 +1,104 @@ +package engine.devcmd.cmds; + +import engine.InterestManagement.WorldGrid; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.DbManager; +import engine.gameManager.ZoneManager; +import engine.math.Vector3fImmutable; +import engine.objects.*; + +/** + * + * @author + * Dev command to move mobile and it's spawn location + * to the player's current location + */ +public class PullCmd extends AbstractDevCmd { + + public PullCmd() { + super("pull"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + + Mob targetMobile; + Vector3fImmutable targetLoc; + Zone serverZone; + + if (validateUserInput(pcSender, target, args) == false) { + this.sendUsage(pcSender); + return; + } + + targetLoc = pcSender.getLoc(); + serverZone = ZoneManager.findSmallestZone(targetLoc); + switch (target.getObjectType()) { + case Mob: + MoveMobile((Mob) target, pcSender, targetLoc, serverZone); + break; + case Building: + MoveBuilding((Building) target, pcSender, targetLoc, serverZone); + break; + case NPC: + MoveNPC((NPC) target, pcSender, targetLoc, serverZone); + } + } + + @Override + protected String _getUsageString() { + return "/pull"; + } + + @Override + protected String _getHelpString() { + return "Moves mobile (and spawn) to player's location"; + } + + private boolean validateUserInput(PlayerCharacter pcSender, AbstractGameObject currTarget, String[] userInput) { + + // No target + if (currTarget == null) { + throwbackError(pcSender, "Requires a Mobile be targeted"); + return false; + } + return true; + } + + private static void MoveMobile(Mob targetMobile, PlayerCharacter pcSender, Vector3fImmutable newLoc, Zone serverZone) { + + Vector3fImmutable localCoords; + + localCoords = ZoneManager.worldToLocal(newLoc, serverZone); + + DbManager.MobQueries.MOVE_MOB(targetMobile.getObjectUUID(), serverZone.getObjectUUID(), localCoords.x, localCoords.y, localCoords.z); + targetMobile.setBindLoc(newLoc); + targetMobile.setLoc(newLoc); + targetMobile.refresh(); + } + + private static void MoveBuilding(Building targetBuilding, PlayerCharacter pcSender, Vector3fImmutable newLoc, Zone serverZone) { + + Vector3fImmutable localCoords; + + localCoords = ZoneManager.worldToLocal(newLoc, serverZone); + + DbManager.BuildingQueries.MOVE_BUILDING(targetBuilding.getObjectUUID(), serverZone.getObjectUUID(), localCoords.x, localCoords.y, localCoords.z); + targetBuilding.setLoc(newLoc); + targetBuilding.getBounds().setBounds(targetBuilding); + targetBuilding.refresh(true); + } + + private static void MoveNPC(NPC targetNPC, PlayerCharacter pcSender, Vector3fImmutable newLoc, Zone serverZone) { + + Vector3fImmutable localCoords; + + localCoords = ZoneManager.worldToLocal(newLoc, serverZone); + + DbManager.NPCQueries.MOVE_NPC(targetNPC.getObjectUUID(), serverZone.getObjectUUID(), localCoords.x, localCoords.y, localCoords.z); + targetNPC.setBindLoc(newLoc); + targetNPC.setLoc(newLoc); + WorldGrid.updateObject(targetNPC, pcSender); + } +} diff --git a/src/engine/devcmd/cmds/PurgeObjectsCmd.java b/src/engine/devcmd/cmds/PurgeObjectsCmd.java new file mode 100644 index 00000000..13545c84 --- /dev/null +++ b/src/engine/devcmd/cmds/PurgeObjectsCmd.java @@ -0,0 +1,315 @@ +package engine.devcmd.cmds; + +import engine.Enum; +import engine.Enum.BuildingGroup; +import engine.Enum.GameObjectType; +import engine.InterestManagement.WorldGrid; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.BuildingManager; +import engine.gameManager.DbManager; +import engine.gameManager.ZoneManager; +import engine.math.Vector3fImmutable; +import engine.objects.*; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.util.HashSet; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * @author + * Summary: Game designer utility command to purge all + objects of a given type within a supplied range + */ + +public class PurgeObjectsCmd extends AbstractDevCmd { + + // Instance variables + + private Vector3fImmutable _currentLocation; + private float _targetRange; + private int _targetMask; + + // Concurrency support + + private ReadWriteLock lock = new ReentrantReadWriteLock(); + + // Constructor + + public PurgeObjectsCmd() { + super("purge"); + } + + private static void PurgeWalls(Zone zone, PlayerCharacter pc){ + + if (!zone.isPlayerCity()) + return; + + for (Building building: zone.zoneBuildingSet){ + if (!BuildingManager.IsWallPiece(building)) + continue; + for (AbstractCharacter ac: building.getHirelings().keySet()){ + NPC npc = null; + Mob mobA = null; + + if (ac.getObjectType() == GameObjectType.NPC) + npc = (NPC)ac; + else if (ac.getObjectType() == GameObjectType.Mob) + mobA = (Mob)ac; + + + + if (npc != null){ + for (Mob mob: npc.getSiegeMinionMap().keySet()){ + WorldGrid.RemoveWorldObject(mob); + WorldGrid.removeObject(mob, pc); + //Mob.getRespawnMap().remove(mob); + if (mob.getParentZone() != null) + mob.getParentZone().zoneMobSet.remove(mob); + } + DbManager.NPCQueries.DELETE_NPC(npc); + DbManager.removeFromCache(GameObjectType.NPC, + npc.getObjectUUID()); + WorldGrid.RemoveWorldObject(npc); + }else if (mobA != null){ + for (Mob mob: mobA.getSiegeMinionMap().keySet()){ + WorldGrid.RemoveWorldObject(mob); + WorldGrid.removeObject(mob, pc); + //Mob.getRespawnMap().remove(mob); + if (mob.getParentZone() != null) + mob.getParentZone().zoneMobSet.remove(mob); + } + DbManager.MobQueries.DELETE_MOB(mobA); + DbManager.removeFromCache(GameObjectType.Mob, + mobA.getObjectUUID()); + WorldGrid.RemoveWorldObject(mobA); + } + + } + + + DbManager.BuildingQueries.DELETE_FROM_DATABASE(building); + DbManager.removeFromCache(building); + WorldGrid.RemoveWorldObject(building); + WorldGrid.removeObject(building, pc); + } + + } + + + // AbstractDevCmd Overridden methods + + @Override + protected void _doCmd(PlayerCharacter pc, String[] args, + AbstractGameObject target) { + + // Grab write lock due to use of instance variables + + lock.writeLock().lock(); + + try { + + if (args[0].toLowerCase().equals("walls")){ + Zone zone = ZoneManager.findSmallestZone(pc.getLoc()); + + PurgeWalls(zone, pc); + return; + } + + if(validateUserInput(args) == false) { + this.sendUsage(pc); + return; + } + + parseUserInput(args); + + // Arguments have been validated and parsed at this point + // Build array of requested objects + + _currentLocation = pc.getLoc(); + + HashSet objectList = + WorldGrid.getObjectsInRangePartial(_currentLocation, _targetRange, _targetMask); + + // Iterate through array and remove objects from game world and database + + for (AbstractWorldObject awo : objectList) { + + switch(awo.getObjectType()) { + case Building: + removeBuilding(pc, (Building) awo); + break; + case NPC: + removeNPC(pc, (NPC) awo); + break; + case Mob: + removeMob(pc, (Mob) awo); + break; + default: + break; + } + } + + // Send results to user + throwbackInfo(pc, "Purge: " + objectList.size() + " objects were removed in range " + _targetRange); + }catch(Exception e){ + Logger.error(e); + } + + // Release Reentrant lock + + finally { + lock.writeLock().unlock(); + } + } + + @Override + protected String _getHelpString() { + return "Purges game objects within range"; + } + + @Override + protected String _getUsageString() { + return "/purge [npc|mob|mesh|all] [range <= 200]"; + } + + // Class methods + + private static boolean validateUserInput(String[] userInput) { + + int stringIndex; + String commandSet = "npcmobmeshall"; + + // incorrect number of arguments test + + if (userInput.length != 2) + return false; + + // Test of game object type argument + + stringIndex = commandSet.indexOf(userInput[0].toLowerCase()); + + if (stringIndex == -1) + return false; + + // Test if range argument can convert to a float + + try { + Float.parseFloat(userInput[1]); } + catch (NumberFormatException | NullPointerException e) { + return false; + } + + // User input passes validation + + return true; + } + + private void parseUserInput(String[] userInput) { + + _targetMask = 0; + _targetRange = 0f; + + // Build mask from user input + + switch (userInput[0].toLowerCase()) { + case "npc": + _targetMask = MBServerStatics.MASK_NPC; + break; + case "mob": + _targetMask = MBServerStatics.MASK_MOB; + break; + case "mesh": + _targetMask = MBServerStatics.MASK_BUILDING; + break; + case "all": + _targetMask = MBServerStatics.MASK_NPC | MBServerStatics.MASK_MOB | MBServerStatics.MASK_BUILDING; + break; + default: + break; + } + + // Parse second argument into range parameter. Cap at 200 units. + + _targetRange = Float.parseFloat(userInput[1]); + _targetRange = Math.min(_targetRange, 200f); + } + + private static void removeBuilding(PlayerCharacter pc, Building building) { + + if ((building.getBlueprintUUID() != 0) && + (building.getBlueprint().getBuildingGroup() == BuildingGroup.TOL)) + return; + if ((building.getBlueprintUUID() != 0) && + (building.getBlueprint().getBuildingGroup() == BuildingGroup.SHRINE)) + Shrine.RemoveShrineFromCacheByBuilding(building); + + if ((building.getBlueprint() != null) && (building.getBlueprint().getBuildingGroup() == BuildingGroup.SPIRE)) + building.disableSpire(false); + + for (AbstractCharacter ac: building.getHirelings().keySet()){ + NPC npc = null; + Mob mobA = null; + + if (ac.getObjectType() == GameObjectType.NPC) + npc = (NPC)ac; + else if (ac.getObjectType() == GameObjectType.Mob) + mobA = (Mob)ac; + + + + if (npc != null){ + for (Mob mob: npc.getSiegeMinionMap().keySet()){ + WorldGrid.RemoveWorldObject(mob); + WorldGrid.removeObject(mob, pc); + //Mob.getRespawnMap().remove(mob); + if (mob.getParentZone() != null) + mob.getParentZone().zoneMobSet.remove(mob); + } + DbManager.NPCQueries.DELETE_NPC(npc); + DbManager.removeFromCache(Enum.GameObjectType.NPC, + npc.getObjectUUID()); + WorldGrid.RemoveWorldObject(npc); + }else if (mobA != null){ + for (Mob mob: mobA.getSiegeMinionMap().keySet()){ + WorldGrid.RemoveWorldObject(mob); + WorldGrid.removeObject(mob, pc); + //Mob.getRespawnMap().remove(mob); + if (mob.getParentZone() != null) + mob.getParentZone().zoneMobSet.remove(mob); + } + DbManager.MobQueries.DELETE_MOB(mobA); + DbManager.removeFromCache(Enum.GameObjectType.Mob, + mobA.getObjectUUID()); + WorldGrid.RemoveWorldObject(mobA); + } + + } + + + DbManager.BuildingQueries.DELETE_FROM_DATABASE(building); + DbManager.removeFromCache(building); + WorldGrid.RemoveWorldObject(building); + WorldGrid.removeObject(building, pc); + } + + private static void removeNPC(PlayerCharacter pc, NPC npc) { + DbManager.NPCQueries.DELETE_NPC(npc); + DbManager.removeFromCache(npc); + WorldGrid.RemoveWorldObject(npc); + WorldGrid.removeObject(npc, pc); + } + + private static void removeMob(PlayerCharacter pc, Mob mob) { + mob.setLoc(Vector3fImmutable.ZERO); //Move it off the plane.. + mob.setBindLoc(Vector3fImmutable.ZERO); //Reset the bind loc.. + //mob.setHealth(-1, pc); //Kill it! + + DbManager.MobQueries.DELETE_MOB(mob); + DbManager.removeFromCache(mob); + WorldGrid.RemoveWorldObject(mob); + WorldGrid.removeObject(mob, pc); + } + +} diff --git a/src/engine/devcmd/cmds/RealmInfoCmd.java b/src/engine/devcmd/cmds/RealmInfoCmd.java new file mode 100644 index 00000000..9ee6b695 --- /dev/null +++ b/src/engine/devcmd/cmds/RealmInfoCmd.java @@ -0,0 +1,92 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + + +import engine.InterestManagement.RealmMap; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ZoneManager; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; +import engine.objects.Realm; +import engine.objects.Zone; + +public class RealmInfoCmd extends AbstractDevCmd { + + public RealmInfoCmd() { + super("realminfo"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + Zone serverZone; + Zone parentZone; + Realm serverRealm; + int realmID; + String outString = ""; + + if (pc == null) { + throwbackError(pc, "Unable to find the pc making the request."); + return; + } + + serverZone = ZoneManager.findSmallestZone(pc.getLoc()); + + if (serverZone == null) { + throwbackError(pc, "Zone not found"); + return; + } + + parentZone = serverZone.getParent(); + + realmID = RealmMap.getRealmIDAtLocation(pc.getLoc()); + + String newline = "\r\n "; + + outString = newline; + outString += "RealmID: " + realmID; + + serverRealm = Realm.getRealm(realmID); + + if (serverRealm == null) + outString += " Name: SeaFloor"; + else + outString += serverRealm.getRealmName(); + + outString += newline; + + outString += " Zone: " + serverZone.getName(); + + outString += newline; + + if (serverZone.getParent() != null) + outString += " Parent: " + serverZone.getParent().getName(); + else + outString += "Parent: NONE"; + + outString += newline; + + throwbackInfo(pc, outString); + } + + @Override + protected String _getHelpString() { + return "Returns info on realm."; + } + + @Override + protected String _getUsageString() { + return "' /info targetID'"; + } + +} diff --git a/src/engine/devcmd/cmds/RebootCmd.java b/src/engine/devcmd/cmds/RebootCmd.java new file mode 100644 index 00000000..2fe01c44 --- /dev/null +++ b/src/engine/devcmd/cmds/RebootCmd.java @@ -0,0 +1,48 @@ +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; +import org.pmw.tinylog.Logger; + +/** + * @author + * Summary: Devcmd to reboot server + * + */ + +public class RebootCmd extends AbstractDevCmd { + + // Instance variables + + public RebootCmd() { + super("reboot"); + } + + + // AbstractDevCmd Overridden methods + + @Override + protected void _doCmd(PlayerCharacter pc, String[] args, + AbstractGameObject target) { + + try { + Runtime rt = Runtime.getRuntime(); + rt.exec("./mbrestart.sh"); + } catch (java.io.IOException err) { + Logger.info( err.getMessage()); + } + + } + + @Override + protected String _getHelpString() { + return "Reboot server"; + } + + @Override + protected String _getUsageString() { + return "./reboot"; + } + +} diff --git a/src/engine/devcmd/cmds/RegionCmd.java b/src/engine/devcmd/cmds/RegionCmd.java new file mode 100644 index 00000000..48fd5390 --- /dev/null +++ b/src/engine/devcmd/cmds/RegionCmd.java @@ -0,0 +1,79 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; + +import java.lang.reflect.Field; + +public class RegionCmd extends AbstractDevCmd { + + public RegionCmd() { + super("region"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + + if (pc.getRegion() == null){ + this.throwbackInfo(pc, "No Region Found."); + return; + } + + + String newLine = System.getProperty("line.separator"); + String result = ""; + result+=(pc.getRegion().getClass().getSimpleName()); + result+=( " {" ); + result+=(newLine); + Field[] fields = pc.getRegion().getClass().getDeclaredFields(); + + //print field names paired with their values + for ( Field field : fields ) { + field.setAccessible(true); + result+=(" "); + try { + + if(field.getName().contains("Furniture")) + continue; + result+=( field.getName()); + result+=(": "); + //requires access to private field: + result+=( field.get(pc.getRegion()).toString()); + } catch ( IllegalAccessException ex ) { + System.out.println(ex); + } + result.trim(); + result+=(newLine); + } + result+=("}"); + + this.throwbackInfo(pc, result.toString()); + + + } + + @Override + protected String _getHelpString() { + return "Temporarily Changes SubRace"; + } + + @Override + protected String _getUsageString() { + return "' /setBuildingCollidables add/remove 'add creates a collision line.' needs 4 integers. startX, endX, startY, endY"; + + } + +} diff --git a/src/engine/devcmd/cmds/RemoveBaneCmd.java b/src/engine/devcmd/cmds/RemoveBaneCmd.java new file mode 100644 index 00000000..98de9f90 --- /dev/null +++ b/src/engine/devcmd/cmds/RemoveBaneCmd.java @@ -0,0 +1,70 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.Enum.SiegeResult; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ZoneManager; +import engine.objects.*; + +/** + * @author Eighty + * + */ +public class RemoveBaneCmd extends AbstractDevCmd { + + public RemoveBaneCmd() { + super("removebane"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + Zone zone = ZoneManager.findSmallestZone(pc.getLoc()); + + if (zone == null) { + throwbackError(pc, "Unable to find the zone you're in."); + return; + } + + if (!zone.isPlayerCity()) { + throwbackError(pc, "This is not a player city."); + return; + } + + City city = City.getCity(zone.getPlayerCityUUID()); + if (city == null) { + throwbackError(pc, "Unable to find the city associated with this zone."); + return; + } + + Bane bane = city.getBane(); + if (bane == null) { + throwbackError(pc, "Could not find bane to remove."); + return; + } + + bane.endBane(SiegeResult.DEFEND); + + throwbackInfo(pc, "The bane has been removed."); + } + + @Override + protected String _getHelpString() { + return "Removes a bane from the city grid you're standing on."; + } + + @Override + protected String _getUsageString() { + return "'./removebane'"; + } + +} diff --git a/src/engine/devcmd/cmds/RemoveObjectCmd.java b/src/engine/devcmd/cmds/RemoveObjectCmd.java new file mode 100644 index 00000000..5b176002 --- /dev/null +++ b/src/engine/devcmd/cmds/RemoveObjectCmd.java @@ -0,0 +1,253 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.Enum.BuildingGroup; +import engine.Enum.DbObjectType; +import engine.Enum.GameObjectType; +import engine.InterestManagement.WorldGrid; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.BuildingManager; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.gameManager.ZoneManager; +import engine.math.Vector3fImmutable; +import engine.objects.*; + +/** + * + */ +public class RemoveObjectCmd extends AbstractDevCmd { + + public RemoveObjectCmd() { + //set to Player access level so it can be run by non-admins on production. + //Actual access level is set in _doCmd. + super("remove"); + this.addCmdString("delete"); + } + + @Override + protected void _doCmd(PlayerCharacter player, String[] words, AbstractGameObject target) { + + int targetID; + DbObjectType targetObjType; + Building targetBuilding; + NPC targetNPC; + Mob targetMob; + + if (target == null && words.length != 1) { + this.sendUsage(player); + return; + } + + // Delete the targeted building + + if (target != null) { + + switch (target.getObjectType()) { + + case Building: + removeBuilding(player, (Building) target); + break; + case NPC: + removeNPC(player, (NPC) target); + break; + case Mob: + removeMob(player, (Mob) target); + break; + default: + throwbackError(player, "Target " + target.getObjectType() + + " is not a valid object type"); + break; + } + return; + } + + // Attempt to delete object based upon parsed UUID from input + + // Parse Target UUID + + try { + targetID = Integer.parseInt(words[0]); + } catch (NumberFormatException e) { + throwbackError(player, "Supplied object ID " + words[0] + + " failed to parse to an Integer"); + return; + } + + // Determine object type of given UUID + + targetObjType = DbManager.BuildingQueries.GET_UID_ENUM(targetID); + + // Process accordingly + + switch (targetObjType) { + case BUILDING: + targetBuilding = BuildingManager.getBuilding(targetID); + removeBuilding(player, targetBuilding); + break; + case NPC: + targetNPC = NPC.getNPC(targetID); + removeNPC(player, targetNPC); + break; + case MOB: + targetMob = Mob.getMob(targetID); + removeMob(player, targetMob); + break; + default: + throwbackError(player, "Invalid UUID: Not found in database"); + break; + } + + } + + @Override + protected String _getHelpString() { + return "Removes targeted or specified object"; + } + + @Override + protected String _getUsageString() { + return "' /remove [objectID]' || ' /delete [objectID]'"; + } + + private void removeBuilding(PlayerCharacter pc, Building building) { + + Zone currentZone = ZoneManager.findSmallestZone(pc.getLoc()); + + if (currentZone == null) { + this.throwbackError(pc, "Could not locate zone for player."); + return; + } + + if ((building.getBlueprint() != null) && (building.getBlueprint().getBuildingGroup() == BuildingGroup.SPIRE)) + building.disableSpire(false); + + if ((building.getBlueprint() != null) && (building.getBlueprint().getBuildingGroup() == BuildingGroup.WAREHOUSE)){ + City city =City.getCity(building.getParentZone().getPlayerCityUUID()); + if (city != null){ + city.setWarehouseBuildingID(0); + } + Warehouse.warehouseByBuildingUUID.remove(building.getObjectUUID()); + } + + + //remove cached shrines. + if ((building.getBlueprintUUID() != 0) + && (building.getBlueprint().getBuildingGroup() == BuildingGroup.SHRINE)) + Shrine.RemoveShrineFromCacheByBuilding(building); + + for (AbstractCharacter ac : building.getHirelings().keySet()) { + NPC npc = null; + Mob mobA = null; + + if (ac.getObjectType() == GameObjectType.NPC) + npc = (NPC)ac; + else if (ac.getObjectType() == GameObjectType.Mob) + mobA = (Mob)ac; + + if (npc != null){ + for (Mob mob : npc.getSiegeMinionMap().keySet()) { + WorldGrid.RemoveWorldObject(mob); + WorldGrid.removeObject(mob, pc); + //Mob.getRespawnMap().remove(mob); + if (mob.getParentZone() != null) + mob.getParentZone().zoneMobSet.remove(mob); + } + DbManager.NPCQueries.DELETE_NPC(npc); + DbManager.removeFromCache(npc); + WorldGrid.RemoveWorldObject(npc); + WorldGrid.removeObject(npc, pc); + }else if (mobA != null){ + for (Mob mob : mobA.getSiegeMinionMap().keySet()) { + WorldGrid.RemoveWorldObject(mob); + WorldGrid.removeObject(mob, pc); + //Mob.getRespawnMap().remove(mob); + if (mob.getParentZone() != null) + mob.getParentZone().zoneMobSet.remove(mob); + } + DbManager.MobQueries.DELETE_MOB(mobA); + DbManager.removeFromCache(mobA); + WorldGrid.RemoveWorldObject(mobA); + WorldGrid.removeObject(mobA, pc); + } + + + } + Zone zone = building.getParentZone(); + DbManager.BuildingQueries.DELETE_FROM_DATABASE(building); + DbManager.removeFromCache(building); + zone.zoneBuildingSet.remove(building); + WorldGrid.RemoveWorldObject(building); + WorldGrid.removeObject(building, pc); + + ChatManager.chatSayInfo(pc, + "Building with ID " + building.getObjectUUID() + " removed"); + this.setResult(String.valueOf(building.getObjectUUID())); + } + + private void removeNPC(PlayerCharacter pc, NPC npc) { + + Zone currentZone = ZoneManager.findSmallestZone(pc.getLoc()); + if (currentZone == null) { + this.throwbackError(pc, "Could not locate zone for player."); + return; + } + + + + for (Mob mob : npc.getSiegeMinionMap().keySet()) { + WorldGrid.RemoveWorldObject(mob); + WorldGrid.removeObject(mob, pc); + if (mob.getParentZone() != null) + mob.getParentZone().zoneMobSet.remove(mob); + } + + DbManager.NPCQueries.DELETE_NPC(npc); + DbManager.removeFromCache(npc); + WorldGrid.RemoveWorldObject(npc); + WorldGrid.removeObject(npc, pc); + ChatManager.chatSayInfo(pc, + "NPC with ID " + npc.getDBID() + " removed"); + this.setResult(String.valueOf(npc.getDBID())); + } + + private void removeMob(PlayerCharacter pc, Mob mob) { + + Zone currentZone = ZoneManager.findSmallestZone(pc.getLoc()); + if (currentZone == null) { + this.throwbackError(pc, "Could not locate zone for player."); + return; + } + + if (mob.getParentZone() != null && mob.getParentZone() != currentZone && !mob.isPet() && !mob.isNecroPet()) { + this.throwbackError(pc, "Error 376954: Could not Remove Mob.Mob is not in the same zone as player."); + return; + } + + mob.setLoc(Vector3fImmutable.ZERO); //Move it off the plane.. + mob.setBindLoc(Vector3fImmutable.ZERO); //Reset the bind loc.. + //mob.setHealth(-1, pc); //Kill it! + + DbManager.MobQueries.DELETE_MOB(mob); + + DbManager.removeFromCache(mob); + WorldGrid.RemoveWorldObject(mob); + WorldGrid.removeObject(mob, pc); + + if (mob.getParentZone() != null) + mob.getParentZone().zoneMobSet.remove(mob); + + ChatManager.chatSayInfo(pc, + "Mob with ID " + mob.getDBID() + " removed"); + this.setResult(String.valueOf(mob.getDBID())); + } + +} diff --git a/src/engine/devcmd/cmds/RenameCmd.java b/src/engine/devcmd/cmds/RenameCmd.java new file mode 100644 index 00000000..73db6a24 --- /dev/null +++ b/src/engine/devcmd/cmds/RenameCmd.java @@ -0,0 +1,81 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.InterestManagement.WorldGrid; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.DbManager; +import engine.objects.AbstractGameObject; +import engine.objects.Building; +import engine.objects.NPC; +import engine.objects.PlayerCharacter; + +/** + * + * @author Eighty + * + */ +public class RenameCmd extends AbstractDevCmd { + + public RenameCmd() { + super("rename"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + if (args.length < 1) { + this.sendUsage(pcSender); + return; + } + if (args[0].isEmpty()) { + throwbackError(pcSender, "Invalid rename Command. must specify a name."); + return; + } + NPC npc = null; + Building building = null; + + if (target != null) { + if (target instanceof NPC) + npc = (NPC) target; + else if (target instanceof Building) + building = (Building)target; + } else + npc = getTargetAsNPC(pcSender); + if (npc != null) { + DbManager.NPCQueries.SET_PROPERTY(npc, "npc_name", args[0]); + String name = args[0]; + name = name.replaceAll("_", " "); + + npc.setName(name); + + this.setResult(String.valueOf(npc.getDBID())); + +// npc.updateDatabase(); + WorldGrid.updateObject(npc, pcSender); + } else if (building != null){ + String name = args[0]; + name = name.replaceAll("_", " "); + building.setName(name); + } + throwbackError(pcSender, "Invalid rename Command. must target an npc."); + } + + @Override + protected String _getUsageString() { + return "' /rename npcName'"; + } + + @Override + protected String _getHelpString() { + return "Renames an NPC."; + } + +} diff --git a/src/engine/devcmd/cmds/RenameMobCmd.java b/src/engine/devcmd/cmds/RenameMobCmd.java new file mode 100644 index 00000000..0e0dee1f --- /dev/null +++ b/src/engine/devcmd/cmds/RenameMobCmd.java @@ -0,0 +1,101 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ChatManager; +import engine.objects.AbstractGameObject; +import engine.objects.MobBase; +import engine.objects.NPC; +import engine.objects.PlayerCharacter; + +/** + * + * @author Eighty + * + */ +public class RenameMobCmd extends AbstractDevCmd { + + public RenameMobCmd() { + super("renamemob"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + if (args.length < 1) { + this.sendUsage(pcSender); + return; + } + int loadID = 0; + String name = ""; + NPC npc; + if (target != null && target instanceof NPC) + npc = (NPC) target; + else + npc = getTargetAsNPC(pcSender); + if (npc != null) { + for (int i = 0; i < args.length; i++) { + name += args[i]; + if (i + 1 < args.length) + name += " "; + } + npc.setName(name); + npc.updateDatabase(); + ChatManager.chatSayInfo( + pcSender, + "NPC with ID " + npc.getObjectUUID() + " renamed to " + + npc.getFirstName()); + } else { + try { + loadID = Integer.parseInt(args[0]); + if (args.length > 1) { + for (int i = 1; i < args.length; i++) { + name += args[i]; + if (i + 1 < args.length) + name += " "; + } + } + } catch (Exception e) { + throwbackError(pcSender, + "Invalid renameMob Command. Need mob ID specified."); + return; // NaN + } + MobBase mob = MobBase.getMobBase(loadID); + if (mob == null) { + throwbackError(pcSender, + "Invalid renameMob Command. Mob ID specified is not valid."); + return; + } + if (!MobBase.renameMobBase(mob.getObjectUUID(), name)) { + throwbackError(pcSender, + "renameMob SQL Error. Failed to rename mob."); + return; + } + mob = MobBase.getMobBase(mob.getObjectUUID(), true); // force refresh + // from db + ChatManager.chatSayInfo( + pcSender, + "MobBase with ID " + mob.getObjectUUID() + " renamed to " + + mob.getFirstName()); + } + } + + @Override + protected String _getUsageString() { + return "' /renamemob [ID] newName'"; + } + + @Override + protected String _getHelpString() { + return "Changes a mobs old name to a new name"; + } + +} diff --git a/src/engine/devcmd/cmds/ResetLevelCmd.java b/src/engine/devcmd/cmds/ResetLevelCmd.java new file mode 100644 index 00000000..ef3dc496 --- /dev/null +++ b/src/engine/devcmd/cmds/ResetLevelCmd.java @@ -0,0 +1,36 @@ +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; + +public class ResetLevelCmd extends AbstractDevCmd { + + public ResetLevelCmd() { + super("resetlevel"); + } + + // AbstractDevCmd Overridden methods + + @Override + protected void _doCmd(PlayerCharacter player, String[] args, + AbstractGameObject target) { + + player.ResetLevel(Short.parseShort(args[0])); + } + + @Override + protected String _getHelpString() { + return "Resets character level to `level`. All training points are reset. Player must relog for changes to update."; + } + + @Override + protected String _getUsageString() { + + + return "/resetlevel "; + } + + + +} diff --git a/src/engine/devcmd/cmds/RotateCmd.java b/src/engine/devcmd/cmds/RotateCmd.java new file mode 100644 index 00000000..c6dc7c73 --- /dev/null +++ b/src/engine/devcmd/cmds/RotateCmd.java @@ -0,0 +1,251 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.InterestManagement.WorldGrid; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.BuildingManager; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.math.Vector3f; +import engine.math.Vector3fImmutable; +import engine.objects.*; + +public class RotateCmd extends AbstractDevCmd { + + public RotateCmd() { + super("rotate"); + this.addCmdString("rot"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + if (target == null && (words.length != 2) ) { + this.sendUsage(pc); + return; + } + + + if (words.length == 3){ + try{ + + }catch(Exception e){ + + } + } + + + float rot; + if (target != null && words.length == 1) { + + try { + if (words[0].equalsIgnoreCase("face")){ + this.rotateFace(pc, target); + return; + } + + rot = Float.parseFloat(words[0]); + } catch (NumberFormatException e) { + throwbackError(pc, "Supplied rotation " + words[0] + + " failed to parse to a Float"); + return; + } catch (Exception e) { + throwbackError(pc, + "Invalid Rotate Command. Need Rotation specified."); + return; + } + + Vector3f rotation = new Vector3f(0f, rot, 0f); + + if (target instanceof Building) + rotateBuilding(pc, (Building) target, rotation, rot,false); + else if (target instanceof NPC) + rotateNPC(pc, (NPC) target, rotation,false); + else if (target instanceof Mob) + rotateMob(pc, (Mob) target, rotation,false); + else + throwbackError(pc, "Target " + target.getObjectType() + + " is not a valid object type"); + } else { + + int id = 0; + if (words.length == 2) { + try { + id = Integer.parseInt(words[0]); + + if (words[1].equalsIgnoreCase("face")){ + + Building b; + if (id != 0) + b = BuildingManager.getBuilding(id); + else + b = getTargetAsBuilding(pc); + if (b != null) { + rotateFace(pc, b); + return; + } + + // building failed, try npc + NPC npc; + if (id != 0) + npc = NPC.getNPC(id); + else + npc = getTargetAsNPC(pc); + if (npc != null) { + rotateFace(pc, npc); + return; + } + + // NPC failed, try mob + Mob mob; + if (id != 0) + mob = Mob.getMob(id); + else + mob = getTargetAsMob(pc); + if (mob != null) { + rotateFace(pc, mob); + return; + } + throwbackError(pc, "Nothing found to rotate."); + return; + } + rot = Float.parseFloat(words[1]); + } catch (NumberFormatException e) { + throwbackError(pc, "Supplied arguments " + words[0] + ' ' + + words[1] + " failed to parse"); + return; + } catch (Exception e) { + throwbackError(pc, + "Invalid Rotate Command. Need Rotation specified."); + return; // NaN + } + } else { + try { + rot = Float.parseFloat(words[0]); + } catch (NumberFormatException e) { + throwbackError(pc, "Supplied rotation " + words[0] + + " failed to parse to a Float"); + return; + } catch (Exception e) { + throwbackError(pc, + "Invalid Rotate Command. Need Rotation specified."); + return; // NaN + } + } + + Vector3f rotation = new Vector3f(0f, rot, 0f); + + Building b; + if (id != 0) + b = BuildingManager.getBuilding(id); + else + b = getTargetAsBuilding(pc); + if (b != null) { + rotateBuilding(pc, b, rotation, rot,false); + return; + } + + // building failed, try npc + NPC npc; + if (id != 0) + npc = NPC.getNPC(id); + else + npc = getTargetAsNPC(pc); + if (npc != null) { + rotateNPC(pc, npc, rotation,false); + return; + } + + // NPC failed, try mob + Mob mob; + if (id != 0) + mob = Mob.getMob(id); + else + mob = getTargetAsMob(pc); + if (mob != null) { + rotateMob(pc, mob, rotation,false); + return; + } + throwbackError(pc, "Nothing found to rotate."); + } + } + + @Override + protected String _getHelpString() { + return "Rotates targeted or specified object"; + } + + @Override + protected String _getUsageString() { + return "' /rotate [objectID] rotation' || ' /rot [objectID] rotation'"; + } + + private void rotateBuilding(PlayerCharacter pc, Building building, Vector3f rot, float orig, boolean faceDirection) { + if (!faceDirection) + rot.set(0.0f, (float)Math.sin(Math.toRadians(orig)/2), 0.0f); + building.setRot(rot); + building.setw( (float) Math.abs(Math.cos(Math.toRadians(orig)/2)) ); + building.getBounds().setBounds(building); + WorldGrid.updateObject(building, pc); + DbManager.BuildingQueries.SET_PROPERTY(building, "rotY", building.getRot().getY()); + DbManager.BuildingQueries.SET_PROPERTY(building, "w", building.getw()); + ChatManager.chatSayInfo(pc, + "Building with ID " + building.getObjectUUID() + " rotated"); + } + + private void rotateNPC(PlayerCharacter pc, NPC npc, Vector3f rot,boolean faceDirection) { + npc.setRot(rot); + DbManager.NPCQueries.SET_PROPERTY(npc, "npc_rotation", rot.y); + WorldGrid.updateObject(npc, pc); + //no rotation for npc's in db currently + ChatManager.chatSayInfo(pc, + "NPC with ID " + npc.getObjectUUID() + " rotated"); + } + + private void rotateMob(PlayerCharacter pc, Mob mob, Vector3f rot,boolean faceDirection) { + mob.setRot(rot); + DbManager.MobQueries.SET_PROPERTY(mob, "mob_rotation", rot.y); + WorldGrid.updateObject(mob, pc); + //no rotation for mobs's in db currently + ChatManager.chatSayInfo(pc, + "Mob with ID " + mob.getObjectUUID() + " rotated"); + } + + private void rotateFace(PlayerCharacter pc, AbstractGameObject target){ + AbstractWorldObject awo = (AbstractWorldObject)target; + if (awo == null) + return; + Vector3fImmutable buildingLoc = awo.getLoc(); + Vector3fImmutable playerLoc = pc.getLoc(); + + Vector3fImmutable faceDirection = playerLoc.subtract2D(buildingLoc); + + float rotangle = faceDirection.getRotation(); + + float rot = (float) Math.toDegrees(rotangle); + + if (rot > 180) + rot*=-1; + + Vector3f buildingrotation = new Vector3f(0f, rot, 0f); + Vector3f rotation = new Vector3f(0f, rotangle, 0f); + if (target instanceof Building) + rotateBuilding(pc, (Building) target, buildingrotation, rot,false); + else if (target instanceof NPC) + rotateNPC(pc, (NPC) target, rotation,true); + else if (target instanceof Mob) + rotateMob(pc, (Mob) target, rotation,true); + else + throwbackError(pc, "Target " + target.getObjectType() + + " is not a valid object type"); + + } +} \ No newline at end of file diff --git a/src/engine/devcmd/cmds/SetAICmd.java b/src/engine/devcmd/cmds/SetAICmd.java new file mode 100644 index 00000000..607e1c8b --- /dev/null +++ b/src/engine/devcmd/cmds/SetAICmd.java @@ -0,0 +1,128 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.DbManager; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; + +/** + * @author Steve + * + */ +public class SetAICmd extends AbstractDevCmd { + + public SetAICmd() { + super("setAI"); + this.addCmdString("ai"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + if (words.length < 2){ + this.sendUsage(pc); + return; + } + + int amount; + + try{ + amount = Integer.valueOf(words[1]); + }catch (NumberFormatException e) { + this.throwbackError(pc, "Failed to parse amount"); + return; + } + + switch(words[0]){ + case "angle" : + float angle = Float.parseFloat(words[1]); + + MBServerStatics.AI_MAX_ANGLE = angle; + break; + case "aggrorange": + MBServerStatics.AI_BASE_AGGRO_RANGE = amount; + DbManager.MobBaseQueries.UPDATE_AI_DEFAULTS(); + this.throwbackInfo(pc, "Aggro Range is now set to " + amount); + break; + case "dropaggrorange": + MBServerStatics.AI_DROP_AGGRO_RANGE = amount; + DbManager.MobBaseQueries.UPDATE_AI_DEFAULTS(); + this.throwbackInfo(pc, "Drop Aggro Range is now set to " + amount); + break; + case "patroldivisor": + MBServerStatics.AI_PATROL_DIVISOR = amount; + DbManager.MobBaseQueries.UPDATE_AI_DEFAULTS(); + this.throwbackInfo(pc, "Patrol Chance is now set to " + amount); + break; + case "pulse": + if (amount < 500){ + this.throwbackError(pc, "pulse amount must be greather than 500 to execute."); + return; + } + MBServerStatics.AI_PULSE_MOB_THRESHOLD = amount; + this.throwbackInfo(pc, "Pulse is now set to " + amount); + break; + case "sleepthread": + if (amount < 500){ + this.throwbackError(pc, "sleep amount must be greather than 500 to execute."); + return; + } + MBServerStatics.AI_THREAD_SLEEP = amount; + this.throwbackInfo(pc, "Thread Sleep is now set to " + amount); + break; + case "recallrange": + MBServerStatics.AI_RECALL_RANGE = amount; + DbManager.MobBaseQueries.UPDATE_AI_DEFAULTS(); + this.throwbackInfo(pc, "Recall Range is now set to " + amount); + break; + case "powerdivisor": + MBServerStatics.AI_POWER_DIVISOR = amount; + DbManager.MobBaseQueries.UPDATE_AI_DEFAULTS(); + this.throwbackInfo(pc, "Power Divisor is now set to " + amount); + break; + case "losehate": + MBServerStatics.PLAYER_HATE_DELIMITER = amount; + break; + case "hatemodcombat": + MBServerStatics.PLAYER_COMBAT_HATE_MODIFIER = amount; + default: + this.throwbackError(pc, words[0] + " is not a valid AI Command."); + break; + } + } + + @Override + protected String _getHelpString() { + String help = "Modifies Mob AI Statics. Commands:"; + help += "\n AGGRORANGE: Sets the range when a mob will aggro it's target. Aggro range is currently " + MBServerStatics.AI_BASE_AGGRO_RANGE; + help += "\n DROPAGGRORANGE: Sets the range when a mob will drop aggro from it's target. Drop aggro range is currently " + MBServerStatics.AI_DROP_AGGRO_RANGE; + help += "\n PATROLDIVISOR: Sets the Patrol Divisor for Mob AI. Setting this will give a 1/[amount] chance to parol the area. Patrol Chance is currently 1/" + MBServerStatics.AI_PATROL_DIVISOR; + help += "\n PULSE: sets how often to run mob's AI. Measured in MS. Pulse is currently " + MBServerStatics.AI_PULSE_MOB_THRESHOLD + "ms."; + help += "\n SLEEPTHREAD: Sets how long to sleep the AI for ALL mobs.Thread sleep is currently " + MBServerStatics.AI_THREAD_SLEEP + "ms."; + help += "\n RECALLRANGE: Sets the range of a mob to recall back to it's bind location. Recall range is currently " + MBServerStatics.AI_RECALL_RANGE; + help += "\n POWERDIVISOR: Sets the Power Divisor for Mob AI.Setting this will give a 1/[amount] chance to use power on a player. Power Divisor is currently " + MBServerStatics.AI_POWER_DIVISOR; + help += "\n LOSEHATE: Sets the amount per second to reduce hate amount for player while they are idle. Hate Delimiter is currently " + MBServerStatics.PLAYER_HATE_DELIMITER; + help += "\n HATEMODCOMBAT: sets the modifier for Hate value for Combat. Hate Value is `Damage *[HATEMODCOMBAT]`.Hate Mod Combat is currently " + MBServerStatics.PLAYER_COMBAT_HATE_MODIFIER; + + return help; + } + + @Override + protected String _getUsageString() { + String usage = "' /setai `command` `amount` `"; + usage += '\n' + _getHelpString(); + return usage; + } + +} diff --git a/src/engine/devcmd/cmds/SetActivateMineCmd.java b/src/engine/devcmd/cmds/SetActivateMineCmd.java new file mode 100644 index 00000000..09972318 --- /dev/null +++ b/src/engine/devcmd/cmds/SetActivateMineCmd.java @@ -0,0 +1,67 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.BuildingManager; +import engine.objects.AbstractGameObject; +import engine.objects.Building; +import engine.objects.Mine; +import engine.objects.PlayerCharacter; + +/** + * + */ +public class SetActivateMineCmd extends AbstractDevCmd { + + public SetActivateMineCmd() { + super("mineactive"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + Building mineBuilding = BuildingManager.getBuilding(target.getObjectUUID()); + if (mineBuilding == null) + return; + + Mine mine = Mine.getMineFromTower(mineBuilding.getObjectUUID()); + if (mine == null) + return; + + String trigger = args[0]; + switch (trigger){ + case "true": + mine.handleStartMineWindow(); + Mine.setLastChange(System.currentTimeMillis()); + break; + case "false": + mine.handleEndMineWindow(); + Mine.setLastChange(System.currentTimeMillis()); + break; + default: + this.sendUsage(pcSender); + break; + + } + } + + @Override + protected String _getUsageString() { + return "' /mineactive true|false"; + } + + @Override + protected String _getHelpString() { + return "Temporarily add visual effects to Character"; + } + +} diff --git a/src/engine/devcmd/cmds/SetAdminRuneCmd.java b/src/engine/devcmd/cmds/SetAdminRuneCmd.java new file mode 100644 index 00000000..039a0b91 --- /dev/null +++ b/src/engine/devcmd/cmds/SetAdminRuneCmd.java @@ -0,0 +1,86 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.InterestManagement.InterestManager; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ChatManager; +import engine.objects.AbstractGameObject; +import engine.objects.CharacterRune; +import engine.objects.PlayerCharacter; + +/** + * + * @author Eighty + * + */ +public class SetAdminRuneCmd extends AbstractDevCmd { + + public SetAdminRuneCmd() { + super("setadminrune"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + int runeID = 0; + boolean add = true; + try { + runeID = Integer.parseInt(args[0]); + if (args.length > 1) + add = (args[1].toLowerCase().equals("false")) ? false : true; + } catch (NumberFormatException e) { + this.sendUsage(pcSender); + return; + } + if (runeID < 2901 || runeID > 2911) { + throwbackError(pcSender, + "Invalid setrune Command. must specify an ID between 2901 and 2911."); + return; + } + + if(!(target instanceof PlayerCharacter)) { + target = pcSender; + } + + boolean worked = false; + if (add) { + worked = CharacterRune.grantRune((PlayerCharacter) target, runeID); + if (worked) + ChatManager.chatSayInfo(pcSender, + "rune of ID " + runeID + " added"); + else + throwbackError(pcSender, "Failed to add the rune of type " + + runeID); + } else { + worked = CharacterRune.removeRune((PlayerCharacter) target, runeID); + if (worked) { + ChatManager.chatSayInfo(pcSender, + "rune of ID " + runeID + " removed"); + InterestManager.reloadCharacter(pcSender); + } else + throwbackError(pcSender, "Failed to remove the rune of type " + + runeID); + } + this.setTarget(target); //for logging + + } + + @Override + protected String _getUsageString() { + return "' /setrune runeID [true/false]'"; + } + + @Override + protected String _getHelpString() { + return "Grant or remove the rune with the specified runeID"; + } + +} diff --git a/src/engine/devcmd/cmds/SetBaneActiveCmd.java b/src/engine/devcmd/cmds/SetBaneActiveCmd.java new file mode 100644 index 00000000..1dabfc7d --- /dev/null +++ b/src/engine/devcmd/cmds/SetBaneActiveCmd.java @@ -0,0 +1,80 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ZoneManager; +import engine.objects.*; + +/** + * @author Eighty + * + */ +public class SetBaneActiveCmd extends AbstractDevCmd { + + public SetBaneActiveCmd() { + super("setbaneactive"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + if (words.length != 1) { + this.sendUsage(pc); + return; + } + + boolean setActive = false; + if (words[0].equals("true")) + setActive = true; + + Zone zone = ZoneManager.findSmallestZone(pc.getLoc()); + + if (zone == null) { + throwbackError(pc, "Unable to find the zone you're in."); + return; + } + + if (!zone.isPlayerCity()) { + throwbackError(pc, "This is not a player city."); + return; + } + + City city = City.getCity(zone.getPlayerCityUUID()); + if (city == null) { + throwbackError(pc, "Unable to find the city associated with this zone."); + return; + } + + Bane bane = city.getBane(); + if (bane == null) { + throwbackError(pc, "Could not find bane to modify."); + return; + } + + bane.getCity().protectionEnforced = !setActive; + + if (setActive) + throwbackInfo(pc, "The bane has been set active."); + else + throwbackInfo(pc, "The bane has been set inactive."); + } + + @Override + protected String _getHelpString() { + return "Sets a bane active or deactivates a bane."; + } + + @Override + protected String _getUsageString() { + return "'./setbaneactive true|false'"; + } + +} diff --git a/src/engine/devcmd/cmds/SetBaseClassCmd.java b/src/engine/devcmd/cmds/SetBaseClassCmd.java new file mode 100644 index 00000000..0c589c7a --- /dev/null +++ b/src/engine/devcmd/cmds/SetBaseClassCmd.java @@ -0,0 +1,64 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.InterestManagement.InterestManager; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ChatManager; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; + +public class SetBaseClassCmd extends AbstractDevCmd { + + public SetBaseClassCmd() { + super("setBaseClass"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + // Arg Count Check + if (words.length != 1) { + this.sendUsage(pc); + return; + } + + int classID = 0; + try { + classID = Integer.parseInt(words[0]); + } catch (Exception e) { + throwbackError(pc, + "Invalid setBaseClass Command. must specify an ID between 2500 and 2503."); + return; + } + if (classID < 2500 || classID > 2503) { + throwbackError(pc, + "Invalid setBaseClass Command. must specify an ID between 2500 and 2503."); + return; + } + pc.setBaseClass(classID); + this.setTarget(pc); //for logging + ChatManager.chatSayInfo(pc, + "BaseClass changed to " + classID); + InterestManager.reloadCharacter(pc); + + } + + @Override + protected String _getHelpString() { + return "Sets your character's BaseClass to 'id'"; + } + + @Override + protected String _getUsageString() { + return "' /setBaseClass id'"; + } + +} diff --git a/src/engine/devcmd/cmds/SetBuildingAltitudeCmd.java b/src/engine/devcmd/cmds/SetBuildingAltitudeCmd.java new file mode 100644 index 00000000..441aec15 --- /dev/null +++ b/src/engine/devcmd/cmds/SetBuildingAltitudeCmd.java @@ -0,0 +1,107 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.Enum.GameObjectType; +import engine.InterestManagement.WorldGrid; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.BuildingManager; +import engine.gameManager.DbManager; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractGameObject; +import engine.objects.Building; +import engine.objects.PlayerCharacter; + +public class SetBuildingAltitudeCmd extends AbstractDevCmd { + + public SetBuildingAltitudeCmd() { + super("setbuildingaltitude"); + this.addCmdString("buildingaltitude"); + } + + private static boolean UpdateBuildingAltitude(Building building, float altitude) { + + if (!DbManager.BuildingQueries.UPDATE_BUILDING_ALTITUDE(building.getObjectUUID(), altitude)) + return false; + + building.statAlt = altitude; + + if (building.parentZone != null) { + if (building.parentBuildingID != 0) { + Building parentBuilding = BuildingManager.getBuilding(building.parentBuildingID); + if (parentBuilding != null) { + building.setLoc(new Vector3fImmutable(building.statLat + building.parentZone.absX + parentBuilding.statLat, building.statAlt + building.parentZone.absY + parentBuilding.statAlt, building.statLon + building.parentZone.absZ + parentBuilding.statLon)); + } else { + building.setLoc(new Vector3fImmutable(building.statLat + building.parentZone.absX, building.statAlt + building.parentZone.absY, building.statLon + building.parentZone.absZ)); + + } + } else + building.setLoc(new Vector3fImmutable(building.statLat + building.parentZone.absX, building.statAlt + building.parentZone.absY, building.statLon + building.parentZone.absZ)); + + } + + return true; + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + // Arg Count Check + if (words.length != 1) { + this.sendUsage(pc); + return; + } + + if (target.getObjectType() != GameObjectType.Building){ + this.sendUsage(pc); + return; + } + + Building targetBuilding = (Building)target; + + + float altitude = 0; + try { + altitude = Float.parseFloat(words[0]); + + if (!UpdateBuildingAltitude(targetBuilding, targetBuilding.getStatAlt() + altitude)){ + this.throwbackError(pc, "Failed to update building altitude"); + return; + } + + + WorldGrid.updateObject(targetBuilding); + + this.setTarget(pc); //for logging + + // Update all surrounding clients. + + } catch (NumberFormatException e) { + this.throwbackError(pc, "Supplied data: " + words[0] + + " failed to parse to an Integer."); + } catch (Exception e) { + this.throwbackError(pc, + "An unknown exception occurred while attempting to setSlot to " + + words[0]); + } + } + + @Override + protected String _getHelpString() { + return "Sets slot position for an NPC to 'slot'"; + } + + @Override + protected String _getUsageString() { + return "' /changeslot slot'"; + } + +} diff --git a/src/engine/devcmd/cmds/SetForceRenameCityCmd.java b/src/engine/devcmd/cmds/SetForceRenameCityCmd.java new file mode 100644 index 00000000..6e592155 --- /dev/null +++ b/src/engine/devcmd/cmds/SetForceRenameCityCmd.java @@ -0,0 +1,54 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ZoneManager; +import engine.objects.AbstractGameObject; +import engine.objects.City; +import engine.objects.PlayerCharacter; +import engine.objects.Zone; + + +public class SetForceRenameCityCmd extends AbstractDevCmd { + + public SetForceRenameCityCmd() { + super("forcerename"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + Zone zone = ZoneManager.findSmallestZone(pc.getLoc()); + if (zone == null) + return; + boolean rename = words[0].equalsIgnoreCase("true") ? true : false; + if (zone.getPlayerCityUUID() == 0) + return; + City city = City.getCity(zone.getPlayerCityUUID()); + if (city == null) + return; + city.setForceRename(rename); + + } + + @Override + protected String _getHelpString() { + return "Temporarily Changes SubRace"; + } + + @Override + protected String _getUsageString() { + return "' /subrace mobBaseID"; + } + +} diff --git a/src/engine/devcmd/cmds/SetGuildCmd.java b/src/engine/devcmd/cmds/SetGuildCmd.java new file mode 100644 index 00000000..b46282ec --- /dev/null +++ b/src/engine/devcmd/cmds/SetGuildCmd.java @@ -0,0 +1,105 @@ +package engine.devcmd.cmds; + +import engine.Enum.GameObjectType; +import engine.InterestManagement.WorldGrid; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.DbManager; +import engine.objects.AbstractGameObject; +import engine.objects.Guild; +import engine.objects.NPC; +import engine.objects.PlayerCharacter; + +/** + * + * @author + * Dev command to set the guild of targeted npc. + * Argument is a valid guild UID + */ +public class SetGuildCmd extends AbstractDevCmd { + + + public SetGuildCmd() { + super("setguild"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + + NPC targetNPC; + Guild targetGuild; + + if(validateUserInput(pcSender, target, args) == false) { + this.sendUsage(pcSender); + return; + } + + // Valid arguments, attempt to set guild of NPC. + + targetNPC = getTargetAsNPC(pcSender); + targetGuild = Guild.getGuild(Integer.parseInt(args[0])); + + DbManager.NPCQueries.SET_PROPERTY(targetNPC, "npc_guildID", args[0]); + targetNPC.setGuild(targetGuild); + + // Refresh loaded game object + + WorldGrid.updateObject(targetNPC, pcSender); + + } + + @Override + protected String _getUsageString() { + return "' /setguild [UID]"; + } + + @Override + protected String _getHelpString() { + return "Assigns NPC to a given guild"; + } + + private boolean validateUserInput(PlayerCharacter pcSender, AbstractGameObject currTarget, String[] userInput) { + + Guild tempGuild; + + // Incorrect number of arguments + + if (userInput.length != 1) + return false; + + // No target + + if (currTarget == null) { + throwbackError(pcSender, "Requires an NPC be targeted"); + return false; + } + + // Target must be an NPC + + if (currTarget.getObjectType() != GameObjectType.NPC) { + throwbackError(pcSender, "Invalid object. Must be an NPC"); + return false; + } + + // Argument must parse as a int. + + try { + Integer.parseInt(userInput[0]); } + catch (NumberFormatException | NullPointerException e) { + return false; + } + + // Argument must return a valid guild + + tempGuild = Guild.getGuild(Integer.parseInt(userInput[0])); + + if (tempGuild == null) { + throwbackError(pcSender, "Invalid Guild UID"); + return false; + } + + + return true; +} + +} diff --git a/src/engine/devcmd/cmds/SetHealthCmd.java b/src/engine/devcmd/cmds/SetHealthCmd.java new file mode 100644 index 00000000..81ef3064 --- /dev/null +++ b/src/engine/devcmd/cmds/SetHealthCmd.java @@ -0,0 +1,67 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.Enum.DispatchChannel; +import engine.devcmd.AbstractDevCmd; +import engine.net.DispatchMessage; +import engine.net.client.msg.TargetedActionMsg; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; + +public class SetHealthCmd extends AbstractDevCmd { + + public SetHealthCmd() { + super("setHealth"); + this.addCmdString("health"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + // Arg Count Check + if (words.length != 1) { + this.sendUsage(pc); + return; + } + + float amount = 0.0f; + try { + amount = Float.parseFloat(words[0]); + pc.modifyHealth(amount, pc, false); + this.setTarget(pc); //for logging + + // Update all surrounding clients. + TargetedActionMsg cmm = new TargetedActionMsg(pc); + DispatchMessage.dispatchMsgToInterestArea(pc, cmm, DispatchChannel.SECONDARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + + } catch (NumberFormatException e) { + this.throwbackError(pc, "Supplied data: " + words[0] + + " failed to parse to a Float."); + } catch (Exception e) { + this.throwbackError(pc, + "An unknown exception occurred while attempting to setHealth to " + + words[0]); + } + } + + @Override + protected String _getHelpString() { + return "Sets your character's health to 'amount'"; + } + + @Override + protected String _getUsageString() { + return "' /setHealth amount'"; + } + +} diff --git a/src/engine/devcmd/cmds/SetInvulCmd.java b/src/engine/devcmd/cmds/SetInvulCmd.java new file mode 100644 index 00000000..a9f952a7 --- /dev/null +++ b/src/engine/devcmd/cmds/SetInvulCmd.java @@ -0,0 +1,91 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + + +import engine.Enum; +import engine.Enum.ProtectionState; +import engine.devcmd.AbstractDevCmd; +import engine.objects.AbstractGameObject; +import engine.objects.Building; +import engine.objects.City; +import engine.objects.PlayerCharacter; + +public class SetInvulCmd extends AbstractDevCmd { + + public SetInvulCmd() { + super("setinvul"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] words, + AbstractGameObject target) { + if (pcSender == null) return; + + if (words.length != 1) { + this.sendUsage(pcSender); + return; + } + + boolean invul; + switch (words[0].toLowerCase()) { + case "true": + invul = true; + break; + case "false": + invul = false; + break; + default: + this.sendUsage(pcSender); + return; + } + + if (target == null || !(target instanceof Building)) { + throwbackError(pcSender, "No building targeted"); + return; + } + + Building b = (Building) target; + + // if strucutre is a TOL then we're modifying the protection + // status of the entire city + + if ( (b.getBlueprint() != null) && + (b.getBlueprint().getBuildingGroup().equals(Enum.BuildingGroup.TOL))) { + + City city; + + city = b.getCity(); + city.protectionEnforced = !city.protectionEnforced; + throwbackInfo(pcSender, "City protection contracts enforced: " + city.protectionEnforced); + return; + } + + if (invul) { + + b.setProtectionState(ProtectionState.PROTECTED); + throwbackInfo(pcSender, "The targetted building is now invulnerable."); + return; + } + b.setProtectionState(ProtectionState.NONE); + throwbackInfo(pcSender, "The targetted building is no longer invulernable."); + } + + @Override + protected String _getUsageString() { + return "'./setInvul true|false'"; + } + + @Override + protected String _getHelpString() { + return "Turns invulernability on or off for building"; + } + +} diff --git a/src/engine/devcmd/cmds/SetLevelCmd.java b/src/engine/devcmd/cmds/SetLevelCmd.java new file mode 100644 index 00000000..aabedc40 --- /dev/null +++ b/src/engine/devcmd/cmds/SetLevelCmd.java @@ -0,0 +1,73 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.InterestManagement.InterestManager; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ChatManager; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; + +public class SetLevelCmd extends AbstractDevCmd { + + public SetLevelCmd() { + super("setLevel"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + // Arg Count Check + if (words.length != 1) { + this.sendUsage(pc); + return; + } + + PlayerCharacter tar; + if (target != null) { + if (target instanceof PlayerCharacter) + tar = (PlayerCharacter) target; + else + tar = pc; + } else + tar = pc; + + int level = 0; + try { + level = Integer.parseInt(words[0]); + } catch (NumberFormatException e) { + this.sendUsage(pc); + return; + } + if (level < 1 || level > 75) { + this.sendHelp(pc); + return; + } + + if (level > 10 && pc.getPromotionClass() == null) + level = 10; + + tar.setLevel((short) level); + this.setTarget(tar); //for logging + ChatManager.chatSayInfo(pc, tar.getFirstName() + " level changed to " + level); + InterestManager.reloadCharacter(tar); + } + + @Override + protected String _getHelpString() { + return "Sets your character's level to 'amount'. 'amount' must be between 1-75"; + } + + @Override + protected String _getUsageString() { + return "' /setLevel amount'"; + } + +} diff --git a/src/engine/devcmd/cmds/SetMaintCmd.java b/src/engine/devcmd/cmds/SetMaintCmd.java new file mode 100644 index 00000000..e81832e4 --- /dev/null +++ b/src/engine/devcmd/cmds/SetMaintCmd.java @@ -0,0 +1,61 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.Enum; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ChatManager; +import engine.gameManager.MaintenanceManager; +import engine.objects.AbstractGameObject; +import engine.objects.Building; +import engine.objects.PlayerCharacter; + +import java.time.LocalDateTime; + +public class SetMaintCmd extends AbstractDevCmd { + + public SetMaintCmd() { + super("setMaint"); + this.addCmdString("setmaint"); + } + + @Override + protected void _doCmd(PlayerCharacter player, String[] words, + AbstractGameObject target) { + + if (!target.getObjectType().equals(Enum.GameObjectType.Building)) { + ChatManager.chatSayInfo(player, "Target is not a valid building"); + return; + } + + Building targetBuilding = (Building)target; + + if (targetBuilding.getProtectionState().equals(Enum.ProtectionState.NPC)) { + ChatManager.chatSayInfo(player, "Target is not a valid building"); + return; + } + + MaintenanceManager.setMaintDateTime(targetBuilding, LocalDateTime.now().minusDays(1).withHour(13).withMinute(0).withSecond(0).withNano(0)); + ChatManager.chatSayInfo(player, "Maint will run for UUID: " + targetBuilding.getObjectUUID()); + } + + + @Override + protected String _getHelpString() { + return "Sets the Rank of either the targets object or the object specified by ID."; + } + + @Override + protected String _getUsageString() { + return "' /setrank ID rank' || ' /setrank rank' || ' /rank ID rank' || ' /rank rank'"; + } + +} diff --git a/src/engine/devcmd/cmds/SetManaCmd.java b/src/engine/devcmd/cmds/SetManaCmd.java new file mode 100644 index 00000000..47e059a3 --- /dev/null +++ b/src/engine/devcmd/cmds/SetManaCmd.java @@ -0,0 +1,56 @@ +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.net.DispatchMessage; +import engine.net.client.msg.TargetedActionMsg; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; + +public class SetManaCmd extends AbstractDevCmd { + + public SetManaCmd() { + super("setMana"); + this.addCmdString("mana"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + // Arg Count Check + if (words.length != 1) { + this.sendUsage(pc); + return; + } + + float amount = 0.0f; + try { + amount = Float.parseFloat(words[0]); + pc.setMana(amount, pc); + this.setTarget(pc); //for logging + + //Update all surrounding clients. - NOT for Mana? + TargetedActionMsg cmm = new TargetedActionMsg(pc); + DispatchMessage.dispatchMsgToInterestArea(pc, cmm, engine.Enum.DispatchChannel.SECONDARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + + } catch (NumberFormatException e) { + this.throwbackError(pc, "Supplied data: " + words[0] + + " failed to parse to a Float."); + } catch (Exception e) { + this.throwbackError(pc, + "An unknown exception occurred while attempting to setMana to " + + words[0]); + } + } + + @Override + protected String _getHelpString() { + return "Sets your character's Mana to 'amount'"; + } + + @Override + protected String _getUsageString() { + return "' /setMana amount'"; + } + +} diff --git a/src/engine/devcmd/cmds/SetMineExpansion.java b/src/engine/devcmd/cmds/SetMineExpansion.java new file mode 100644 index 00000000..0bffe902 --- /dev/null +++ b/src/engine/devcmd/cmds/SetMineExpansion.java @@ -0,0 +1,87 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + + +import engine.Enum.GameObjectType; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.BuildingManager; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.objects.AbstractGameObject; +import engine.objects.Building; +import engine.objects.Mine; +import engine.objects.PlayerCharacter; + +/** + * + * @author Eighty + * + */ +public class SetMineExpansion extends AbstractDevCmd { + + public SetMineExpansion() { + super("setexpansion"); + this.addCmdString("setexpansion"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + + + if (target.getObjectType() != GameObjectType.Building) + return; + Building mineBuilding = BuildingManager.getBuilding(target.getObjectUUID()); + if (mineBuilding == null) + return; + Mine mine = Mine.getMineFromTower(mineBuilding.getObjectUUID()); + if (mine == null) + return; + int bit = 2; + switch (args[0].toUpperCase()) { + case "ON": + + bit |= mine.getFlags(); + if (!DbManager.MineQueries.SET_FLAGS(mine, bit)) + return; + mine.setFlags(bit); + ChatManager.chatSystemInfo(pcSender, mine.getZoneName() + "'s " + mine.getMineType().name + " is now an expansion mine."); + Mine.setLastChange(System.currentTimeMillis()); + break; + + case "OFF": + bit &= ~mine.getFlags(); + if (!DbManager.MineQueries.SET_FLAGS(mine, bit)) + return; + mine.setFlags(bit); + ChatManager.chatSystemInfo(pcSender, mine.getZoneName() + "'s " + mine.getMineType().name + " is no longer an expansion mine."); + Mine.setLastChange(System.currentTimeMillis()); + break; + + } + + + + + + } + + @Override + protected String _getUsageString() { + return "' /setmineexpansion on|off"; + } + + @Override + protected String _getHelpString() { + return "enables or disables an expansion type for a mine."; + } + +} diff --git a/src/engine/devcmd/cmds/SetMineTypeCmd.java b/src/engine/devcmd/cmds/SetMineTypeCmd.java new file mode 100644 index 00000000..c932923e --- /dev/null +++ b/src/engine/devcmd/cmds/SetMineTypeCmd.java @@ -0,0 +1,65 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + + +import engine.Enum.GameObjectType; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.BuildingManager; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.objects.*; + +/** + * + * @author Eighty + * + */ +public class SetMineTypeCmd extends AbstractDevCmd { + + public SetMineTypeCmd() { + super("setminetype"); + this.addCmdString("setminetype"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + + MineProduction mineType = MineProduction.valueOf(args[0].toUpperCase()); + if (mineType == null) + return; + + if (target.getObjectType() != GameObjectType.Building) + return; + Building mineBuilding = BuildingManager.getBuilding(target.getObjectUUID()); + if (mineBuilding == null) + return; + Mine mine = Mine.getMineFromTower(mineBuilding.getObjectUUID()); + if (mine == null) + return; + if (!DbManager.MineQueries.CHANGE_TYPE(mine, mineType)) + return; + mine.setMineType(mineType.name()); + ChatManager.chatSystemInfo(pcSender, "The mine in " + mine.getZoneName() + " is now a(n) " + mine.getMineType().name); + Mine.setLastChange(System.currentTimeMillis()); + } + + @Override + protected String _getUsageString() { + return "' /setminetype gold|ore|magic|lumber"; + } + + @Override + protected String _getHelpString() { + return "Changes the mine type of the current mine."; + } + +} diff --git a/src/engine/devcmd/cmds/SetNPCSlotCmd.java b/src/engine/devcmd/cmds/SetNPCSlotCmd.java new file mode 100644 index 00000000..5b31eb3d --- /dev/null +++ b/src/engine/devcmd/cmds/SetNPCSlotCmd.java @@ -0,0 +1,80 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.Enum.GameObjectType; +import engine.InterestManagement.WorldGrid; +import engine.devcmd.AbstractDevCmd; +import engine.objects.AbstractGameObject; +import engine.objects.NPC; +import engine.objects.PlayerCharacter; + +public class SetNPCSlotCmd extends AbstractDevCmd { + + public SetNPCSlotCmd() { + super("updateNPCSlot"); + this.addCmdString("changeslot"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + // Arg Count Check + if (words.length != 1) { + this.sendUsage(pc); + return; + } + + if (target.getObjectType() != GameObjectType.NPC){ + this.sendUsage(pc); + return; + } + + NPC npc = (NPC)target; + + + int slot = 0; + try { + slot = Integer.parseInt(words[0]); + + if (!NPC.UpdateSlot(npc, slot)){ + this.throwbackError(pc, "Failed to Update Slot"); + return; + } + + npc.setParentZone(npc.getParentZone()); + WorldGrid.updateObject(npc); + + this.setTarget(pc); //for logging + + // Update all surrounding clients. + + } catch (NumberFormatException e) { + this.throwbackError(pc, "Supplied data: " + words[0] + + " failed to parse to an Integer."); + } catch (Exception e) { + this.throwbackError(pc, + "An unknown exception occurred while attempting to setSlot to " + + words[0]); + } + } + + @Override + protected String _getHelpString() { + return "Sets slot position for an NPC to 'slot'"; + } + + @Override + protected String _getUsageString() { + return "' /changeslot slot'"; + } + +} diff --git a/src/engine/devcmd/cmds/SetNpcEquipSetCmd.java b/src/engine/devcmd/cmds/SetNpcEquipSetCmd.java new file mode 100644 index 00000000..d4b2dd67 --- /dev/null +++ b/src/engine/devcmd/cmds/SetNpcEquipSetCmd.java @@ -0,0 +1,138 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.Enum.GameObjectType; +import engine.InterestManagement.WorldGrid; +import engine.devcmd.AbstractDevCmd; +import engine.objects.AbstractGameObject; +import engine.objects.MobBase; +import engine.objects.NPC; +import engine.objects.PlayerCharacter; + +public class SetNpcEquipSetCmd extends AbstractDevCmd { + + public static int lastEquipSetID = 0; + public SetNpcEquipSetCmd() { + super("setEquipSet"); + this.addCmdString("equipset"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + // Arg Count Check + if (words.length != 1) { + this.sendUsage(pc); + return; + } + + if (target.getObjectType() != GameObjectType.NPC){ + this.sendUsage(pc); + return; + } + + NPC npc = (NPC)target; + + if (words[0].equalsIgnoreCase("next")){ + + if (SetNpcEquipSetCmd.lastEquipSetID >= 1222) + SetNpcEquipSetCmd.lastEquipSetID = 1; + else + SetNpcEquipSetCmd.lastEquipSetID++; + + boolean complete = false; + + while (complete == false){ + complete = NPC.UpdateEquipSetID(npc, SetNpcEquipSetCmd.lastEquipSetID); + + if (!complete){ + SetNpcEquipSetCmd.lastEquipSetID++; + if (SetNpcEquipSetCmd.lastEquipSetID >= 1222) + SetNpcEquipSetCmd.lastEquipSetID = 1; + } + } + + if (complete){ + npc.equip = MobBase.loadEquipmentSet(npc.getEquipmentSetID()); + WorldGrid.updateObject(npc); + this.throwbackInfo(pc, "Equipment Set Changed to " + SetNpcEquipSetCmd.lastEquipSetID ); + } + + + + return; + } + + if (words[0].equalsIgnoreCase("last")){ + + if (SetNpcEquipSetCmd.lastEquipSetID <= 1) + SetNpcEquipSetCmd.lastEquipSetID = 1222; + else + SetNpcEquipSetCmd.lastEquipSetID--; + + boolean complete = false; + + while (complete == false){ + complete = NPC.UpdateEquipSetID(npc, SetNpcEquipSetCmd.lastEquipSetID); + + if (!complete){ + SetNpcEquipSetCmd.lastEquipSetID--; + if (SetNpcEquipSetCmd.lastEquipSetID <= 1) + SetNpcEquipSetCmd.lastEquipSetID = 1222; + } + + } + + if (complete){ + this.throwbackInfo(pc, "Equipment Set Changed to " + SetNpcEquipSetCmd.lastEquipSetID ); + npc.equip = MobBase.loadEquipmentSet(npc.getEquipmentSetID()); + WorldGrid.updateObject(npc); + } + + + + return; + } + + int equipSetID = 0; + + try{ + equipSetID = Integer.parseInt(words[0]); + }catch(Exception e){ + this.throwbackError(pc, e.getMessage()); + } + + if (!NPC.UpdateEquipSetID(npc, equipSetID)){ + this.throwbackError(pc, "Unable to find Equipset for ID " + equipSetID ); + return; + } + + SetNpcEquipSetCmd.lastEquipSetID = equipSetID; + npc.equip = MobBase.loadEquipmentSet(npc.getEquipmentSetID()); + WorldGrid.updateObject(npc); + + this.throwbackInfo(pc, "Equipment Set Changed to " + equipSetID ); + + + } + + @Override + protected String _getHelpString() { + return "Sets slot position for an NPC to 'slot'"; + } + + @Override + protected String _getUsageString() { + return "' /changeslot slot'"; + } + +} diff --git a/src/engine/devcmd/cmds/SetNpcMobbaseCmd.java b/src/engine/devcmd/cmds/SetNpcMobbaseCmd.java new file mode 100644 index 00000000..07002761 --- /dev/null +++ b/src/engine/devcmd/cmds/SetNpcMobbaseCmd.java @@ -0,0 +1,67 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.Enum.GameObjectType; +import engine.InterestManagement.WorldGrid; +import engine.devcmd.AbstractDevCmd; +import engine.objects.AbstractGameObject; +import engine.objects.MobBase; +import engine.objects.NPC; +import engine.objects.PlayerCharacter; + +public class SetNpcMobbaseCmd extends AbstractDevCmd { + + public SetNpcMobbaseCmd() { + super("setmobbase"); + this.addCmdString("npcmobbase"); + } + + @Override + protected void _doCmd(PlayerCharacter player, String[] words, + AbstractGameObject target) { + + // Arg Count Check + if (words.length != 1) { + this.sendUsage(player); + return; + } + + if (target.getObjectType() != GameObjectType.NPC){ + this.sendUsage(player); + return; + } + + NPC npc = (NPC)target; + + int mobBaseID = Integer.parseInt(words[0]); + + if (MobBase.getMobBase(mobBaseID) == null){ + this.throwbackError(player, "Cannot find Mobbase for ID " + mobBaseID); + return; + } + NPC.UpdateRaceID(npc, mobBaseID); + + WorldGrid.updateObject(npc); + + } + + @Override + protected String _getHelpString() { + return "Sets mobbase override for an NPC"; + } + + @Override + protected String _getUsageString() { + return "' /setmobbase mobBaseID'"; + } + +} diff --git a/src/engine/devcmd/cmds/SetNpcNameCmd.java b/src/engine/devcmd/cmds/SetNpcNameCmd.java new file mode 100644 index 00000000..2e05ae2c --- /dev/null +++ b/src/engine/devcmd/cmds/SetNpcNameCmd.java @@ -0,0 +1,62 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.Enum.GameObjectType; +import engine.InterestManagement.WorldGrid; +import engine.devcmd.AbstractDevCmd; +import engine.objects.AbstractGameObject; +import engine.objects.NPC; +import engine.objects.PlayerCharacter; + +public class SetNpcNameCmd extends AbstractDevCmd { + + public static int lastEquipSetID = 0; + public SetNpcNameCmd() { + super("setNPCName"); + this.addCmdString("npcname"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + // Arg Count Check + if (words.length != 1) { + this.sendUsage(pc); + return; + } + + if (target.getObjectType() != GameObjectType.NPC){ + this.sendUsage(pc); + return; + } + + NPC npc = (NPC)target; + + String name = words[0]; + + NPC.UpdateName(npc, name); + + WorldGrid.updateObject(npc); + + } + + @Override + protected String _getHelpString() { + return "Sets slot position for an NPC to 'slot'"; + } + + @Override + protected String _getUsageString() { + return "' /changeslot slot'"; + } + +} diff --git a/src/engine/devcmd/cmds/SetOwnerCmd.java b/src/engine/devcmd/cmds/SetOwnerCmd.java new file mode 100644 index 00000000..f07241bf --- /dev/null +++ b/src/engine/devcmd/cmds/SetOwnerCmd.java @@ -0,0 +1,118 @@ +package engine.devcmd.cmds; + +import engine.Enum.DbObjectType; +import engine.Enum.GameObjectType; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.DbManager; +import engine.objects.*; + + +/** + * + * @author + * Dev command to set the owner of targeted building. + * Argument is a valid guild UID + */ +public class SetOwnerCmd extends AbstractDevCmd { + + Building _targetBuilding = null; + DbObjectType _newOwnerType; + AbstractCharacter outOwner; + + public SetOwnerCmd() { + super("setowner"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + + if(validateUserInput(pcSender, target, args) == false) { + this.sendUsage(pcSender); + return; + } + + // Valid arguments, attempt to set owner of Building. + + _targetBuilding = getTargetAsBuilding(pcSender); + + // if it's a tol change ownership of the city + + if (_targetBuilding.getBlueprint() != null && + _targetBuilding.getBlueprint().getBuildingGroup().equals(engine.Enum.BuildingGroup.TOL)) { + + City city = _targetBuilding.getCity(); + + if (city != null) { + city.claim(outOwner); + return; } + } + _targetBuilding.setOwner(outOwner); + + DbManager.BuildingQueries.SET_PROPERTY(_targetBuilding, "ownerUUID", args[0]); + + _targetBuilding.refreshGuild(); + + } + + @Override + protected String _getUsageString() { + return "' /setowner [UID]"; + } + + @Override + protected String _getHelpString() { + return "Assigns new owner to building"; + } + + private boolean validateUserInput(PlayerCharacter pcSender, AbstractGameObject currTarget, String[] userInput) { + + // Incorrect number of arguments + + if (userInput.length != 1) + return false; + + // No target + + if (currTarget == null) { + throwbackError(pcSender, "Requires a Building to be targeted"); + return false; + } + + // Target must be an Building + + if (currTarget.getObjectType() != GameObjectType.Building) { + throwbackError(pcSender, "Invalid target. Must be a Building"); + return false; + } + + // Argument must parse to a long. + + try { + Long.parseLong(userInput[0]); } + catch (NumberFormatException | NullPointerException e) { + return false; + } + + // Argument must return a valid NPC or PlayerCharacter + + _newOwnerType = DbManager.BuildingQueries.GET_UID_ENUM(Long.parseLong(userInput[0])); + + switch (_newOwnerType) { + case NPC: + outOwner = (AbstractCharacter)DbManager.getObject(GameObjectType.NPC, Integer.parseInt(userInput[0])); + break; + case CHARACTER: + outOwner = (AbstractCharacter)DbManager.getObject(GameObjectType.PlayerCharacter, Integer.parseInt(userInput[0])); + break; + } + + if (outOwner == null) { + throwbackError(pcSender, "Invalid Owner UID"); + return false; + } + + return true; +} + +} diff --git a/src/engine/devcmd/cmds/SetPromotionClassCmd.java b/src/engine/devcmd/cmds/SetPromotionClassCmd.java new file mode 100644 index 00000000..88239ab2 --- /dev/null +++ b/src/engine/devcmd/cmds/SetPromotionClassCmd.java @@ -0,0 +1,72 @@ +package engine.devcmd.cmds; + +import engine.Enum.DispatchChannel; +import engine.InterestManagement.InterestManager; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ChatManager; +import engine.net.DispatchMessage; +import engine.net.client.msg.ApplyRuneMsg; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; +import engine.objects.PromotionClass; +import engine.server.MBServerStatics; + +public class SetPromotionClassCmd extends AbstractDevCmd { + + public SetPromotionClassCmd() { + super("setPromotionClass"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + // Arg Count Check + if (words.length != 1) { + this.sendUsage(pc); + return; + } + + int classID = 0; + try { + classID = Integer.parseInt(words[0]); + } catch (Exception e) { + throwbackError(pc, + "Invalid setPromotionClass Command. must specify an ID between 2504 and 2526."); + return; + } + if (classID < 2504 || classID > 2526 || classID == 2522) { + throwbackError(pc, + "Invalid setPromotionClass Command. must specify an ID between 2504 and 2526."); + return; + } + pc.setPromotionClass(classID); + ChatManager.chatSayInfo(pc, + "PromotionClass changed to " + classID); + InterestManager.reloadCharacter(pc); + this.setTarget(pc); //for logging + + + // recalculate all bonuses/formulas/skills/powers + pc.recalculate(); + + // send the rune application to the clients + + PromotionClass promo = pc.getPromotionClass(); + if (promo != null) { + ApplyRuneMsg arm = new ApplyRuneMsg(pc.getObjectType().ordinal(), pc.getObjectUUID(), promo.getObjectUUID(), promo.getObjectType().ordinal(), promo.getObjectUUID(), true); + DispatchMessage.dispatchMsgToInterestArea(pc, arm, DispatchChannel.SECONDARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + } + + } + + @Override + protected String _getHelpString() { + return "Sets your character's PromotionClass to 'ID'"; + } + + @Override + protected String _getUsageString() { + return "' /setPromotionClass id'"; + } + +} diff --git a/src/engine/devcmd/cmds/SetRankCmd.java b/src/engine/devcmd/cmds/SetRankCmd.java new file mode 100644 index 00000000..6e7a43a4 --- /dev/null +++ b/src/engine/devcmd/cmds/SetRankCmd.java @@ -0,0 +1,142 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.InterestManagement.WorldGrid; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.BuildingManager; +import engine.gameManager.ChatManager; +import engine.objects.*; + +public class SetRankCmd extends AbstractDevCmd { + + public SetRankCmd() { + super("setRank"); + this.addCmdString("setrank"); + this.addCmdString("rank"); + } + + @Override + protected void _doCmd(PlayerCharacter player, String[] words, + AbstractGameObject target) { + + int targetRank; + int uuid = 0; + + if (words.length == 2) { + try { + uuid = Integer.parseInt(words[0]); + targetRank = Integer.parseInt(words[1]); + } catch (NumberFormatException e) { + this.sendUsage(player); + return; // NaN + } + } else if (words.length == 1) { + try { + targetRank = Integer.parseInt(words[0]); + } catch (NumberFormatException e) { + this.sendUsage(player); + return; // NaN + } + } else { + this.sendUsage(player); + return; + } + + if (target != null){ + switch(target.getObjectType()){ + case Building: + Building targetBuilding = (Building)target; + Blueprint blueprint = targetBuilding.getBlueprint(); + + if (blueprint == null) { + targetBuilding.setRank(targetRank); + ChatManager.chatSayInfo(player, "Building ranked without blueprint" + targetBuilding.getObjectUUID()); + return; + } + + if (targetRank > blueprint.getMaxRank()) { + throwbackError(player, "Attempt to set Invalid Rank" + targetBuilding.getObjectUUID()); + return; + } + + // Set the current targetRank + int lastMeshID = targetBuilding.getMeshUUID(); + targetBuilding.setRank(targetRank); + + ChatManager.chatSayInfo(player, "Rank set for building with ID " + targetBuilding.getObjectUUID() + " to rank " + targetRank); + break; + case NPC: + NPC toRank = (NPC)target; + toRank.setRank(targetRank * 10); + toRank.setUpgradeDateTime(null); + WorldGrid.updateObject(toRank); + break; + case Mob: + Mob toRankCaptain = (Mob)target; + if (toRankCaptain.getContract() != null){ + toRankCaptain.setRank(targetRank * 10); + Mob.setUpgradeDateTime(toRankCaptain, null); + WorldGrid.updateObject(toRankCaptain); + } + + break; + } + + }else{ + Building targetBuilding = null; + if (uuid != 0) + targetBuilding = BuildingManager.getBuilding(uuid); + + if (targetBuilding == null) { + throwbackError(player, "Unable to find building."); + return; + } + + Blueprint blueprint = targetBuilding.getBlueprint(); + + if (blueprint == null) { + throwbackError(player, "Attempt to rank building without blueprint" + targetBuilding.getObjectUUID()); + return; + } + + if (targetRank > blueprint.getMaxRank()) { + throwbackError(player, "Attempt to set Invalid Rank" + targetBuilding.getObjectUUID()); + return; + } + + // Set the current targetRank + int lastMeshID = targetBuilding.getMeshUUID(); + targetBuilding.setRank(targetRank); + + if (lastMeshID != targetBuilding.getMeshUUID()) + targetBuilding.refresh(true); + else + targetBuilding.refresh(false); + + ChatManager.chatSayInfo(player, "Rank set for building with ID " + targetBuilding.getObjectUUID() + " to rank " + targetRank); + } + + + + } + + @Override + protected String _getHelpString() { + return "Sets the Rank of either the targets object or the object specified by ID."; + } + + @Override + protected String _getUsageString() { + return "' /setrank ID rank' || ' /setrank rank' || ' /rank ID rank' || ' /rank rank'"; + } + +} diff --git a/src/engine/devcmd/cmds/SetRateCmd.java b/src/engine/devcmd/cmds/SetRateCmd.java new file mode 100644 index 00000000..b73c0d92 --- /dev/null +++ b/src/engine/devcmd/cmds/SetRateCmd.java @@ -0,0 +1,98 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; + + +/** + * + * @author Murray + * + */ +public class SetRateCmd extends AbstractDevCmd { + + public SetRateCmd() { + super("setrate"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] args, AbstractGameObject target) { + + if (args.length != 2) { + this.sendUsage(pc); + return; + } + + float mod = 0f; + + try { + mod = Float.parseFloat(args[1]); + } catch (NumberFormatException e) { + throwbackError(pc, "Supplied data failed to parse to Float."); + return; + } + + + if (args[0].equals("exp")){ + + MBServerStatics.EXP_RATE_MOD = mod; + throwbackInfo(pc, "Experience Rate set to: " + mod); + + } else if (args[0].equals("gold")){ + + MBServerStatics.GOLD_RATE_MOD = mod; + throwbackInfo(pc, "Gold Rate set to: " + mod); + + } else if (args[0].equals("drop")){ + + MBServerStatics.DROP_RATE_MOD = mod; + throwbackInfo(pc, "Drop Multiplier Rate set to: " + mod); + + } else if (args[0].equals("hotexp")){ + + MBServerStatics.HOT_EXP_RATE_MOD = mod; + throwbackInfo(pc, "HOTZONE Experience Rate set to: " + mod); + + } else if (args[0].equals("hotgold")){ + + MBServerStatics.HOT_GOLD_RATE_MOD = mod; + throwbackInfo(pc, "HOTZONE Gold Rate set to: " + mod); + + } else if (args[0].equals("hotdrop")){ + + MBServerStatics.HOT_DROP_RATE_MOD = mod; + throwbackInfo(pc, "HOTZONE Drop Multiplier Rate set to: " + mod); + + } else if (args[0].equals("production")){ + + MBServerStatics.PRODUCTION_TIME_MULTIPLIER = mod; + throwbackInfo(pc, "Production Time Multiplier Rate set to: " + mod); + + } else { + this.sendUsage(pc); + } + + } + + @Override + protected String _getUsageString() { + return "' /setrate {exp|gold|drop|hotexp|hotgold|hotdrop} rate'"; + } + + @Override + protected String _getHelpString() { + return "Sets the rates for exp, gold or drops. Accepts a float, defaults to 1.0"; + } + +} \ No newline at end of file diff --git a/src/engine/devcmd/cmds/SetRuneCmd.java b/src/engine/devcmd/cmds/SetRuneCmd.java new file mode 100644 index 00000000..99bd3b2d --- /dev/null +++ b/src/engine/devcmd/cmds/SetRuneCmd.java @@ -0,0 +1,80 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.InterestManagement.InterestManager; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ChatManager; +import engine.objects.AbstractGameObject; +import engine.objects.CharacterRune; +import engine.objects.PlayerCharacter; + +/** + * + * @author Eighty + * + */ +public class SetRuneCmd extends AbstractDevCmd { + + public SetRuneCmd() { + super("setRune"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + int runeID = 0; + boolean add = true; + try { + runeID = Integer.parseInt(args[0]); + if (args.length > 1) + add = (args[1].toLowerCase().equals("false")) ? false : true; + } catch (NumberFormatException e) { + this.sendUsage(pcSender); + return; + } + if (runeID < 3001 || runeID > 3049) { + throwbackError(pcSender, + "Invalid setrune Command. must specify an ID between 3001 and 3048."); + return; + } + boolean worked = false; + if (add) { + worked = CharacterRune.grantRune(pcSender, runeID); + if (worked) + ChatManager.chatSayInfo(pcSender, + "rune of ID " + runeID + " added"); + else + throwbackError(pcSender, "Failed to add the rune of type " + + runeID); + } else { + worked = CharacterRune.removeRune(pcSender, runeID); + if (worked) { + ChatManager.chatSayInfo(pcSender, + "rune of ID " + runeID + " removed"); + InterestManager.reloadCharacter(pcSender); + } else + throwbackError(pcSender, "Failed to remove the rune of type " + + runeID); + } + this.setTarget(pcSender); //for logging + } + + @Override + protected String _getUsageString() { + return "' /setrune runeID [true/false]'"; + } + + @Override + protected String _getHelpString() { + return "Grant or remove the rune with the specified runeID"; + } + +} diff --git a/src/engine/devcmd/cmds/SetStaminaCmd.java b/src/engine/devcmd/cmds/SetStaminaCmd.java new file mode 100644 index 00000000..e0cde8e7 --- /dev/null +++ b/src/engine/devcmd/cmds/SetStaminaCmd.java @@ -0,0 +1,68 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.Enum.DispatchChannel; +import engine.devcmd.AbstractDevCmd; +import engine.net.DispatchMessage; +import engine.net.client.msg.TargetedActionMsg; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; + + +public class SetStaminaCmd extends AbstractDevCmd { + + public SetStaminaCmd() { + super("setStamina"); + this.addCmdString("stamina"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + // Arg Count Check + if (words.length != 1) { + this.sendUsage(pc); + return; + } + + float amount = 0.0f; + try { + amount = Float.parseFloat(words[0]); + pc.setStamina(amount, pc); + this.setTarget(pc); //for logging + + // Update all surrounding clients. + TargetedActionMsg cmm = new TargetedActionMsg(pc); + DispatchMessage.dispatchMsgToInterestArea(pc, cmm, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + + } catch (NumberFormatException e) { + this.throwbackError(pc, "Supplied data: " + words[0] + + " failed to parse to a Float."); + } catch (Exception e) { + this.throwbackError(pc, + "An unknown exception occurred while attempting to setStamina to " + + words[0]); + } + } + + @Override + protected String _getHelpString() { + return "Sets your character's Stamina to 'amount'"; + } + + @Override + protected String _getUsageString() { + return "' /setStamina amount'"; + } + +} diff --git a/src/engine/devcmd/cmds/SetSubRaceCmd.java b/src/engine/devcmd/cmds/SetSubRaceCmd.java new file mode 100644 index 00000000..78227780 --- /dev/null +++ b/src/engine/devcmd/cmds/SetSubRaceCmd.java @@ -0,0 +1,209 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + + +import engine.Enum.GameObjectType; +import engine.devcmd.AbstractDevCmd; +import engine.net.DispatchMessage; +import engine.net.client.msg.ApplyBuildingEffectMsg; +import engine.net.client.msg.UpdateCharOrMobMessage; +import engine.objects.*; + +public class SetSubRaceCmd extends AbstractDevCmd { + + public SetSubRaceCmd() { + super("setSubRace"); + this.addCmdString("subrace"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + if (target instanceof AbstractCharacter){ + + if (words[0].equals("race")){ + if (target.getObjectType() != GameObjectType.PlayerCharacter) + return; + PlayerCharacter player = (PlayerCharacter)target; + int raceID = Integer.parseInt(words[1]); + player.setSubRaceID(raceID); + if (raceID == 0) + raceID = player.getRaceID(); + UpdateCharOrMobMessage ucm = new UpdateCharOrMobMessage(player, 1,raceID); + DispatchMessage.sendToAllInRange(player, ucm); + return; + } + if (words[0].equals("all")){ + for (int i = 15999; i< 16337;i++){ + ApplyBuildingEffectMsg applyBuildingEffectMsg = new ApplyBuildingEffectMsg(4, 0, target.getObjectType().ordinal(), target.getObjectUUID(), i); + DispatchMessage.sendToAllInRange((AbstractWorldObject) target, applyBuildingEffectMsg); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + }else{ + ApplyBuildingEffectMsg applyBuildingEffectMsg = new ApplyBuildingEffectMsg(4, 0, target.getObjectType().ordinal(), target.getObjectUUID(), Integer.parseInt(words[0])); + DispatchMessage.sendToAllInRange((AbstractWorldObject) target, applyBuildingEffectMsg); + } + + return; + } + + Building building = (Building)target; + + building.removeAllVisualEffects(); + building.addEffectBit(1<(), new ArrayList<>(), null, true); + // pc.getClientConnection().sendMsg(updateInventoryMsg); + + //pc.getCharItemManager().updateInventory(); + + + + // Mob mob = (Mob)target; + // + // if (mob == null) + // return; + // + // MobLoot mobLoot = new MobLoot(mob, ItemBase.getItemBase(Integer.parseInt(words[0])), false); + // + // mob.getCharItemManager().addItemToInventory(mobLoot); + + + + + // if (target.getObjectType() != GameObjectType.Building) + // return; + // + // Building warehouseBuilding = (Building)target; + // + // if (Warehouse.warehouseByBuildingUUID.get(warehouseBuilding.getObjectUUID()) == null) + // return; + // + // Warehouse warehouse = Warehouse.warehouseByBuildingUUID.get(warehouseBuilding.getObjectUUID()); + // + // for (int ibID: Warehouse.getMaxResources().keySet()){ + // ItemBase ib = ItemBase.getItemBase(ibID); + // warehouse.depositFromMine(null, ib, Warehouse.getMaxResources().get(ibID)); + // } + + + // int raceID = Integer.parseInt(words[0]); + // + // UpdateCharOrMobMessage ucm = new UpdateCharOrMobMessage(pc, raceID); + // + // pc.getClientConnection().sendMsg(ucm); + + // LoadCharacterMsg lcm = new LoadCharacterMsg((AbstractCharacter)null,false); + // try { + // DispatchMessage.sendToAllInRange(pc, lcm); + // } catch (MsgSendException e) { + // // TODO Auto-generated catch block + // e.printStackTrace(); + // } + // ModifyHealthMsg mhm =new ModifyHealthMsg(pc, pc, -50f, 0f, 0f, 0, null, 9999, 0); + // mhm.setOmitFromChat(1); + // pc.getClientConnection().sendMsg(mhm); + // + // int temp = 0; + // boolean start = false; + // + // for (EffectsBase eb: EffectsBase.getAllEffectsBase()){ + // + // + // + // if (!pc.getClientConnection().isConnected()){ + // Logger.info("", "PLAYER DC!" + eb.getIDString()); + // break; + // } + // + // eb = PowersManager.getEffectByIDString("WLR-018A"); + // + // + // NoTimeJob ntj = new NoTimeJob(pc, "NONE", eb, 40); + // pc.addEffect(String.valueOf(eb.getUUID()), 1000, ntj, eb, 40); + // eb.sendEffectNoPower(ntj, 1000, pc.getClientConnection()); + // + // ThreadUtils.sleep(500); + // pc.clearEffects(); + // + // //WorldServer.updateObject((AbstractWorldObject)target, pc); + // this.throwbackInfo(pc, eb.getIDString()); + // break; + // //ThreadUtils.sleep(500); + // + // } + + + + + + + + + + // for (EffectsBase eb : EffectsBase.getAllEffectsBase()){ + // if (eb.getToken() == 0) + // continue; + // int token = eb.getToken(); + // ApplyEffectMsg pum = new ApplyEffectMsg(); + // pum.setEffectID(token); + // pum.setSourceType(pc.getObjectType().ordinal()); + // pum.setSourceID(pc.getObjectUUID()); + // pum.setTargetType(pc.getObjectType().ordinal()); + // pum.setTargetID(pc.getObjectUUID()); + // pum.setNumTrains(40); + // pum.setDuration(-1); + //// pum.setDuration((pb.isChant()) ? (int)pb.getChantDuration() : ab.getDurationInSeconds(trains)); + // pum.setPowerUsedID(0); + // // pum.setPowerUsedName("Inflict Poison"); + // this.throwbackInfo(pc, eb.getName() + "Token = " + eb.getToken()); + // pc.getClientConnection().sendMsg(pum); + // ThreadUtils.sleep(200); + // } + // + + // UpdateObjectMsg uom = new UpdateObjectMsg(pc, 4); + // try { + // Location.sendToAllInRange(pc, uom); + // } catch (MsgSendException e) { + // // TODO Auto-generated catch block + // e.printStackTrace(); + // } + + + + + } + + @Override + protected String _getHelpString() { + return "Temporarily Changes SubRace"; + } + + @Override + protected String _getUsageString() { + return "' /subrace mobBaseID"; + } + +} diff --git a/src/engine/devcmd/cmds/ShowOffsetCmd.java b/src/engine/devcmd/cmds/ShowOffsetCmd.java new file mode 100644 index 00000000..45fc2ae6 --- /dev/null +++ b/src/engine/devcmd/cmds/ShowOffsetCmd.java @@ -0,0 +1,57 @@ +package engine.devcmd.cmds; + +import engine.Enum.GameObjectType; +import engine.devcmd.AbstractDevCmd; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractGameObject; +import engine.objects.Building; +import engine.objects.PlayerCharacter; + +public class ShowOffsetCmd extends AbstractDevCmd { + + public ShowOffsetCmd() { + super("showoffset"); + } + + // AbstractDevCmd Overridden methods + + @Override + protected void _doCmd(PlayerCharacter pc, String[] args, + AbstractGameObject target) { + + + Building targetBuilding; + String outString; + Vector3fImmutable offset; + + String newline = "\r\n "; + targetBuilding = (Building)target; + + if (targetBuilding.getObjectType() != GameObjectType.Building) { + throwbackInfo(pc, "showgate: target must be an Building"); + return; + } + + offset = pc.getLoc().subtract(targetBuilding.getLoc()); + + outString = "Location: " + pc.getLoc().x + "x " + pc.getLoc().z + 'y'; + outString += newline; + outString += "Offset: " + offset.x + "x " + offset.y + 'y'; + throwbackInfo(pc, outString); + } + + @Override + protected String _getHelpString() { + return "Shows offset to current building"; + } + + @Override + protected String _getUsageString() { + + + return "Shows offset to current building"; + } + + + +} diff --git a/src/engine/devcmd/cmds/SlotNpcCmd.java b/src/engine/devcmd/cmds/SlotNpcCmd.java new file mode 100644 index 00000000..264c3ced --- /dev/null +++ b/src/engine/devcmd/cmds/SlotNpcCmd.java @@ -0,0 +1,155 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.Enum.BuildingGroup; +import engine.Enum.GameObjectType; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.objects.AbstractGameObject; +import engine.objects.Contract; +import engine.objects.PlayerCharacter; +import engine.util.StringUtils; +import org.pmw.tinylog.Logger; + +/** + * Summary: Game designer utility command to add or + * remove building slot access for contracts + */ + +public class SlotNpcCmd extends AbstractDevCmd { + + public SlotNpcCmd() { + super("slotnpc"); + } + + + // AbstractDevCmd Overridden methods + + @Override + protected void _doCmd(PlayerCharacter pc, String[] args, + AbstractGameObject target) { + + Contract contractObject; + BuildingGroup buildingGroup; + + long slotBitvalue; + String outString; + + if (target.getObjectType() != GameObjectType.NPC) { + throwbackInfo(pc, "NpcSlot: target must be an NPC"); + return; + } + + // Get the contract from the npc + contractObject = getTargetAsNPC(pc).getContract(); + + // User requests list of current groups. + + if (args[0].toUpperCase().equals("LIST")) { + + outString = "Current: " + contractObject.getAllowedBuildings(); + + throwbackInfo(pc, outString); + return; + } + + if(validateUserInput(args) == false) { + this.sendUsage(pc); + return; + } + + // Extract the building group flag from user input + + buildingGroup = BuildingGroup.valueOf(args[0].toUpperCase()); + + switch (args[1].toUpperCase()) { + + case "ON": + contractObject.getAllowedBuildings().add(buildingGroup); + + if (!DbManager.ContractQueries.updateAllowedBuildings(contractObject, contractObject.getAllowedBuildings().toLong())){ + Logger.error( "Failed to update Database for Contract Allowed buildings"); + ChatManager.chatSystemError(pc, "Failed to update Database for Contract Allowed buildings. " + + "Contact A CCR, oh wait, you are a CCR. You're Fubared."); + return; + } + + throwbackInfo(pc, "SlotNpc " + buildingGroup.name() + " added to npc"); + break; + case "OFF": + contractObject.getAllowedBuildings().remove(buildingGroup); + if (!DbManager.ContractQueries.updateAllowedBuildings(contractObject, contractObject.getAllowedBuildings().toLong())){ + Logger.error( "Failed to update Database for Contract Allowed buildings"); + ChatManager.chatSystemError(pc, "Failed to update Database for Contract Allowed buildings. " + + "Contact A CCR, oh wait, you are a CCR. You're Fubared."); + return; + } + + throwbackInfo(pc, "SlotNpc " + buildingGroup.name() + " removed from npc"); + break; + } + + } + + @Override + protected String _getHelpString() { + return "Sets a building slot on a targeted npc"; + } + + @Override + protected String _getUsageString() { + String usage = "/npcslot [BuildingType] on-off \n"; + + for (BuildingGroup group:BuildingGroup.values()) { + usage += group.name() + ' '; + } + + usage = StringUtils.wordWrap(usage, 30); + + return usage; + } + + // Class methods + + private static boolean validateUserInput(String[] userInput) { + + int stringIndex; + BuildingGroup testGroup; + + testGroup = BuildingGroup.FORGE; + + String commandSet = "onoff"; + + // incorrect number of arguments test + + if (userInput.length > 2) + return false; + + + // Test of toggle argument + + stringIndex = commandSet.indexOf(userInput[1].toLowerCase()); + + if (stringIndex == -1) + return false; + + // Validate we have a corrent building group name + + for (BuildingGroup group:BuildingGroup.values()) { + if (group.name().equals(userInput[0].toUpperCase())) + return true; + } + return false; + } + +} diff --git a/src/engine/devcmd/cmds/SplatMobCmd.java b/src/engine/devcmd/cmds/SplatMobCmd.java new file mode 100644 index 00000000..4d660692 --- /dev/null +++ b/src/engine/devcmd/cmds/SplatMobCmd.java @@ -0,0 +1,166 @@ +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ZoneManager; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractGameObject; +import engine.objects.Mob; +import engine.objects.PlayerCharacter; +import engine.objects.Zone; + +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * @author + * Summary: Game designer utility command to create multiple + * mobiles of a given UUID within a supplied range + */ + +public class SplatMobCmd extends AbstractDevCmd { + + // Instance variables + + private int _mobileUUID; + private int _mobileCount; + private float _targetRange; + private Vector3fImmutable _currentLocation; + + // Concurrency support + + private ReadWriteLock lock = new ReentrantReadWriteLock(); + + // Constructor + + public SplatMobCmd() { + super("splatmob"); + } + + + // AbstractDevCmd Overridden methods + + @Override + protected void _doCmd(PlayerCharacter pc, String[] args, + AbstractGameObject target) { + + // Member variables + + Vector3fImmutable mobileLocation; + Mob mobile; + Zone serverZone; + + // Concurrency write lock due to instance variable usage + + lock.writeLock().lock(); + + try { + + // Validate user input + + if(validateUserInput(args) == false) { + this.sendUsage(pc); + return; + } + + // Parse user input + + parseUserInput(args); + + // Arguments have been validated and parsed at this point + // Begin creating mobiles + + _currentLocation = pc.getLoc(); + serverZone = ZoneManager.findSmallestZone(_currentLocation); + + for(int i=0;i<_mobileCount;i++) { + + mobile = Mob.createMob(_mobileUUID, + Vector3fImmutable.getRandomPointInCircle(_currentLocation, _targetRange), + null, true, serverZone,null,0); + + if (mobile != null) { + mobile.updateDatabase(); + } + } + + } // End Try Block + + // Release Reentrant lock + + finally { + lock.writeLock().unlock(); + } + } + + @Override + protected String _getHelpString() { + return "Spawns multiple mobiles with a given range"; + } + + @Override + protected String _getUsageString() { + return "/splatmob UUID [Count <= 100] [range <= 1200]"; + } + + // Class methods + + private static boolean validateUserInput(String[] userInput) { + + // incorrect number of arguments test + + if (userInput.length != 3) + return false; + + // Test for UUID conversion to int + + try { + Integer.parseInt(userInput[0]); } + catch (NumberFormatException | NullPointerException e) { + return false; + } + + + // Test for Number of Mobs conversion to int + + try { + Integer.parseInt(userInput[1]); } + catch (NumberFormatException | NullPointerException e) { + return false; + } + + // Test if range argument can convert to a float + + try { + Float.parseFloat(userInput[2]); } + catch (NumberFormatException | NullPointerException e) { + return false; + } + + return true; + } + + private void parseUserInput(String[] userInput) { + + // Clear previous values + + _mobileUUID = 0; + _mobileCount = 0; + _targetRange = 0f; + + // Parse first argument into mobile UID. + + _mobileUUID = Integer.parseInt(userInput[0]); + + // Parse second argument into mobile count. Cap at 100 mobs. + + _mobileCount = Integer.parseInt(userInput[1]); + _mobileCount = Math.min(_mobileCount, 100); + + // Parse third argument into range. Cap at 200 units. + + _targetRange = Float.parseFloat(userInput[2]); + _targetRange = Math.min(_targetRange, 1200f); + + } + +} \ No newline at end of file diff --git a/src/engine/devcmd/cmds/SqlDebugCmd.java b/src/engine/devcmd/cmds/SqlDebugCmd.java new file mode 100644 index 00000000..18087692 --- /dev/null +++ b/src/engine/devcmd/cmds/SqlDebugCmd.java @@ -0,0 +1,86 @@ +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; + +/** + * @author + * Summary: Devcmd to toggle logging of mysql statements + * + */ + +public class SqlDebugCmd extends AbstractDevCmd { + + // Instance variables + + + public SqlDebugCmd() { + super("sqldebug"); + } + + + // AbstractDevCmd Overridden methods + + @Override + protected void _doCmd(PlayerCharacter pc, String[] args, + AbstractGameObject target) { + + Boolean debugState = false; + + if(validateUserInput(args) == false) { + this.sendUsage(pc); + return; + } + + // Arguments have been validated use argument to set debug state + + switch (args[0]) { + case "on": + debugState = true; + break; + case "off": + debugState = false; + break; + default: + break; + } + + MBServerStatics.DB_ENABLE_QUERY_OUTPUT = debugState; + + // Send results to user + throwbackInfo(pc, "SQL debug state: " + debugState.toString()); + } + + @Override + protected String _getHelpString() { + return "Toggles sending SQL statements to log"; + } + + @Override + protected String _getUsageString() { + return "/sqldebug on|off"; + } + + // Class methods + + private static boolean validateUserInput(String[] userInput) { + + int stringIndex; + String commandSet = "onoff"; + + // incorrect number of arguments test + + if (userInput.length != 1) + return false; + + // Validate arguments + + stringIndex = commandSet.indexOf(userInput[0].toLowerCase()); + + return stringIndex != -1; + } + + +} diff --git a/src/engine/devcmd/cmds/SummonCmd.java b/src/engine/devcmd/cmds/SummonCmd.java new file mode 100644 index 00000000..c0a1c7d1 --- /dev/null +++ b/src/engine/devcmd/cmds/SummonCmd.java @@ -0,0 +1,114 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.SessionManager; +import engine.gameManager.ZoneManager; +import engine.math.Vector3fImmutable; +import engine.net.client.msg.RecvSummonsRequestMsg; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; +import engine.objects.Zone; + +public class SummonCmd extends AbstractDevCmd { + + public SummonCmd() { + super("summon"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] args, + AbstractGameObject target) { + // Arg Count Check + if (args.length != 1) { + this.sendUsage(pc); + return; + } + PlayerCharacter pcToSummon = null; + + + if (args[0].equalsIgnoreCase("all")){ + for (PlayerCharacter toSummon: SessionManager.getAllActivePlayerCharacters()){ + Zone zone = ZoneManager.findSmallestZone(pc.getLoc()); + String location = "Somewhere"; + if (zone != null) + location = zone.getName(); + RecvSummonsRequestMsg rsrm = new RecvSummonsRequestMsg(pc.getObjectType().ordinal(), pc.getObjectUUID(), pc.getFirstName(), + location, false); + toSummon.getClientConnection().sendMsg(rsrm); + + } + return; + } + // 1-9 numeric digits, must be playerID + if (args[0].matches("\\d{1,9}?")) { + try { + int playerID = Integer.parseInt(args[0]); + pcToSummon = SessionManager + .getPlayerCharacterByID(playerID); + + if (pcToSummon == null) { + this.throwbackError(pc, "Character not found by ID: " + + playerID); + return; + } + } catch (NumberFormatException e) { + this.throwbackError(pc, "Supplied ID: '" + args[0] + + "' failed to parse to an INT"); + return; + + } catch (Exception e) { + this.throwbackError(pc, + "An unknown exception occurred while attempting to summon '" + + args[0] + "'by ID"); + return; + } + + } else { // player name + try { + pcToSummon = SessionManager + .getPlayerCharacterByLowerCaseName(args[0]); + if (pcToSummon == null) { + this.throwbackError(pc, "Character not found by name: " + + args[0]); + return; + } + } catch (Exception e) { + this.throwbackError(pc, + "An unknown exception occurred while attempting to summon '" + + args[0] + "'by name"); + return; + } + } + this.setTarget(pcToSummon); //for logging + + Vector3fImmutable loc = pc.getLoc(); + pcToSummon.teleport(loc); + + this.throwbackInfo(pc, "Player " + pcToSummon.getCombinedName() + + " has been summoned to your location."); + this.throwbackInfo(pcToSummon, + "You have been transported to another location."); + } + + @Override + protected String _getHelpString() { + return "Summons 'character' TO your current position. Can summon by character's first name or by the character's characterID."; + + } + + @Override + protected String _getUsageString() { + return "' /summon characterName' || ' /summon characterID'"; + } + +} diff --git a/src/engine/devcmd/cmds/SysMsgCmd.java b/src/engine/devcmd/cmds/SysMsgCmd.java new file mode 100644 index 00000000..4004655e --- /dev/null +++ b/src/engine/devcmd/cmds/SysMsgCmd.java @@ -0,0 +1,67 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ChatManager; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; + +/** + * + */ + +public class SysMsgCmd extends AbstractDevCmd { + + public SysMsgCmd() { + super("sysmsg"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] args, + AbstractGameObject target) { + if (args.length != 1 || args[0].isEmpty()) { + this.sendUsage(pcSender); + return; + } + String text = "[System Admin Message]: " + args[0]; + ChatManager.chatSystemChannel(text); + } + + /** + * This function is called by the DevCmdManager. Override to avoid splitting + * argString into String array, since sysmsg displays full String as + * message, then calls the subclass specific _doCmd method. + */ + + @Override + public void doCmd(PlayerCharacter pcSender, String argString, + AbstractGameObject target) { + String[] args = new String[1]; + args[0] = argString; + + if (pcSender == null) { + return; + } + + this._doCmd(pcSender, args, target); + } + + @Override + protected String _getUsageString() { + return "' /sysmsg message'"; + } + + @Override + protected String _getHelpString() { + return "Send system message in chat window to all players"; + } + +} diff --git a/src/engine/devcmd/cmds/TeleportModeCmd.java b/src/engine/devcmd/cmds/TeleportModeCmd.java new file mode 100644 index 00000000..630f3824 --- /dev/null +++ b/src/engine/devcmd/cmds/TeleportModeCmd.java @@ -0,0 +1,56 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.objects.AbstractGameObject; +import engine.objects.PlayerCharacter; + +public class TeleportModeCmd extends AbstractDevCmd { + + public TeleportModeCmd() { + super("teleportMode"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + boolean newTeleportMode = false; + if (words.length == 0) { // toggle + newTeleportMode = !pc.isTeleportMode(); + + } else if (words[0].equalsIgnoreCase("on")) { + newTeleportMode = true; + + } else if (words[0].equalsIgnoreCase("off")) { + newTeleportMode = false; + + } else { + this.sendUsage(pc); + return; + } + pc.setTeleportMode(newTeleportMode); + this.setTarget(pc); //for logging + String output = (newTeleportMode ? "on" : "off"); + + throwbackInfo(pc, "Teleport mode is now '" + output + "'."); + } + + @Override + protected String _getHelpString() { + return "'on' enables teleport mode, 'off' disables. No arguments to the /teleportMode command will toggle the setting."; + } + + @Override + protected String _getUsageString() { + return "' /teleportMode [on | off]'"; + } + +} diff --git a/src/engine/devcmd/cmds/UnloadFurnitureCmd.java b/src/engine/devcmd/cmds/UnloadFurnitureCmd.java new file mode 100644 index 00000000..4197a800 --- /dev/null +++ b/src/engine/devcmd/cmds/UnloadFurnitureCmd.java @@ -0,0 +1,102 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.Enum.GameObjectType; +import engine.devcmd.AbstractDevCmd; +import engine.net.client.msg.LoadStructureMsg; +import engine.net.client.msg.MoveToPointMsg; +import engine.net.client.msg.UnloadObjectsMsg; +import engine.objects.AbstractGameObject; +import engine.objects.AbstractWorldObject; +import engine.objects.Building; +import engine.objects.PlayerCharacter; + +/** + * @author + * + */ +public class UnloadFurnitureCmd extends AbstractDevCmd { + + public UnloadFurnitureCmd() { + super("furniture"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + if (target.getObjectType() != GameObjectType.Building){ + this.throwbackError(pc, "Must be targeting a building to load/unload furniture."); + return; + } + if (words[0].equalsIgnoreCase("unload")){ + + UnloadObjectsMsg uom = new UnloadObjectsMsg(); + for (AbstractWorldObject awo: pc.getLoadedStaticObjects()){ + if (awo.getObjectType() != GameObjectType.Building) + continue; + + Building awoBuilding = (Building)awo; + + if (!awoBuilding.isFurniture) + continue; + + if (awoBuilding.parentBuildingID != target.getObjectUUID()) + continue; + + MoveToPointMsg msg = new MoveToPointMsg(awoBuilding); + pc.getClientConnection().sendMsg(msg); + + uom.addObject(awoBuilding); + + + } + + pc.getClientConnection().sendMsg(uom); + + }else if (words[0].equalsIgnoreCase("load")){ + LoadStructureMsg lsm = new LoadStructureMsg(); + + for (AbstractWorldObject awo: pc.getLoadedStaticObjects()){ + if (awo.getObjectType() != GameObjectType.Building) + continue; + + Building awoBuilding = (Building)awo; + + if (!awoBuilding.isFurniture) + continue; + + if (awoBuilding.parentBuildingID != target.getObjectUUID()) + continue; + + lsm.addObject(awoBuilding); + + + } + + pc.getClientConnection().sendMsg(lsm); + + } + } + + @Override + protected String _getHelpString() { + String help = "Enchants an item with a prefix and suffix"; + return help; + } + + @Override + protected String _getUsageString() { + String usage = "' /enchant clear/Enchant1 Enchant2 Enchant3 ...'"; + return usage; + } + +} diff --git a/src/engine/devcmd/cmds/ZoneInfoCmd.java b/src/engine/devcmd/cmds/ZoneInfoCmd.java new file mode 100644 index 00000000..0cc7c2f2 --- /dev/null +++ b/src/engine/devcmd/cmds/ZoneInfoCmd.java @@ -0,0 +1,150 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ZoneManager; +import engine.objects.AbstractGameObject; +import engine.objects.City; +import engine.objects.PlayerCharacter; +import engine.objects.Zone; +import engine.util.StringUtils; + +import java.util.ArrayList; + + +/** + * @author + * + */ +public class ZoneInfoCmd extends AbstractDevCmd { + + public ZoneInfoCmd() { + super("zoneinfo"); + } + + @Override + protected void _doCmd(PlayerCharacter player, String[] words, + AbstractGameObject target) { + // Arg Count Check + Zone zone = null; + + if (player == null) { + throwbackError(player, "Unable to find the pc making the request."); + return; + } + + try { + int targetID = Integer.parseInt(words[0]); + + //try get zone by objectUUID + zone = ZoneManager.getZoneByUUID(targetID); + + //that failed, try get by zoneID + if (zone == null) + zone = ZoneManager.getZoneByZoneID(targetID); + + //no zone found, so fail + if (zone == null) { + throwbackError(player, "Zone with ID " + targetID + "not found"); + return; + } + } catch (Exception e) { + zone = ZoneManager.findSmallestZone(player.getLoc()); + } + + if (zone == null) { + throwbackError(player, "Zone not found"); + return; + } + + String newline = "\r\n "; + + + int objectUUID = zone.getObjectUUID(); + String output; + + output = "Target Information:" + newline; + output += StringUtils.addWS("UUID: " + objectUUID, 20); + output += newline; + output += "name: " + zone.getName(); + output += newline; + output += "loadNum: " + zone.getLoadNum(); + if (zone.getParent() != null) { + output += StringUtils.addWS(", parent: " + zone.getParent().getObjectUUID(), 30); + output += "Parentabs: x: " + zone.getParent().getAbsX() + ", y: " + zone.getParent().getAbsY() + ", z: " + zone.getParent().getAbsZ(); + + } else + output += StringUtils.addWS(", parent: none", 30); + output += newline; + output += "absLoc: x: " + zone.getAbsX() + ", y: " + zone.getAbsY() + ", z: " + zone.getAbsZ(); + output += newline; + output += "offset: x: " + zone.getXCoord() + ", y: " + zone.getYCoord() + ", z: " + zone.getZCoord(); + output += newline; + output += "radius: x: " + zone.getBounds().getHalfExtents().x + ", z: " + zone.getBounds().getHalfExtents().y; + output += newline; + + if (zone.getHeightMap() != null){ + output += "HeightMap ID: " + zone.getHeightMap().getHeightMapID(); + output += newline; + output += "Bucket Width X : " + zone.getHeightMap().getBucketWidthX(); + output += newline; + output += "Bucket Width Y : " + zone.getHeightMap().getBucketWidthY(); + + } + output += "radius: x: " + zone.getBounds().getHalfExtents().x + ", z: " + zone.getBounds().getHalfExtents().y; + output += newline; + // output += "minLvl = " + zone.getMinLvl() + " | maxLvl = " + zone.getMaxLvl(); + output += newline; + output += "Sea Level = " +zone.getSeaLevel(); + output += newline; + output += "World Altitude = " + zone.getWorldAltitude(); + throwbackInfo(player, output); + + City city = ZoneManager.getCityAtLocation(player.getLoc()); + + output += newline; + output += (city == null)? "None" : city.getParent().getName(); + + if (city != null ) { + + if (city.isLocationOnCityGrid(player.getLoc())) + output += " (Grid)"; + else if (city.isLocationOnCityZone(player.getLoc())) + output += " (Zone)"; + else if (city.isLocationWithinSiegeBounds(player.getLoc())) + output += " (Siege)"; + } else { + output = "children:"; + + ArrayList nodes = zone.getNodes(); + + if (nodes.isEmpty()) + output += " none"; + + for (Zone child : nodes) { + output += newline; + output += child.getName() + " (" + child.getLoadNum() + ')'; + } + } + throwbackInfo(player, output); + } + + @Override + protected String _getHelpString() { + return "Gets information on an Object."; + } + + @Override + protected String _getUsageString() { + return "' /info targetID'"; + } + +} diff --git a/src/engine/devcmd/cmds/convertLoc.java b/src/engine/devcmd/cmds/convertLoc.java new file mode 100644 index 00000000..8190c6ee --- /dev/null +++ b/src/engine/devcmd/cmds/convertLoc.java @@ -0,0 +1,62 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.devcmd.cmds; + +import engine.Enum.GameObjectType; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.ChatManager; +import engine.gameManager.ZoneManager; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractGameObject; +import engine.objects.Building; +import engine.objects.PlayerCharacter; + +public class convertLoc extends AbstractDevCmd { + + public convertLoc() { + super("convertLoc"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + + if (target == null){ + Vector3fImmutable convertLoc = ZoneManager.findSmallestZone(pc.getLoc()).getLoc().subtract(pc.getLoc()); + ChatManager.chatSystemInfo(pc, Vector3fImmutable.toString(convertLoc)); + return; + } + + + if (target.getObjectType() != GameObjectType.Building) + return; + Building toConvert = (Building)target; + Vector3fImmutable convertedLoc = ZoneManager.convertWorldToLocal(toConvert, pc.getLoc()); + ChatManager.chatSystemInfo(pc, Vector3fImmutable.toString(convertedLoc)); + + + + + } + + @Override + protected String _getHelpString() { + return "Temporarily Changes SubRace"; + } + + @Override + protected String _getUsageString() { + return "' /setBuildingCollidables add/remove 'add creates a collision line.' needs 4 integers. startX, endX, startY, endY"; + + } + +} diff --git a/src/engine/devcmd/cmds/setOpenDateCmd.java b/src/engine/devcmd/cmds/setOpenDateCmd.java new file mode 100644 index 00000000..a74e9857 --- /dev/null +++ b/src/engine/devcmd/cmds/setOpenDateCmd.java @@ -0,0 +1,94 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.Enum.BuildingGroup; +import engine.Enum.GameObjectType; +import engine.devcmd.AbstractDevCmd; +import engine.gameManager.BuildingManager; +import engine.objects.*; + +public class setOpenDateCmd extends AbstractDevCmd { + + public setOpenDateCmd() { + super("minedate"); + } + + @Override + protected void _doCmd(PlayerCharacter pcSender, String[] words, + AbstractGameObject target) { + + + if (words[0].equalsIgnoreCase("list")){ + for (int buildingID : Mine.towerMap.keySet()){ + Building building = BuildingManager.getBuildingFromCache(buildingID); + if (building == null){ + this.throwbackError(pcSender, "null building for ID " + buildingID); + continue; + } + + Zone zone = building.getParentZone(); + + Zone parentZone = zone.getParent(); + + Mine mine = Mine.towerMap.get(buildingID); + this.throwbackInfo(pcSender, "Mine UUID : " + mine.getObjectUUID() + " Mine Type: " + zone.getName() + " Zone : " + parentZone.getName() + + " Open Date : " + mine.openDate); + } + + } + if (target == null){ + this.throwbackError(pcSender, "null target"); + return; + } + if (target.getObjectType().equals(GameObjectType.Building) == false){ + this.throwbackError(pcSender, "target must be object type building"); + return; + } + + Building building = (Building)target; + if (building.getBlueprint() == null){ + this.throwbackError(pcSender, "null blueprint"); + return; + } + + if (building.getBlueprint().getBuildingGroup().equals(BuildingGroup.MINE) == false){ + + this.throwbackError(pcSender, "target not mine"); + return; + } + + Mine mine = Mine.getMineFromTower(building.getObjectUUID()); + + if (mine == null){ + this.throwbackError(pcSender, "null mine"); + return; + } + int days = Integer.parseInt(words[0]); + int hours = Integer.parseInt(words[1]); + + mine.openDate = mine.openDate.plusDays(days).plusHours(hours); + + this.throwbackInfo(pcSender, "Mine Open Date Changed to " + mine.openDate.toString()); + } + + + @Override + protected String _getUsageString() { + return "' /bounds'"; + } + + @Override + protected String _getHelpString() { + return "Audits all the mobs in a zone."; + + } + +} diff --git a/src/engine/exception/FactoryBuildException.java b/src/engine/exception/FactoryBuildException.java new file mode 100644 index 00000000..e0293036 --- /dev/null +++ b/src/engine/exception/FactoryBuildException.java @@ -0,0 +1,32 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.exception; + +public class FactoryBuildException extends MBServerException { + + private static final long serialVersionUID = 2331961867931593523L; + + public FactoryBuildException() { + super(); + } + + public FactoryBuildException(String arg0, Throwable arg1) { + super(arg0, arg1); + } + + public FactoryBuildException(String arg0) { + super(arg0); + } + + public FactoryBuildException(Throwable arg0) { + super(arg0); + } + +} diff --git a/src/engine/exception/MBServerException.java b/src/engine/exception/MBServerException.java new file mode 100644 index 00000000..3ede5360 --- /dev/null +++ b/src/engine/exception/MBServerException.java @@ -0,0 +1,38 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.exception; + +import org.pmw.tinylog.Logger; + + +public abstract class MBServerException extends Exception { + private static final long serialVersionUID = 3878845236025977250L; + + public MBServerException() { + super(); + } + + public MBServerException(String arg0, Throwable arg1) { + super(arg0, arg1); + } + + public MBServerException(String arg0) { + super(arg0); + } + + public MBServerException(Throwable arg0) { + super(arg0); + } + + public void logException(String origin) { + Logger.error(origin, this.getClass().getSimpleName() + "(1): " + this.getMessage()); + + } +} diff --git a/src/engine/exception/MsgSendException.java b/src/engine/exception/MsgSendException.java new file mode 100644 index 00000000..b84fcd33 --- /dev/null +++ b/src/engine/exception/MsgSendException.java @@ -0,0 +1,31 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + package engine.exception; + +public class MsgSendException extends MBServerException { + private static final long serialVersionUID = 6927044139998382254L; + + public MsgSendException() { + super(); + } + + public MsgSendException(String arg0, Throwable arg1) { + super(arg0, arg1); + } + + public MsgSendException(String arg0) { + super(arg0); + } + + public MsgSendException(Throwable arg0) { + super(arg0); + } + +} diff --git a/src/engine/exception/SerializationException.java b/src/engine/exception/SerializationException.java new file mode 100644 index 00000000..0dd0066c --- /dev/null +++ b/src/engine/exception/SerializationException.java @@ -0,0 +1,31 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + package engine.exception; + +public class SerializationException extends MBServerException { + private static final long serialVersionUID = 6927044139998382254L; + + public SerializationException() { + super(); + } + + public SerializationException(String arg0, Throwable arg1) { + super(arg0, arg1); + } + + public SerializationException(String arg0) { + super(arg0); + } + + public SerializationException(Throwable arg0) { + super(arg0); + } + +} diff --git a/src/engine/gameManager/BuildingManager.java b/src/engine/gameManager/BuildingManager.java new file mode 100644 index 00000000..79511e56 --- /dev/null +++ b/src/engine/gameManager/BuildingManager.java @@ -0,0 +1,636 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.gameManager; + +import engine.Enum; +import engine.Enum.BuildingGroup; +import engine.Enum.GameObjectType; +import engine.InterestManagement.WorldGrid; +import engine.job.JobContainer; +import engine.job.JobScheduler; +import engine.jobs.UpgradeBuildingJob; +import engine.math.Bounds; +import engine.math.Vector3fImmutable; +import engine.net.client.msg.ErrorPopupMsg; +import engine.objects.*; +import org.pmw.tinylog.Logger; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.concurrent.ThreadLocalRandom; + +public enum BuildingManager { + + BUILDINGMANAGER; + + public static boolean playerCanManage(PlayerCharacter player, Building building) { + + if (player == null) + return false; + + if (building == null) + return false; + + + if (building.getRank() == -1) + return false; + + if (IsOwner(building, player)) + return true; + + //individual friend. + if (building.getFriends().get(player.getObjectUUID()) != null) + return true; + + //Admin's can access stuff + + if (player.isCSR()) + return true; + + //Guild stuff + + + if (building.getGuild() != null && building.getGuild().isGuildLeader(player.getObjectUUID())) + return true; + + if (building.getFriends().get(player.getGuild().getObjectUUID()) != null + && building.getFriends().get(player.getGuild().getObjectUUID()).getFriendType() == 8) + return true; + + if (building.getFriends().get(player.getGuild().getObjectUUID()) != null + && building.getFriends().get(player.getGuild().getObjectUUID()).getFriendType() == 9 + && GuildStatusController.isInnerCouncil(player.getGuildStatus())) + return true; + + if (Guild.sameGuild(building.getGuild(), player.getGuild()) && GuildStatusController.isInnerCouncil(player.getGuildStatus())) + return true; + + if (Guild.sameGuild(building.getGuild(), player.getGuild()) && GuildStatusController.isGuildLeader(player.getGuildStatus())) + return true; + + return false; + + //TODO test friends list once added + //does not meet above criteria. Cannot access. + } + + public static boolean playerCanManageNotFriends(PlayerCharacter player, Building building) { + + //Player Can only Control Building if player is in Same Guild as Building and is higher rank than IC. + + if (player == null) + return false; + + if (building == null) + return false; + + + if (building.getRank() == -1) + return false; + + if (IsOwner(building, player)) + return true; + + //Somehow guild leader check fails? lets check if Player is true Guild GL. + if (building.getGuild() != null && building.getGuild().isGuildLeader(player.getObjectUUID())) + return true; + if (GuildStatusController.isGuildLeader(player.getGuildStatus()) == false && GuildStatusController.isInnerCouncil(player.getGuildStatus()) == false) + return false; + + + return false; + + } + + public static synchronized boolean lootBuilding(PlayerCharacter player, Building building) { + + if (building == null) + return false; + + if (player == null) + return false; + + if (building.getRank() != -1) + return false; + + if (building.getBlueprintUUID() == 0) + return false; + + switch (building.getBlueprint().getBuildingGroup()) { + case SHRINE: + Shrine shrine = Shrine.shrinesByBuildingUUID.get(building.getObjectUUID()); + if (shrine == null) + return false; + + int amount = shrine.getFavors(); + //no more favors too loot! + if (amount == 0) { + + try { + ErrorPopupMsg.sendErrorPopup(player, 166); + } catch (Exception e) { + + } + return false; + } + + ItemBase elanIB = ItemBase.getItemBase(1705032); + + if (elanIB == null) + return false; + + if (!player.getCharItemManager().hasRoomInventory(elanIB.getWeight())) + return false; + + if (!Item.MakeItemForPlayer(elanIB, player, amount)) + return false; + + shrine.setFavors(0); + break; + case WAREHOUSE: + + Warehouse warehouse = Warehouse.warehouseByBuildingUUID.get(building.getObjectUUID()); + + if (warehouse == null) + return false; + + for (ItemBase resourceBase : ItemBase.getResourceList()) { + if (!player.getCharItemManager().hasRoomInventory(resourceBase.getWeight())) { + ChatManager.chatSystemInfo(player, "You can not carry any more of that item."); + return false; + } + if (warehouse.getResources().get(resourceBase) == null) + continue; + + int resourceAmount = warehouse.getResources().get(resourceBase); + + if (resourceAmount <= 0) + continue; + + if (warehouse.loot(player, resourceBase, resourceAmount, true)) + ChatManager.chatInfoInfo(player, "You have looted " + resourceAmount + ' ' + resourceBase.getName()); + } + break; + + } + //Everything was looted, Maybe we should + return true; + } + + //This method restarts an upgrade timer when a building is loaded from the database. + // Submit upgrade job for this building based upon it's current upgradeDateTime + + public static void submitUpgradeJob(Building building) { + + if (building == null) + return; + + + if (building.getUpgradeDateTime() == null) { + Logger.error("Attempt to submit upgrade job for non-ranking building"); + return; + } + + // Submit upgrade job for future date or current instant + + if (building.getUpgradeDateTime().isAfter(LocalDateTime.now())) { + JobContainer jc = JobScheduler.getInstance().scheduleJob(new UpgradeBuildingJob(building), + building.getUpgradeDateTime().atZone(ZoneId.systemDefault()) + .toInstant().toEpochMilli()); + } else + JobScheduler.getInstance().scheduleJob(new UpgradeBuildingJob(building), 0); + } + + public static void setUpgradeDateTime(Building building, LocalDateTime upgradeDateTime, int rankCost) { + + if (building == null) + return; + if (!DbManager.BuildingQueries.updateBuildingUpgradeTime(upgradeDateTime, building, rankCost)) { + Logger.error("Failed to set upgradeTime for building " + building.getObjectUUID()); + return; + } + + building.upgradeDateTime = upgradeDateTime; + + } + + // Method transfers ownership of all hirelings in a building + + public static void refreshHirelings(Building building) { + + if (building == null) + return; + + Guild newGuild; + + if (building.getOwner() == null) + newGuild = Guild.getErrantGuild(); + else + newGuild = building.getOwner().getGuild(); + + for (AbstractCharacter hireling : building.getHirelings().keySet()) { + hireling.setGuild(newGuild); + WorldGrid.updateObject(hireling); + } + + } + + public static void cleanupHirelings(Building building) { + + // Early exit: Cannot have hirelings in a building + // without a blueprint. + + if (building.getBlueprintUUID() == 0) + return; + + // Remove all hirelings for destroyed buildings + + if (building.getRank() < 1) { + + for (AbstractCharacter slottedNPC : building.getHirelings().keySet()) { + + if (slottedNPC.getObjectType() == Enum.GameObjectType.NPC) + ((NPC) slottedNPC).remove(); + else if (slottedNPC.getObjectType() == Enum.GameObjectType.Mob) + ((Mob) slottedNPC).remove(building); + } + return; + } + + // Delete hireling if building has deranked. + for (AbstractCharacter hireling : building.getHirelings().keySet()) { + + NPC npc = null; + Mob mob = null; + + if (hireling.getObjectType() == Enum.GameObjectType.NPC) + npc = (NPC) hireling; + else if (hireling.getObjectType() == Enum.GameObjectType.Mob) + mob = (Mob) hireling; + + if (building.getHirelings().get(hireling) > building.getBlueprint().getSlotsForRank(building.getRank())) + + if (npc != null) { + if (!npc.remove()) + Logger.error("Failed to remove npc " + npc.getObjectUUID() + + "from Building " + building.getObjectUUID()); + else + building.getHirelings().remove(npc); + } else if (mob != null) { + if (!mob.remove(building)) + Logger.error("Failed to remove npc " + npc.getObjectUUID() + + "from Building " + building.getObjectUUID()); + else + building.getHirelings().remove(npc); + } + + } + + refreshHirelings(building); + } + + public static Building getBuilding(int id) { + + if (id == 0) + return null; + + Building building; + + building = (Building) DbManager.getFromCache(Enum.GameObjectType.Building, id); + + if (building != null) + return building; + + return DbManager.BuildingQueries.GET_BUILDINGBYUUID(id); + + } + + public static boolean PlayerCanControlNotOwner(Building building, PlayerCharacter player) { + + if (player == null) + return false; + + if (building == null) + return false; + + if (building.getOwner() == null) + return false; + + //lets pass true if player is owner anyway. + if (building.getOwner().equals(player)) + return true; + + if (player.getGuild().isErrant()) + return false; + + if (building.getGuild().isGuildLeader(player.getObjectUUID())) + return true; + + if (!Guild.sameGuild(building.getGuild(), player.getGuild())) + return false; + + return GuildStatusController.isGuildLeader(player.getGuildStatus()) != false || GuildStatusController.isInnerCouncil(player.getGuildStatus()) != false; + } + + //This is mainly used for Rolling and gold sharing between building and warehouse. + + public static int GetWithdrawAmountForRolling(Building building, int cost) { + + //all funds are available to roll. + + if (cost <= GetAvailableGold(building)) + return cost; + + // cost is more than available gold, return available gold + + return GetAvailableGold(building); + + } + + public static int GetAvailableGold(Building building) { + + if (building.getStrongboxValue() == 0) + return 0; + + if (building.getStrongboxValue() < building.reserve) + return 0; + + return building.getStrongboxValue() - building.reserve; + } + + public static int GetOverdraft(Building building, int cost) { + int availableGold = GetWithdrawAmountForRolling(building, cost); + return cost - availableGold; + } + + public static boolean IsPlayerHostile(Building building, PlayerCharacter player) { + + //Nation Members and Guild members are not hostile. + // if (building.getGuild() != null){ + // if (pc.getGuild() != null) + // if (building.getGuild().getObjectUUID() == pc.getGuildUUID() + // || pc.getGuild().getNation().getObjectUUID() == building.getGuild().getNation().getObjectUUID()) + // return false; + // } + if (Guild.sameNationExcludeErrant(building.getGuild(), player.getGuild())) + return false; + + if (!building.reverseKOS) { + + Condemned condemn = building.getCondemned().get(player.getObjectUUID()); + + if (condemn != null && condemn.isActive()) + return true; + + if (player.getGuild() != null) { + + Condemned guildCondemn = building.getCondemned().get(player.getGuild().getObjectUUID()); + + if (guildCondemn != null && guildCondemn.isActive()) + return true; + + Condemned nationCondemn = building.getCondemned().get(player.getGuild().getNation().getObjectUUID()); + return nationCondemn != null && nationCondemn.isActive() && nationCondemn.getFriendType() == Condemned.NATION; + } else { + //TODO ADD ERRANT KOS CHECK + } + } else { + + Condemned condemn = building.getCondemned().get(player.getObjectUUID()); + + if (condemn != null && condemn.isActive()) + return false; + + if (player.getGuild() != null) { + + Condemned guildCondemn = building.getCondemned().get(player.getGuild().getObjectUUID()); + + if (guildCondemn != null && guildCondemn.isActive()) + return false; + + Condemned nationCondemn = building.getCondemned().get(player.getGuild().getNation().getObjectUUID()); + return nationCondemn == null || !nationCondemn.isActive() || nationCondemn.getFriendType() != Condemned.NATION; + } else { + //TODO ADD ERRANT KOS CHECK + } + return true; + } + + //When we get to here, This means The building was not reverse KOS + //and passed the hostile test. + + return false; + } + + public static final synchronized boolean addHirelingForWorld(Building building, PlayerCharacter contractOwner, Vector3fImmutable NpcLoc, Zone zone, Contract NpcID, int rank) { + + String pirateName = NPC.getPirateName(NpcID.getMobbaseID()); + + NPC npc = null; + + npc = NPC.createNPC( pirateName, NpcID.getObjectUUID(), NpcLoc, null, false, zone, (short) rank, false, building); + + if (npc == null) + return false; + + + npc.setBuilding(building); + npc.setParentZone(zone); + WorldGrid.addObject(npc, contractOwner); + + return true; + + } + + public static synchronized boolean addHireling(Building building, PlayerCharacter contractOwner, Vector3fImmutable NpcLoc, Zone zone, Contract NpcID, Item item) { + + int rank = 1; + + if (building.getBlueprintUUID() == 0) + return false; + + if (building.getBlueprint().getMaxSlots() == building.getHirelings().size()) + return false; + + String pirateName = NPC.getPirateName(NpcID.getMobbaseID()); + + if (item.getChargesRemaining() > 0) + rank = item.getChargesRemaining() * 10; + else rank = 10; + + Mob mob = null; + NPC npc = null; + + if (NPC.ISGuardCaptain(NpcID.getContractID())) { + + mob = Mob.createMob( NpcID.getMobbaseID(), NpcLoc, contractOwner.getGuild(), true, zone, building, NpcID.getContractID()); + + if (mob == null) + return false; + + mob.setRank(rank); + mob.setPlayerGuard(true); + mob.setParentZone(zone); + return true; + } else { + npc = NPC.createNPC( pirateName, NpcID.getObjectUUID(), NpcLoc, contractOwner.getGuild(), false, zone, (short) rank, false, building); + + if (npc == null) + return false; + + + npc.setBindLoc(NpcLoc.x - zone.getAbsX(), NpcLoc.y - zone.getAbsY(), NpcLoc.z - zone.getAbsZ()); + + npc.setBuilding(building); + npc.setParentZone(zone); + + if (NPC.GetNPCProfits(npc) == null) + NPCProfits.CreateProfits(npc); + + WorldGrid.addObject(npc, contractOwner); + + return true; + } + } + + public static boolean IsWallPiece(Building building) { + + if (building.getBlueprint() == null) + return false; + + BuildingGroup buildingGroup = building.getBlueprint().getBuildingGroup(); + + switch (buildingGroup) { + case WALLSTRAIGHT: + case WALLCORNER: + case SMALLGATE: + case ARTYTOWER: + case WALLSTRAIGHTTOWER: + case WALLSTAIRS: + return true; + default: + return false; + } + } + + public static Building getBuildingFromCache(int id) { + return (Building) DbManager.getFromCache(GameObjectType.Building, id); + } + + public static boolean IsOwner(Building building, PlayerCharacter player) { + if (building == null || player == null) + return false; + + if (building.getOwner() == null) + return false; + + + return building.getOwner().getObjectUUID() == player.getObjectUUID(); + + } + + public static float GetMissingHealth(Building building) { + return building.healthMax - building.getCurrentHitpoints(); + } + + public static int GetRepairCost(Building building) { + return (int) (GetMissingHealth(building) * .10f); + } + + public static Regions GetRegion(Building building, float x, float y, float z) { + if (building.getBounds() == null) + return null; + + if (building.getBounds().getRegions() == null) + return null; + + Regions currentRegion = null; + for (Regions region : building.getBounds().getRegions()) { + + if (region.isPointInPolygon(new Vector3fImmutable(x, y, z))) { + if (y > (region.highLerp.y - 5)) + currentRegion = region; + } + } + return currentRegion; + } + + public static Regions GetRegion(Building building, int room, int level, float x, float z) { + if (building.getBounds() == null) + return null; + + if (building.getBounds().getRegions() == null) + return null; + + for (Regions region : building.getBounds().getRegions()) { + + if (region.getLevel() != level) + continue; + if (region.getRoom() != room) + continue; + + if (region.isPointInPolygon(new Vector3fImmutable(x, 0, z))) { + return region; + } + } + return null; + } + + public static Vector3fImmutable GetBindLocationForBuilding(Building building){ + + Vector3fImmutable bindLoc = null; + + if (building == null) + return Enum.Ruins.getRandomRuin().getLocation(); + + + bindLoc = building.getLoc(); + + + float radius = Bounds.meshBoundsCache.get(building.getMeshUUID()).radius; + if ( building.getRank() == 8){ + bindLoc = building.getStuckLocation(); + if (bindLoc != null) + return bindLoc; + } + + float x = bindLoc.getX(); + float z = bindLoc.getZ(); + float offset = ((ThreadLocalRandom.current().nextFloat() * 2) - 1) * radius; + int direction = ThreadLocalRandom.current().nextInt(4); + + switch (direction) { + case 0: + x += radius; + z += offset; + break; + case 1: + x += offset; + z -= radius; + break; + case 2: + x -= radius; + z += offset; + break; + case 3: + x += offset; + z += radius; + break; + } + bindLoc = new Vector3fImmutable(x, bindLoc.getY(), z); + + return bindLoc; + + + + } + +} diff --git a/src/engine/gameManager/ChatManager.java b/src/engine/gameManager/ChatManager.java new file mode 100644 index 00000000..758c12a7 --- /dev/null +++ b/src/engine/gameManager/ChatManager.java @@ -0,0 +1,1231 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.gameManager; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.Enum.ModType; +import engine.Enum.SourceType; +import engine.InterestManagement.WorldGrid; +import engine.db.archive.BaneRecord; +import engine.db.archive.PvpRecord; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.MessageDispatcher; +import engine.net.client.ClientConnection; +import engine.net.client.Protocol; +import engine.net.client.msg.chat.*; +import engine.net.client.msg.commands.ClientAdminCommandMsg; +import engine.objects.*; +import engine.server.MBServerStatics; +import engine.server.world.WorldServer; +import engine.session.Session; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +public enum ChatManager { + + CHATMANAGER; + + // Sending a quantity of (FLOOD_QTY_THRESHOLD) messages within + // (FLOOD_TIME_THRESHOLD) ms will flag as a flood message + // Example, set to 3 & 2000 to flag the 3rd message within 2000 ms. + + private static final int FLOOD_QTY_THRESHOLD = 3; + private static final int FLOOD_TIME_THRESHOLD = 2000; + private static final String FLOOD_USER_ERROR = "You talk too much!"; + private static final String SILENCED = "You find yourself mute!"; + private static final String UNKNOWN_COMMAND = "No such command."; + /** + * This method used when handling a ChatMsg received from the network. + */ + public static void handleChatMsg(Session sender, AbstractChatMsg msg) { + + if (msg == null) { + Logger.warn( + "null message: "); + return; + } + + if (sender == null) { + Logger.warn( + "null sender: "); + return; + } + + PlayerCharacter pc = sender.getPlayerCharacter(); + + if (pc == null) { + Logger.warn( + "invalid sender: "); + return; + } + + Protocol protocolMsg = msg.getProtocolMsg(); + + // Flood control, implemented per channel + + boolean isFlood = false; + long curMsgTime = System.currentTimeMillis(); + long checkTime = pc.chatFloodTime(protocolMsg.opcode, curMsgTime, FLOOD_QTY_THRESHOLD - 1); + + if ((checkTime > 0L) && (curMsgTime - checkTime < FLOOD_TIME_THRESHOLD)) + isFlood = true; + + switch (protocolMsg) { + case CHATSAY: + ChatManager.chatSay(pc, msg.getMessage(), isFlood); + return; + case CHATCSR: + ChatManager.chatCSR(msg); + return; + case CHATTELL: + ChatTellMsg ctm = (ChatTellMsg) msg; + ChatManager.chatTell(pc, ctm.getTargetName(), ctm.getMessage(), isFlood); + return; + case CHATSHOUT: + ChatManager.chatShout(pc, msg.getMessage(), isFlood); + return; + case CHATGUILD: + ChatManager.chatGuild(pc, (ChatGuildMsg) msg); + return; + case CHATGROUP: + ChatManager.chatGroup(pc, (ChatGroupMsg) msg); + return; + case CHATIC: + ChatManager.chatIC(pc, (ChatICMsg) msg); + return; + case LEADERCHANNELMESSAGE: + ChatManager.chatGlobal(pc, msg.getMessage(), isFlood); + return; + case GLOBALCHANNELMESSAGE: + case CHATPVP: + case CHATCITY: + case CHATINFO: + case SYSTEMBROADCASTCHANNEL: + default: + } + + } + + /* + * Channels + */ + /* + * CSR + */ + public static void chatCSR(AbstractChatMsg msg) { + PlayerCharacter sender = (PlayerCharacter) msg.getSource(); + if (sender == null) + return; + // if (promotionClass == null) + // return false; + // int promotionClassID = promotionClass.getUUID(); + // switch (promotionClassID) { + // case 2901: + // case 2902: + // case 2903: + // case 2904: + // case 2910: + // return true; + // } + // return false; + if (!sender.isCSR) { + ChatManager.chatSystemError(sender, UNKNOWN_COMMAND); + return; + } + ArrayList distroList = new ArrayList<>(); + for (PlayerCharacter pc : SessionManager + .getAllActivePlayerCharacters()) { + if (pc.getAccount().status.equals(Enum.AccountStatus.BANNED) == false) + distroList.add(pc); + } + // Send dispatch to each player + + for (AbstractWorldObject abstractWorldObject : distroList) { + PlayerCharacter playerCharacter = (PlayerCharacter) abstractWorldObject; + Dispatch dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + } + + public static boolean testSilenced(PlayerCharacter pc) { + + PlayerBonuses bonus = pc.getBonuses(); + + if (bonus != null && bonus.getBool(ModType.Silenced, SourceType.None)) { + ChatManager.chatSayError(pc, SILENCED); + return true; + } + return false; + } + + /* + * Say + */ + public static void chatSay(AbstractWorldObject player, String text, boolean isFlood) { + + PlayerCharacter pcSender = null; + + if (player.getObjectType() == GameObjectType.PlayerCharacter) + pcSender = (PlayerCharacter) player; + + // Parser eats all dev commands + + if (isFlood) { + ChatManager.chatSayError(pcSender, FLOOD_USER_ERROR); + return; + } + + if (ChatManager.isDevCommand(text) == true) { + ChatManager.processDevCommand(player, text); + return; + } + + if (ChatManager.isUpTimeRequest(text) == true) { + sendSystemMessage(pcSender, WorldServer.getUptimeString()); + return; + } + + if (ChatManager.isVersionRequest(text) == true) { + sendSystemMessage(pcSender, ConfigManager.MB_WORLD_GREETING.getValue()); + return; + } + + if (ChatManager.isNetStatRequest(text) == true) { + sendSystemMessage(pcSender, MessageDispatcher.getNetstatString()); + return; + } + + // Needs to be refactored + + if (ChatManager.isPopulationRequest(text) == true) { + sendSystemMessage(pcSender, SimulationManager.getPopulationString()); + return; + } + + if (ChatManager.isPvpRequest(text) == true) { + sendSystemMessage(pcSender, PvpRecord.getPvpHistoryString(pcSender.getObjectUUID())); + return; + } + + if (ChatManager.isBaneRequest(text) == true) { + sendSystemMessage(pcSender, BaneRecord.getBaneHistoryString()); + return; + } + + if (pcSender != null && testSilenced(pcSender)) + return; + + // Make the Message + ChatSayMsg chatSayMsg = new ChatSayMsg(player, text); + DispatchMessage.dispatchMsgToInterestArea(pcSender, chatSayMsg, Enum.DispatchChannel.SECONDARY, MBServerStatics.SAY_RANGE, true, true); + + } + + public static void sendSystemMessage(PlayerCharacter targetPlayer, String messageString) { + + ChatSystemMsg msg = new ChatSystemMsg(null, messageString); + msg.setMessageType(4); + msg.setChannel(engine.Enum.ChatChannelType.SYSTEM.getChannelID()); + Dispatch dispatch = Dispatch.borrow(targetPlayer, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + /* + * Shout + */ + public static void chatShout(AbstractWorldObject sender, String text, + boolean isFlood) { + + PlayerCharacter pcSender = null; + + if (sender.getObjectType().equals(GameObjectType.PlayerCharacter)) + pcSender = (PlayerCharacter) sender; + + if (isFlood) { + ChatManager.chatSayError(pcSender, FLOOD_USER_ERROR); + return; + } + + if (pcSender != null && testSilenced(pcSender)) + return; + + // Make the Message + ChatShoutMsg msg = new ChatShoutMsg(sender, text); + DispatchMessage.dispatchMsgToInterestArea(pcSender, msg, engine.Enum.DispatchChannel.SECONDARY, MBServerStatics.SHOUT_RANGE, true, true); + + } + + public static void chatGlobal(PlayerCharacter sender, String text, + boolean isFlood) { + +PlayerCharacter pcSender = null; + +if (sender.getObjectType().equals(GameObjectType.PlayerCharacter)) +pcSender = (PlayerCharacter) sender; + +if (isFlood) { +ChatManager.chatSayError(pcSender, FLOOD_USER_ERROR); +return; +} + +if (pcSender != null && testSilenced(pcSender)) +return; + +// Make the Message +ChatGlobalMsg msg = new ChatGlobalMsg(sender, text); +DispatchMessage.dispatchMsgToAll(sender, msg, true); + +} + + /* + * Tell + */ + public static void chatTell(AbstractWorldObject sender, String recipient, + String text, boolean isFlood) { + if (text.isEmpty()) + return; + + PlayerCharacter pcSender = null; + + if (sender.getObjectType().equals(GameObjectType.PlayerCharacter)) + pcSender = (PlayerCharacter) sender; + + if (pcSender != null && testSilenced(pcSender)) + return; + + PlayerCharacter pcRecip = SessionManager + .getPlayerCharacterByLowerCaseName(recipient); + + if (pcRecip != null) { + if (isFlood) { + ChatManager.chatTellError(pcSender, FLOOD_USER_ERROR); + return; + } + + ChatManager.chatTell(sender, pcRecip, text); + } else + ChatManager.chatTellError((PlayerCharacter) sender, + "There is no such player."); + + } + + public static void chatTell(AbstractWorldObject sender, + AbstractWorldObject recipient, String text) { + + PlayerCharacter pcSender = null; + + if (sender.getObjectType().equals(GameObjectType.PlayerCharacter)) + pcSender = (PlayerCharacter) sender; + + if (pcSender != null && testSilenced(pcSender)) + return; + + // Verify we are sending to a PlayerCharacter + PlayerCharacter pcRecip; + if (recipient.getObjectType().equals(GameObjectType.PlayerCharacter)) + pcRecip = (PlayerCharacter) recipient; + else + return; + + ClientConnection cc = SessionManager.getClientConnection( + pcRecip); + + // make sure we have a good ClientConnection + if (cc != null) { + + ChatTellMsg chatTellMsg = new ChatTellMsg(sender, pcRecip, text); + + // Don't send to recipient if they're ignoring sender + if (!pcRecip.isIgnoringPlayer(pcSender)) { + // Send dispatch to each player + + Dispatch dispatch = Dispatch.borrow(pcRecip, chatTellMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + // Also send /tell to sender + if (pcSender != null) { + Dispatch dispatch = Dispatch.borrow(pcSender, chatTellMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + } else + ChatManager.chatTellError(pcSender, + "There is no such player."); + } + + public static void chatGuild(PlayerCharacter sender, ChatGuildMsg msg) { + + // Verify sender has PlayerCharacter + if (sender == null) + return; + + if (testSilenced(sender)) + return; + + // Verify player is in guild and get guild + Guild guild = sender.getGuild(); + if (guild == null || guild.getObjectUUID() == 0) + return; + + // Get Distro List for guild + ArrayList distroList = SessionManager.getActivePCsInGuildID(guild.getObjectUUID()); + if (msg.getUnknown02() == 5) { + + Guild nation = guild.getNation(); + if (nation == null) + return; + distroList = ChatManager.getNationListChat(nation, sender); + } + + // Check the DistroList size + if (distroList.size() < 1) { + ChatManager.chatGuildError(sender, + "You find yourself mute!"); + Logger.error("Guild Chat returned a list of Players <1 in length."); + return; + } + + // Make the Message + + // make the ed message + ChatGuildMsg chatGuildMsg = new ChatGuildMsg(msg); + + chatGuildMsg.setSourceType(sender.getObjectType().ordinal()); + chatGuildMsg.setSourceID(sender.getObjectUUID()); + chatGuildMsg.setSourceName(sender.getFirstName()); + chatGuildMsg.setUnknown03(MBServerStatics.worldMapID); // Server ID + chatGuildMsg.setUnknown04(sender.getGuild() != null ? sender.getGuild() + .getCharter() : 0); // Charter? + chatGuildMsg.setUnknown05(GuildStatusController.getTitle(sender.getGuildStatus())); // Title? + chatGuildMsg.setUnknown06(sender.getRace().getRaceType().getCharacterSex().equals(Enum.CharacterSex.MALE) ? 1 : 2); // isMale?, seen 1 and 2 + + // Send dispatch to each player + + for (AbstractWorldObject abstractWorldObject : distroList) { + PlayerCharacter playerCharacter = (PlayerCharacter) abstractWorldObject; + Dispatch dispatch = Dispatch.borrow(playerCharacter, chatGuildMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + } + + public static void chatIC(PlayerCharacter sender, ChatICMsg msg) { + + // Verify sender has PlayerCharacter + if (sender == null) + return; + + if (testSilenced(sender)) + return; + + // Verify player is in guild and get guild + Guild guild = sender.getGuild(); + if (guild == null || guild.getObjectUUID() == 0) + return; + + //Verify player is an IC + + if (GuildStatusController.isInnerCouncil(sender.getGuildStatus()) == false) + return; + + // Get Distro List for guild + HashSet distroList = ChatManager.getGuildICList(guild, sender); + + // Check the DistroList size + if (distroList.size() < 1) { + ChatManager.chatICError(sender, "You find yourself mute!"); + Logger.error("Guild Chat returned a list of Players <1 in length."); + return; + } + + // TODO Hrm, are we modifying the incoming message or making a response? + // Not anymore we aren't! + + // Create new outgoing message + + ChatICMsg chatICMsg = new ChatICMsg(msg); + + chatICMsg.setSourceType(sender.getObjectType().ordinal()); + chatICMsg.setSourceID(sender.getObjectUUID()); + chatICMsg.setSourceName(sender.getFirstName()); + chatICMsg.setUnknown02(MBServerStatics.worldMapID); // Server ID + chatICMsg.setUnknown03(GuildStatusController.getRank(sender.getGuildStatus())); // correct? + chatICMsg.setUnknown04(GuildStatusController.getTitle(sender.getGuildStatus())); // correct? + chatICMsg.setUnknown05(2); // unknown, seen 1 and 2 + + + // Send dispatch to each player + + for (AbstractWorldObject abstractWorldObject : distroList) { + PlayerCharacter playerCharacter = (PlayerCharacter) abstractWorldObject; + Dispatch dispatch = Dispatch.borrow(playerCharacter, chatICMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + } + + private static boolean isVersionRequest(String text) { + return text.equalsIgnoreCase("lua_version()"); + } + + private static boolean isPvpRequest(String text) { + return text.equalsIgnoreCase("lua_pvp()"); + } + + private static boolean isBaneRequest(String text) { + return text.equalsIgnoreCase("lua_banes()"); + } + + public static void chatGroup(PlayerCharacter sender, ChatGroupMsg msg) { + + // Verify sender has PlayerCharacter + if (sender == null) + return; + + if (testSilenced(sender)) + return; + + // Verify player is in guild and get guild + + Group group = GroupManager.getGroup(sender); + + if (group == null) + return; + + // Get Distro List for guild + HashSet distroList = ChatManager.getGroupList(group, sender); + + // Check the DistroList size + if (distroList.size() < 1) { + ChatManager.chatGroupError(sender, + "You find yourself mute!"); + Logger.error("Group Chat returned a list of Players <1 in length."); + return; + } + + // Make the Message + + ChatGroupMsg chatGroupMsg = new ChatGroupMsg(msg); + + chatGroupMsg.setSourceType(sender.getObjectType().ordinal()); + chatGroupMsg.setSourceID(sender.getObjectUUID()); + chatGroupMsg.setSourceName(sender.getFirstName()); + chatGroupMsg.setUnknown02(MBServerStatics.worldMapID); // Server ID + + + // Send dispatch to each player + + for (AbstractWorldObject abstractWorldObject : distroList) { + PlayerCharacter playerCharacter = (PlayerCharacter) abstractWorldObject; + Dispatch dispatch = Dispatch.borrow(playerCharacter, chatGroupMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + } + + public static void GuildEnterWorldMsg(PlayerCharacter sender, + ClientConnection origin) { + // Verify sender has PlayerCharacter + if (sender == null) + return; + // Verify player is in guild and get guild + Guild guild = sender.getGuild(); + if (guild == null || guild.getObjectUUID() == 0) + return; + // Get Distro List for guild + HashSet distroList = ChatManager.getGuildList(guild, null); + // Check the DistroList size + if (distroList.size() < 1) { + Logger.error("Guild EnterWorldMsg returned a list of Players <1 in length."); + return; + } + + // Make and send enter world message + GuildEnterWorldMsg msg = new GuildEnterWorldMsg(sender); + msg.setName(sender.getFirstName()); + msg.setGuildTitle(GuildStatusController.getTitle(sender.getGuildStatus())); + msg.setGuildUUID(guild.getObjectUUID()); + msg.setCharter(guild.getCharter()); + + // Send dispatch to each player + + for (AbstractWorldObject abstractWorldObject : distroList) { + PlayerCharacter playerCharacter = (PlayerCharacter) abstractWorldObject; + Dispatch dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + } + + public static void chatPeekSteal(PlayerCharacter sender, AbstractCharacter tar, Item item, boolean success, boolean detect, int amount) { + if (sender == null || tar == null) + return; + + PlayerCharacter target = null; + + if (tar.getObjectType().equals(GameObjectType.PlayerCharacter)) + target = (PlayerCharacter) tar; + + // Get Distro List + HashSet distroList = WorldGrid + .getObjectsInRangePartial(sender.getLoc(), + MBServerStatics.SAY_RANGE, MBServerStatics.MASK_PLAYER); + + // Check the DistroList size + if (distroList.size() < 1) + return; + + //remove Thief and Victim from other's list + int size = distroList.size(); + for (int i = size - 1; i > -1; i--) { + AbstractWorldObject awo = (AbstractWorldObject) distroList.toArray()[i]; + if (awo.getObjectUUID() == sender.getObjectUUID()) + distroList.remove(awo); + else if (awo.getObjectUUID() == tar.getObjectUUID()) + distroList.remove(awo); + + } + + String textToThief = ""; + String textToVictim = ""; + String textToOthers = ""; + + if (item != null) //Steal + if (success) { + String name = ""; + if (item.getItemBase() != null) + if (item.getItemBase().getUUID() == 7) + name = amount + " gold "; + else { + String vowels = "aeiou"; + String iName = item.getItemBase().getName(); + if (iName.length() > 0) + if (vowels.indexOf(iName.substring(0, 1).toLowerCase()) >= 0) + name = "an " + iName + ' '; + else + name = "a " + iName + ' '; + } + textToThief = "You have stolen " + name + "from " + tar.getFirstName() + '!'; + textToVictim = sender.getFirstName() + "is caught with thier hands in your pocket!"; + //textToOthers = sender.getFirstName() + " steals " + name + "from " + target.getFirstName() + "."; + } else + textToThief = "Your attempt at stealing failed..."; //textToVictim = sender.getFirstName() + " fails to steal from you."; + //textToOthers = sender.getFirstName() + " fails to steal from " + target.getFirstName() + "."; + else //Peek + if (success) { + if (detect) { + textToThief = tar.getFirstName() + " catches you peeking through their belongings!"; + textToVictim = "You catch " + sender.getFirstName() + " peeking through your belongings!"; + } + } else { + textToThief = "Your attempt at peeking failed..."; + textToVictim = sender.getFirstName() + " is seen eyeing up your backpack..."; + textToOthers = sender.getFirstName() + " is seen eyeing up the backpack of " + tar.getFirstName() + "..."; + } + + //Send msg to thief + HashSet senderList = new HashSet<>(); + senderList.add(sender); + if (!textToThief.isEmpty()) + ChatManager.chatSystemSend(senderList, textToThief, 1, 2); + + if (target != null && !textToVictim.isEmpty()) { + HashSet victimList = new HashSet<>(); + victimList.add(target); + ChatManager.chatSystemSend(victimList, textToVictim, 1, 2); + } + + //Send msg to Others in range\ + if (!textToOthers.isEmpty()) + ChatManager.chatSystemSend(distroList, textToOthers, 1, 2); + } + + // Send System Announcement as a flash at top of screen + public static void chatSystemFlash(String text) { + + chatSystem(null, text, 2, 1); + } + + public static void chatSystemChannel(String text) { + chatSystem(null, text, 1, 4); // Type 4 simply displays text as is + } + + // Send Error Message to player + public static void chatSystemError(PlayerCharacter pc, String text) { + sendErrorMsgToPlayer(pc, text, 1); + } + + public static void chatSystemInfo(PlayerCharacter pc, String text) { + sendInfoMsgToPlayer(pc, text, 1); + } + + public static void chatCommanderError(PlayerCharacter pc, String text) { + sendErrorMsgToPlayer(pc, text, 3); + } + + public static void chatNationError(PlayerCharacter pc, String text) { + sendErrorMsgToPlayer(pc, text, 5); + } + + public static void chatLeaderError(PlayerCharacter pc, String text) { + sendErrorMsgToPlayer(pc, text, 6); + } + + public static void chatShoutError(PlayerCharacter pc, String text) { + sendErrorMsgToPlayer(pc, text, 7); + } + + public static void chatInfoError(PlayerCharacter pc, String text) { + sendErrorMsgToPlayer(pc, text, 10); + } + + public static void chatGuildError(PlayerCharacter pc, String text) { + sendErrorMsgToPlayer(pc, text, 12); + } + + public static void chatICError(PlayerCharacter pc, String text) { + sendErrorMsgToPlayer(pc, text, 13); + } + + public static void chatGroupError(PlayerCharacter pc, String text) { + sendErrorMsgToPlayer(pc, text, 14); + } + + public static void chatCityError(PlayerCharacter pc, String text) { + sendErrorMsgToPlayer(pc, text, 15); + } + + public static void chatSayError(PlayerCharacter pc, String text) { + sendErrorMsgToPlayer(pc, text, 16); + } + + public static void chatEmoteError(PlayerCharacter pc, String text) { + sendErrorMsgToPlayer(pc, text, 17); + } + + public static void chatTellError(PlayerCharacter pc, String text) { + sendErrorMsgToPlayer(pc, text, 19); + } + + // Send Info Message to channels + public static void chatCommanderInfo(PlayerCharacter pc, String text) { + chatSystem(pc, text, 3, 2); + } + + public static void chatNationInfo(PlayerCharacter pc, String text) { + chatSystem(pc, text, 5, 2); + } + + public static void chatNationInfo(Guild nation, String text) { + chatSystemGuild(nation, text, 5, 2); + } + + public static void chatLeaderInfo(PlayerCharacter pc, String text) { + chatSystem(pc, text, 6, 2); + } + + public static void chatShoutInfo(PlayerCharacter pc, String text) { + chatSystem(pc, text, 7, 2); + } + + public static void chatInfoInfo(PlayerCharacter pc, String text) { + chatSystem(pc, text, 10, 2); + } + + public static void chatGuildInfo(PlayerCharacter pc, String text) { + chatSystem(pc, text, 12, 2); + } + + public static void chatICInfo(PlayerCharacter pc, String text) { + chatSystem(pc, text, 13, 2); + } + + public static void chatGroupInfo(PlayerCharacter pc, String text) { + chatSystem(pc, text, 14, 2); + } + + public static void chatCityInfo(PlayerCharacter pc, String text) { + chatSystem(pc, text, 15, 2); + } + + public static void chatSayInfo(PlayerCharacter pc, String text) { + chatSystem(pc, text, 16, 2); + } + + public static void chatEmoteInfo(PlayerCharacter pc, String text) { + chatSystem(pc, text, 17, 2); + } + + public static void chatTellInfo(PlayerCharacter pc, String text) { + chatSystem(pc, text, 19, 2); + } + + public static void chatGroupInfoCanSee(PlayerCharacter pc, String text) { + HashSet distroList = null; + + Group group = GroupManager.getGroup(pc); + if (group != null) { + distroList = ChatManager.getGroupList(group, pc); + if (distroList != null) { + Iterator it = distroList.iterator(); + while (it.hasNext()) { + AbstractWorldObject awo = it.next(); + if (!(awo.getObjectType().equals(GameObjectType.PlayerCharacter))) + it.remove(); + else { + PlayerCharacter pcc = (PlayerCharacter) awo; + if (pcc.getSeeInvis() < pc.getHidden()) + it.remove(); + } + } + } + } + ChatManager.chatSystemSend(distroList, text, 14, 2); + } + + // Send MOTD Message to channels + public static void chatNationMOTD(PlayerCharacter pc, String text) { + chatNationMOTD(pc, text, false); + } + + public static void chatNationMOTD(PlayerCharacter pc, String text, boolean all) { + if (all) // Send to all Nation + + chatSystem(pc, text, 5, 3); + else // Send to just pc + + chatSystemMOTD(pc, text, 5, 3); + } + + public static void chatGuildMOTD(PlayerCharacter pc, String text) { + chatGuildMOTD(pc, text, false); + } + + public static void chatGuildMOTD(PlayerCharacter pc, String text, boolean all) { + if (all) // Send to all Guild + + chatSystem(pc, text, 12, 3); + else // Send to just pc + + chatSystemMOTD(pc, text, 12, 3); + } + + public static void chatICMOTD(PlayerCharacter pc, String text) { + chatICMOTD(pc, text, false); + } + + public static void chatICMOTD(PlayerCharacter pc, String text, boolean all) { + if (all) // Send to all IC's + + chatSystem(pc, text, 13, 3); + else // Send to just pc + + chatSystemMOTD(pc, text, 13, 3); + } + + // Send Info Message to guild channel based on guild + public static void chatGuildInfo(Guild guild, String text) { + HashSet distroList = null; + if (guild != null) + distroList = ChatManager.getGuildList(guild, null); + ChatManager.chatSystemSend(distroList, text, 12, 2); + } + + public static void chatSystemMOTD(PlayerCharacter sender, String text, + int channel, int messageType) { + HashSet distroList = ChatManager.getOwnPlayer(sender); + ChatManager.chatSystemSend(distroList, text, channel, messageType); + } + + public static void chatPVP(String text) { + // Create message + ChatPvPMsg msg = new ChatPvPMsg(null, text); + DispatchMessage.dispatchMsgToAll(msg); + } + + public static void chatInfo(String text) { + HashSet distroList = ChatManager.getAllPlayers(null); + chatSystemSend(distroList, text, 1, 2); + } + + public static ChatSystemMsg CombatInfo(AbstractWorldObject source, AbstractWorldObject target) { + String text = "The " + target.getName() + " attacks " + source.getName(); + ChatSystemMsg msg = new ChatSystemMsg(null, text); + msg.setChannel(20); + msg.setMessageType(2); + return msg; + } + + public static void chatSystem(PlayerCharacter sender, String text, int channel, + int messageType) { + HashSet distroList = null; + if (channel == 1) // System Channel Message + + distroList = ChatManager.getAllPlayers(sender); + else if (channel == 2) // System Flash, send to everyone + + distroList = ChatManager.getAllPlayers(sender); + else if (channel == 3) { // Commander Channel + } else if (channel == 5) { // Nation Channel, get Nation list + Guild guild = sender.getGuild(); + if (guild != null) { + Guild nation = guild.getNation(); + if (nation != null) + if (nation.getObjectUUID() != 0) // Don't /nation to errant + // nation + + distroList = ChatManager.getNationList(nation, sender); + } + } else if (channel == 6) { // Leader Channel + } else if (channel == 7) // Shout Channel + distroList = ChatManager.getOwnPlayer(sender); + else if (channel == 10) // Info Channel + distroList = getOwnPlayer(sender); + else if (channel == 12) { // guild Channel, get Guild list + Guild guild = sender.getGuild(); + if (guild != null) + if (guild.getObjectUUID() != 0) // Don't /guild to errant guild + + distroList = ChatManager.getGuildList(guild, sender); + } else if (channel == 13) { // IC Channel, get Guild IC list + Guild guild = sender.getGuild(); + if (guild != null) + if (guild.getObjectUUID() != 0) // Don't /IC to errant guild + + distroList = ChatManager.getGuildICList(guild, sender); + } else if (channel == 14) { // Group Channel, get group list + Group group = GroupManager.getGroup(sender); + if (group != null) + distroList = ChatManager.getGroupList(group, sender); + } else if (channel == 15) { // City Channel, get people bound to city + // list + } else if (channel == 16) // Say Channel + distroList = ChatManager.getOwnPlayer(sender); + else if (channel == 17) { // Emote Channel, get say List + } else if (channel == 19) // Tell Channel + distroList = ChatManager.getOwnPlayer(sender); + else + return; + ChatManager.chatSystemSend(distroList, text, channel, messageType); + } + + public static void chatSystemGuild(Guild sender, String text, int channel, + int messageType) { + HashSet distroList = null; + + if (channel == 5) { // Nation Channel, get Nation list + if (sender != null) { + Guild nation = sender.getNation(); + if (nation != null) + if (nation.getObjectUUID() != 0) // Don't /nation to errant + // nation + + distroList = ChatManager.getNationList(nation, null); + } + } else if (channel == 12) { // guild Channel, get Guild list + if (sender != null) + if (sender.getObjectUUID() != 0) // Don't /guild to errant guild + + distroList = ChatManager.getGuildList(sender, null); + } else if (channel == 13) { // IC Channel, get Guild IC list + if (sender != null) + if (sender.getObjectUUID() != 0) // Don't /IC to errant guild + + distroList = ChatManager.getGuildICList(sender, null); + } else + return; + ChatManager.chatSystemSend(distroList, text, channel, messageType); + + } + + public static void chatSystemSend(HashSet distroList, + String text, int channel, int messageType) { + // verify someone in distroList to send message to + if (distroList == null) + return; + if (distroList.size() < 1) + return; + + // Create message + ChatSystemMsg chatSystemMsg = new ChatSystemMsg(null, text); + chatSystemMsg.setChannel(channel); + chatSystemMsg.setMessageType(messageType); + + // Send dispatch to each player + + for (AbstractWorldObject abstractWorldObject : distroList) { + PlayerCharacter playerCharacter = (PlayerCharacter) abstractWorldObject; + Dispatch dispatch = Dispatch.borrow(playerCharacter, chatSystemMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + } + + // Get distroList for guild + public static HashSet getGuildList(Guild guild, PlayerCharacter source) { + HashSet distroList = new HashSet<>(); + + for (PlayerCharacter playerCharacter : SessionManager.getAllActivePlayerCharacters()) { + + if (Guild.sameGuild(playerCharacter.getGuild(), guild)) { + if (source != null && playerCharacter.isIgnoringPlayer(source)) + continue; // dont add if recip has ignored source + distroList.add(playerCharacter); + } + } + return distroList; + } + + // Get distroList for guild IC's + public static HashSet getGuildICList(Guild guild, PlayerCharacter source) { + + HashSet distroList = new HashSet<>(); + + for (PlayerCharacter pc : SessionManager.getAllActivePlayerCharacters()) { + + if (Guild.sameGuild(pc.getGuild(), guild)) + if (GuildStatusController.isInnerCouncil(pc.getGuildStatus())) { + if (source != null && pc.isIgnoringPlayer(source)) + continue; // dont add if recip has ignored source + distroList.add(pc); + } + } + return distroList; + } + + // Get distroList for group + public static HashSet getGroupList(Group group, PlayerCharacter source) { + HashSet distroList = new HashSet<>(); + Set players = group.getMembers(); + for (PlayerCharacter pc : players) { + if (source != null && pc.isIgnoringPlayer(source)) + continue; // dont add if recip has ignored source + distroList.add(pc); + } + return distroList; + } + + // Get distroList for nation + public static HashSet getNationList(Guild nation, PlayerCharacter source) { + HashSet distroList = new HashSet<>(); + + for (PlayerCharacter pc : SessionManager.getAllActivePlayerCharacters()) { + + Guild guild = pc.getGuild(); + + if (guild != null) + if (guild.getNation().getObjectUUID() == nation.getObjectUUID()) { + if (source != null && pc.isIgnoringPlayer(source)) + continue; // dont add if recip has ignored source + distroList.add(pc); + } + } + return distroList; + } + + public static ArrayList getNationListChat(Guild nation, PlayerCharacter source) { + ArrayList distroList = new ArrayList<>(); + + for (PlayerCharacter pc : SessionManager.getAllActivePlayerCharacters()) { + + Guild guild = pc.getGuild(); + + if (guild != null) + if (guild.getNation().getObjectUUID() == nation.getObjectUUID()) { + if (source != null && pc.isIgnoringPlayer(source)) + continue; // dont add if recip has ignored source + distroList.add(pc); + } + } + return distroList; + } + + // Get distroList for all players + public static HashSet getAllPlayers(PlayerCharacter source) { + + HashSet distroList = new HashSet<>(); + for (PlayerCharacter pc : SessionManager.getAllActivePlayerCharacters()) { + if (source != null && pc.isIgnoringPlayer(source)) + continue; // dont add if recip has ignored source + distroList.add(pc); + } + return distroList; + } + + // Get just self for distrList + public static HashSet getOwnPlayer(PlayerCharacter pc) { + if (pc == null) + return null; + HashSet distroList = new HashSet<>(); + distroList.add(pc); + return distroList; + } + + /* + * Utils + */ + // Error Message for type channel + private static void sendErrorMsgToPlayer(AbstractCharacter player, String message, + int channel) { + if (player == null) + return; + ChatManager.sendSystemMsgToPlayer(player, message, channel, 1); + } + + // Info Message for type channel + private static void sendInfoMsgToPlayer(AbstractCharacter player, String message, + int channel) { + ChatManager.sendSystemMsgToPlayer(player, message, channel, 2); + } + + // Message of the Day Message for type channel + // private void sendMOTDMsgToPlayer(AbstractCharacter player, String message, int channel) { + // this.sendSystemMsgToPlayer(player, message, channel, 3); + // } + private static void sendSystemMsgToPlayer(AbstractCharacter player, + String message, int channel, int messageType) { + + PlayerCharacter playerCharacter; + + if (player == null) + return; + + if (player.getObjectType().equals(GameObjectType.PlayerCharacter) == false) { + Logger.error("Chat message sent to non player"); + return; + } + + // Wtf recasting this? If we're sending chat messages to players + // or mobiles, then something is really wrong. + + playerCharacter = (PlayerCharacter) player; + + ChatSystemMsg chatSystemMsg = new ChatSystemMsg(null, message); + chatSystemMsg.setMessageType(messageType); // Error message + chatSystemMsg.setChannel(channel); + + Dispatch dispatch = Dispatch.borrow(playerCharacter, chatSystemMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + } + + private static boolean isDevCommand(String text) { + return text.startsWith(MBServerStatics.DEV_CMD_PREFIX); + } + + private static boolean isUpTimeRequest(String text) { + return text.equalsIgnoreCase("lua_uptime()"); + } + + private static boolean isNetStatRequest(String text) { + return text.equalsIgnoreCase("lua_netstat()"); + } + + private static boolean isPopulationRequest(String text) { + return text.equalsIgnoreCase("lua_population()"); + } + + private static boolean processDevCommand(AbstractWorldObject sender, String text) { + + if (sender.getObjectType().equals(GameObjectType.PlayerCharacter)) { + + PlayerCharacter pcSender = (PlayerCharacter) sender; + + // first remove the DEV_CMD_PREFIX + String[] words = text.split(MBServerStatics.DEV_CMD_PREFIX, 2); + + if (words[1].length() == 0) + return false; + + // next get the command + String[] commands = words[1].split(" ", 2); + String cmd = commands[0].toLowerCase(); + String cmdArgument = ""; + + if (commands.length > 1) + cmdArgument = commands[1].trim(); + + AbstractGameObject target = pcSender.getLastTarget(); + // return DevCmd.processDevCommand(pcSender, cmd, cmdArgument, + // target); + return DevCmdManager.handleDevCmd(pcSender, cmd, + cmdArgument, target); + } + return false; + } + + /** + * Process an Admin Command, which is a preset command sent from the client + */ + public static void HandleClientAdminCmd(ClientAdminCommandMsg data, + ClientConnection cc) { + + PlayerCharacter pcSender = SessionManager.getPlayerCharacter(cc); + + if (pcSender == null) + return; + + Account acct = SessionManager.getAccount(pcSender); + + if (acct == null) + return; + + // require minimal access to continue + // specific accessLevel checks performed by the DevCmdManager + if (acct.status.equals(Enum.AccountStatus.ADMIN) == false) { + Logger.warn(pcSender.getFirstName() + " Attempted to use a client admin command"); + //wtf? ChatManager.chatSystemInfo(pcSender, "CHEATER!!!!!!!!!!!!!"); + return; + } + + // First remove the initial slash + String d = data.getMsgCommand(); + String[] words = d.split("/", 2); + + if (words[1].length() == 0) + return; + + // Next get the command + String[] commands = words[1].split(" ", 2); + String cmd = commands[0].toLowerCase(); + String cmdArgument = ""; + + if (commands.length > 1) + cmdArgument = commands[1].trim(); + + AbstractGameObject target = data.getTarget(); + + // Map to a DevCmd + String devCmd = ""; + + if (cmd.compareTo("goto") == 0) + devCmd = "goto"; + else if (cmd.compareTo("suspend") == 0) + devCmd = "suspend"; + else if (cmd.compareTo("getinfo") == 0) + devCmd = "info"; + else if (devCmd.isEmpty()) { + Logger.info( "Unhandled admin command was used: /" + + cmd); + return; + } + + DevCmdManager.handleDevCmd(pcSender, devCmd, cmdArgument, + target); + } + + +} diff --git a/src/engine/gameManager/CombatManager.java b/src/engine/gameManager/CombatManager.java new file mode 100644 index 00000000..fffe09c8 --- /dev/null +++ b/src/engine/gameManager/CombatManager.java @@ -0,0 +1,1455 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.gameManager; + +import engine.Enum.*; +import engine.ai.MobileFSM.STATE; +import engine.exception.MsgSendException; +import engine.job.JobContainer; +import engine.job.JobScheduler; +import engine.jobs.AttackJob; +import engine.jobs.DeferredPowerJob; +import engine.math.Vector3fImmutable; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.*; +import engine.objects.*; +import engine.powers.DamageShield; +import engine.powers.PowersBase; +import engine.powers.effectmodifiers.AbstractEffectModifier; +import engine.powers.effectmodifiers.WeaponProcEffectModifier; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.util.HashSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; + +import static engine.math.FastMath.sqr; + +public enum CombatManager { + + COMBATMANAGER; + + /** + * Message sent by player to attack something. + */ + public static void setAttackTarget(AttackCmdMsg msg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter player; + int targetType; + AbstractWorldObject target; + + if (TargetedActionMsg.un2cnt == 60 || TargetedActionMsg.un2cnt == 70) { + return; + } + + player = SessionManager.getPlayerCharacter(origin); + + if (player == null) { + + return; + } + + //source must match player this account belongs to + if (player.getObjectUUID() != msg.getSourceID() || player.getObjectType().ordinal() != msg.getSourceType()) { + Logger.error("Msg Source ID " + msg.getSourceID() + " Does not Match Player ID " + player.getObjectUUID() ); + + return; + } + + targetType = msg.getTargetType(); + + if (targetType == GameObjectType.PlayerCharacter.ordinal()) { + target = PlayerCharacter.getFromCache(msg.getTargetID()); + } else if (targetType == GameObjectType.Building.ordinal()) { + target = BuildingManager.getBuildingFromCache(msg.getTargetID()); + } else if (targetType == GameObjectType.Mob.ordinal()) { + target = Mob.getFromCache(msg.getTargetID()); + }else{ + player.setCombatTarget(null); + return; //not valid type to attack + } + // quit of the combat target is already the current combat target + // or there is no combat target + if (target == null) { + return; + } + + //set sources target + player.setCombatTarget(target); + + //put in combat if not already + if (!player.isCombat()) { + toggleCombat(true, origin); + } + + //make character stand if sitting + if (player.isSit()) { + toggleSit(false, origin); + } + + AttackTarget(player, target); + + } + + public static void AttackTarget(PlayerCharacter pc, AbstractWorldObject target) { + + boolean swingOffhand = false; + + //check my weapon can I do an offhand attack + Item weaponOff = pc.getCharItemManager().getEquipped().get(MBServerStatics.SLOT_OFFHAND); + Item weaponMain = pc.getCharItemManager().getEquipped().get(MBServerStatics.SLOT_MAINHAND); + + // if you carry something in the offhand thats a weapon you get to swing it + if (weaponOff != null) { + if (weaponOff.getItemBase().getType().equals(ItemType.WEAPON)) { + swingOffhand = true; + } + } + // if you carry nothing in either hand you get to swing your offhand + if (weaponOff == null && weaponMain == null) { + swingOffhand = true; + } + + //we always swing our mainhand if we are not on timer + JobContainer main = pc.getTimers().get("Attack" + MBServerStatics.SLOT_MAINHAND); + if (main == null) { + // no timers on the mainhand, lets submit a job to swing + CombatManager.createTimer(pc, MBServerStatics.SLOT_MAINHAND, 1, true); // attack in 0.1 of a second + } + + if (swingOffhand) { + /* + only swing offhand if we have a weapon in it or are unarmed in both hands + and no timers running + */ + JobContainer off = pc.getTimers().get("Attack" + MBServerStatics.SLOT_OFFHAND); + if (off == null) { + CombatManager.createTimer(pc, MBServerStatics.SLOT_OFFHAND, 1, true); // attack in 0.1 of a second + } + } + } + + public static void setAttackTarget(PetAttackMsg msg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter player; + Mob pet; + int targetType; + AbstractWorldObject target; + + if (TargetedActionMsg.un2cnt == 60 || TargetedActionMsg.un2cnt == 70) + return; + + player = SessionManager.getPlayerCharacter(origin); + + if (player == null) + return; + + pet = player.getPet(); + + if (pet == null) + return; + + targetType = msg.getTargetType(); + + if (targetType == GameObjectType.PlayerCharacter.ordinal()) + target = PlayerCharacter.getFromCache(msg.getTargetID()); + else if (targetType == GameObjectType.Building.ordinal()) + target = BuildingManager.getBuildingFromCache(msg.getTargetID()); + else if (targetType == GameObjectType.Mob.ordinal()) + target = Mob.getFromCache(msg.getTargetID()); + else { + pet.setCombatTarget(null); + return; //not valid type to attack + } + + if (pet.equals(target)) + return; + + // quit of the combat target is already the current combat target + // or there is no combat target + + if (target == null || target == pet.getCombatTarget()) + return; + + + + //set sources target + pet.setCombatTarget(target); + pet.setState(STATE.Attack); + // setFirstHitCombatTarget(player,target); + + //put in combat if not already + if (!pet.isCombat()) + pet.setCombat(true); + + //make character stand if sitting + if (pet.isSit()) + toggleSit(false, origin); + + } + + private static void removeAttackTimers(AbstractCharacter ac) { + + JobContainer main; + JobContainer off; + + if (ac == null) + return; + + main = ac.getTimers().get("Attack" + MBServerStatics.SLOT_MAINHAND); + off = ac.getTimers().get("Attack" + MBServerStatics.SLOT_OFFHAND); + + if (main != null) + JobScheduler.getInstance().cancelScheduledJob(main); + + ac.getTimers().remove("Attack" + MBServerStatics.SLOT_MAINHAND); + + if (off != null) + JobScheduler.getInstance().cancelScheduledJob(off); + + ac.getTimers().remove("Attack" + MBServerStatics.SLOT_OFFHAND); + + ac.setCombatTarget(null); + + } + + /** + * Begin Attacking + */ + public static void doCombat(AbstractCharacter ac, int slot) { + + int ret = 0; + + if (ac == null) + return; + + // Attempt to eat null targets until we can clean + // up this unholy mess and refactor it into a thread. + + + ret = attemptCombat(ac, slot); + + //handle pets + if (ret < 2 && ac.getObjectType().equals(GameObjectType.Mob)) { + Mob mob = (Mob) ac; + if (mob.isPet()) { + return; + } + } + + //ret values + //0: not valid attack, fail attack + //1: cannot attack, wrong hand + //2: valid attack + //3: cannot attack currently, continue checking + + if (ret == 0 || ret == 1) { + + //Could not attack, clear timer + + ConcurrentHashMap timers = ac.getTimers(); + + if (timers != null && timers.containsKey("Attack" + slot)) + timers.remove("Attack" + slot); + + //clear combat target if not valid attack + if (ret == 0) + ac.setCombatTarget(null); + + } else if (ret == 3) { + //Failed but continue checking. reset timer + createTimer(ac, slot, 5, false); + } + } + + /** + * Verify can attack target + */ + private static int attemptCombat(AbstractCharacter abstractCharacter, int slot) { + + if (abstractCharacter == null) { + // debugCombat(ac, "Source is null"); + return 0; + } + + try { + //Make sure player can attack + PlayerBonuses bonus = abstractCharacter.getBonuses(); + + if (bonus != null && bonus.getBool(ModType.ImmuneToAttack, SourceType.None)) + return 0; + + AbstractWorldObject target = abstractCharacter.getCombatTarget(); + + if (target == null){ + return 0; + } + + + //target must be valid type + if (AbstractWorldObject.IsAbstractCharacter(target)) { + AbstractCharacter tar = (AbstractCharacter) target; + //must be alive, attackable and in World + if (!tar.isAlive()) { + return 0; + } + else if (tar.isSafeMode()) { + return 0; + } + else if (!tar.isActive()) { + return 0; + } + + if (target.getObjectType().equals(GameObjectType.PlayerCharacter) && abstractCharacter.getObjectType().equals(GameObjectType.PlayerCharacter) && abstractCharacter.getTimers().get("Attack" + slot) == null) { + if (!((PlayerCharacter) abstractCharacter).canSee((PlayerCharacter) target)) { + return 0; + } + } + + //must not be immune to all or immune to attack + Resists res = tar.getResists(); + bonus = tar.getBonuses(); + if (bonus != null && !bonus.getBool(ModType.NoMod, SourceType.ImmuneToAttack)) { + if (res != null) { + if (res.immuneToAll() || res.immuneToAttacks()) { + return 0; + } + } + } + } + else if (target.getObjectType().equals(GameObjectType.Building)) { + Building tar = (Building) target; + + // Cannot attack an invuln building + + if (tar.isVulnerable() == false) { + return 0; + } + + } + else { + return 0; //only characters and buildings may be attacked + } + + //source must be in world and alive + if (!abstractCharacter.isActive()) { + return 0; + } + else if (!abstractCharacter.isAlive()) { + return 0; + } + + //make sure source is in combat mode + if (!abstractCharacter.isCombat()) { + return 0; + } + + //See if either target is in safe zone + if (abstractCharacter.getObjectType().equals(GameObjectType.PlayerCharacter) && target.getObjectType().equals(GameObjectType.PlayerCharacter)) { + if (((PlayerCharacter) abstractCharacter).inSafeZone() || ((PlayerCharacter) target).inSafeZone()) { + return 0; + } + } + + if (!(slot == MBServerStatics.SLOT_MAINHAND || slot == MBServerStatics.SLOT_OFFHAND)) { + return 0; + } + + if (abstractCharacter.getCharItemManager() == null) { + return 0; + } + + //get equippment + ConcurrentHashMap equipped = abstractCharacter.getCharItemManager().getEquipped(); + boolean hasNoWeapon = false; + + if (equipped == null) { + return 0; + } + + //get Weapon + boolean isWeapon = true; + Item weapon = equipped.get(slot); + ItemBase wb = null; + if (weapon == null) { + isWeapon = false; + } + else { + ItemBase ib = weapon.getItemBase(); + if (ib == null || !ib.getType().equals(ItemType.WEAPON)) { + isWeapon = false; + } + else { + wb = ib; + } + } + + //if no weapon, see if other hand has a weapon + if (!isWeapon) { + //no weapon, see if other hand has a weapon + if (slot == MBServerStatics.SLOT_MAINHAND) { + //make sure offhand has weapon, not shield + Item weaponOff = equipped.get(MBServerStatics.SLOT_OFFHAND); + if (weaponOff != null) { + ItemBase ib = weaponOff.getItemBase(); + if (ib == null || !ib.getType().equals(ItemType.WEAPON)) { + hasNoWeapon = true; + } + else { + // debugCombat(ac, "mainhand, weapon in other hand"); + return 1; //no need to attack with this hand + } + } + else { + hasNoWeapon = true; + } + } + else { + if (equipped.get(MBServerStatics.SLOT_MAINHAND) == null) { + // debgCombat(ac, "offhand, weapon in other hand"); + return 1; //no need to attack with this hand + } + } + } + + //Source can attack. + //NOTE Don't 'return;' beyond this point until timer created + boolean attackFailure = false; + + //Target can't attack on move with ranged weapons. + if ((wb != null) && (wb.getRange() > 35f) && abstractCharacter.isMoving()) { + // debugCombat(ac, "Cannot attack with throwing weapon while moving"); + attackFailure = true; + } + + //if not enough stamina, then skip attack + if (wb == null) { + if (abstractCharacter.getStamina() < 1) { + // debugCombat(ac, "Not enough stamina to attack"); + attackFailure = true; + } + } + else if (abstractCharacter.getStamina() < wb.getWeight()) { + // debugCombat(ac, "Not enough stamina to attack"); + attackFailure = true; + } + + //skipping for now to test out mask casting. + // //if attacker is casting, then skip this attack + // if (ac.getLastPower() != null) { + // debugCombat(ac, "Cannot attack, curently casting"); + // attackFailure = true; + // } + //see if attacker is stunned. If so, stop here + bonus = abstractCharacter.getBonuses(); + if (bonus != null && bonus.getBool(ModType.Stunned,SourceType.None)) { + // debugCombat(ac, "Cannot attack while stunned"); + attackFailure = true; + } + + //Get Range of weapon + float range; + if (hasNoWeapon) { + range = MBServerStatics.NO_WEAPON_RANGE; + } + else { + range = getWeaponRange(wb); + if (bonus != null){ + float buffRange = 1; + buffRange += bonus.getFloat(ModType.WeaponRange, SourceType.None) *.01f; + range*= buffRange; + } + } + + if (abstractCharacter.getObjectType() == GameObjectType.Mob) { + Mob minion = (Mob) abstractCharacter; + if (minion.isSiege()) { + range = 300f; + } + } + + //Range check. + if (NotInRange(abstractCharacter, target, range)) { + //target is in stealth and can't be seen by source + if (target.getObjectType().equals(GameObjectType.PlayerCharacter) && abstractCharacter.getObjectType().equals(GameObjectType.PlayerCharacter)) { + if (!((PlayerCharacter) abstractCharacter).canSee((PlayerCharacter) target)) { + // debugCombat(ac, "cannot see target."); + return 0; + } + } + attackFailure = true; + } + + //handle pet, skip timers (handled by AI) + if (abstractCharacter.getObjectType().equals(GameObjectType.Mob)) { + Mob mob = (Mob) abstractCharacter; + if (mob.isPet()) { + attack(abstractCharacter, target, weapon, wb, (slot == MBServerStatics.SLOT_MAINHAND) ? true : false); + return 2; + } + } + + //TODO Verify attacker has los (if not ranged weapon). + if (!attackFailure) { + if (hasNoWeapon || abstractCharacter.getObjectType().equals(GameObjectType.Mob)) { + createTimer(abstractCharacter, slot, 20, true); //2 second for no weapon + } + else { + int wepSpeed = (int) (wb.getSpeed()); + if (weapon != null && weapon.getBonusPercent(ModType.WeaponSpeed, SourceType.None) != 0f) //add weapon speed bonus + { + wepSpeed *= (1 + weapon.getBonus(ModType.WeaponSpeed, SourceType.None)); + } + if (abstractCharacter.getBonuses() != null && abstractCharacter.getBonuses().getFloatPercentAll(ModType.AttackDelay, SourceType.None) != 0f) //add effects speed bonus + { + wepSpeed *= (1 + abstractCharacter.getBonuses().getFloatPercentAll(ModType.AttackDelay, SourceType.None)); + } + if (wepSpeed < 10) { + wepSpeed = 10; //Old was 10, but it can be reached lower with legit buffs,effects. + } + createTimer(abstractCharacter, slot, wepSpeed, true); + } + + if (target == null) + return 0; + + attack(abstractCharacter, target, weapon, wb, (slot == MBServerStatics.SLOT_MAINHAND) ? true : false); + } + else { + // changed this to half a second to make combat attempts more aggressive than movement sync + createTimer(abstractCharacter, slot, 5, false); //0.5 second timer if attack fails + //System.out.println("Attack attempt failed"); + } + + } catch(Exception e) { + return 0; + } + return 2; + } + + private static void debugCombat(AbstractCharacter ac, String reason) { + if (ac == null) { + return; + } + + //if DebugMeleeSync is on, then debug reason for melee failure + if (ac.getDebug(64)) { + if (ac.getObjectType().equals(GameObjectType.PlayerCharacter)) { + String out = "Attack Failure: " + reason; + ChatManager.chatSystemInfo((PlayerCharacter) ac, out); + } + } + } + + private static void debugCombatRange(AbstractCharacter ac, Vector3fImmutable sl, Vector3fImmutable tl, float range, float distance) { + if (ac == null || sl == null || tl == null) { + return; + } + + //if DebugMeleeSync is on, then debug reason for melee failure + if (ac.getDebug(64)) { + if (ac.getObjectType().equals(GameObjectType.PlayerCharacter)) { + String out = "Attack Failure: Out of Range: Range: " + distance + ", weaponRange: " + range; + out += ", sourceLoc: " + sl.x + ", " + sl.y + ", " + sl.z; + out += ", targetLoc: " + tl.x + ", " + tl.y + ", " + tl.z; + ChatManager.chatSystemInfo((PlayerCharacter) ac, out); + } + } + } + + private static void createTimer(AbstractCharacter ac, int slot, int time, boolean success) { + ConcurrentHashMap timers = ac.getTimers(); + if (timers != null) { + AttackJob aj = new AttackJob(ac, slot, success); + JobContainer job; + job = JobScheduler.getInstance().scheduleJob(aj, (time * 100)); + timers.put("Attack" + slot, job); + } else { + Logger.error( "Unable to find Timers for Character " + ac.getObjectUUID()); + } + } + + /** + * Attempt to attack target + */ + private static void attack(AbstractCharacter ac, AbstractWorldObject target, Item weapon, ItemBase wb, boolean mainHand) { + + float atr; + int minDamage, maxDamage; + int errorTrack = 0; + + try { + + if (ac == null) + return; + + if (target == null) + return; + + if (mainHand) { + atr = ac.getAtrHandOne(); + minDamage = ac.getMinDamageHandOne(); + maxDamage = ac.getMaxDamageHandOne(); + } + else { + atr = ac.getAtrHandTwo(); + minDamage = ac.getMinDamageHandTwo(); + maxDamage = ac.getMaxDamageHandTwo(); + } + + boolean tarIsRat = false; + + if (target.getObjectTypeMask() == MBServerStatics.MASK_RAT) + tarIsRat = true; + else if (target.getObjectType() == GameObjectType.PlayerCharacter){ + PlayerCharacter pTar = (PlayerCharacter)target; + for (Effect eff: pTar.getEffects().values()){ + if (eff.getPowerToken() == 429513599 || eff.getPowerToken() == 429415295){ + tarIsRat = true; + } + } + } + + //Dont think we need to do this anymore. + if (tarIsRat){ + //strip away current % dmg buffs then add with rat % + if (ac.getBonuses().getFloatPercentAll(ModType.Slay, SourceType.Rat) != 0){ + + + float percent = 1 + ac.getBonuses().getFloatPercentAll(ModType.Slay, SourceType.Rat); + + minDamage *= percent; + maxDamage *= percent; + } + + } + + errorTrack = 1; + + //subtract stamina + if (wb == null) { + ac.modifyStamina(-0.5f, ac, true); + } + else { + float stam = wb.getWeight() / 3; + stam = (stam < 1) ? 1 : stam; + ac.modifyStamina(-(stam), ac, true); + } + + ac.cancelOnAttackSwing(); + + errorTrack = 2; + + //set last time this player has attacked something. + if (target.getObjectType().equals(GameObjectType.PlayerCharacter) && target.getObjectUUID() != ac.getObjectUUID() && ac.getObjectType() == GameObjectType.PlayerCharacter) { + ac.setTimeStamp("LastCombatPlayer", System.currentTimeMillis()); + ((PlayerCharacter) target).setTimeStamp("LastCombatPlayer", System.currentTimeMillis()); + } + else { + ac.setTimeStamp("LastCombatMob", System.currentTimeMillis()); + } + + errorTrack = 3; + + //Get defense for target + float defense; + if (target.getObjectType().equals(GameObjectType.Building)) { + + if (BuildingManager.getBuildingFromCache(target.getObjectUUID()) == null){ + ac.setCombatTarget(null); + return; + } + defense = 0; + + Building building = (Building)target; + if (building.getParentZone() != null && building.getParentZone().isPlayerCity()){ + + if (System.currentTimeMillis() > building.getTimeStamp("CallForHelp")){ + building.getTimestamps().put("CallForHelp", System.currentTimeMillis() + 15000); + int count = 0; + for (Mob mob:building.getParentZone().zoneMobSet){ + if (!mob.isPlayerGuard()) + continue; + if (mob.getCombatTarget() != null) + continue; + if (mob.getGuild() != null && building.getGuild() != null) + if (!Guild.sameGuild(mob.getGuild().getNation(), building.getGuild().getNation())) + continue; + + if (mob.getLoc().distanceSquared2D(building.getLoc()) > sqr(300)) + continue; + + if (count == 5) + count++; + + mob.setCombatTarget(ac); + mob.setState(STATE.Attack); + } + } + } + } + else { + AbstractCharacter tar = (AbstractCharacter) target; + defense = tar.getDefenseRating(); + //Handle target attacking back if in combat and has no other target + handleRetaliate(tar, ac); + } + + errorTrack = 4; + + //Get hit chance + int chance; + float dif = atr - defense; + if (dif > 100) { + chance = 94; + } + else if (dif < -100) { + chance = 4; + } + else { + chance = (int) ((0.45 * dif) + 49); + } + + errorTrack = 5; + + //calculate hit/miss + int roll = ThreadLocalRandom.current().nextInt(100); + DeferredPowerJob dpj = null; + if (roll < chance) { + if (ac.getObjectType().equals(GameObjectType.PlayerCharacter)) { + updateAttackTimers((PlayerCharacter) ac, target, true); + } + + boolean skipPassives = false; + PlayerBonuses bonuses = ac.getBonuses(); + if (bonuses != null && bonuses.getBool(ModType.IgnorePassiveDefense, SourceType.None)) { + skipPassives = true; + } + + AbstractCharacter tarAc = null; + if (AbstractWorldObject.IsAbstractCharacter(target)) { + tarAc = (AbstractCharacter) target; + } + + errorTrack = 6; + + // Apply Weapon power effect if any. don't try to apply twice if + // dual wielding. Perform after passive test for sync purposes. + + + if (ac.getObjectType().equals(GameObjectType.PlayerCharacter) && (mainHand || wb.isTwoHanded())) { + dpj = ((PlayerCharacter) ac).getWeaponPower(); + if (dpj != null) { + float attackRange = getWeaponRange(wb); + + dpj.attack(target, attackRange); + + if (dpj.getPower() != null && (dpj.getPowerToken() == -1851459567 || dpj.getPowerToken() == -1851489518)) + ((PlayerCharacter)ac).setWeaponPower(dpj); + } + } + //check to apply second backstab. + if (ac.getObjectType().equals(GameObjectType.PlayerCharacter) && !mainHand){ + dpj = ((PlayerCharacter) ac).getWeaponPower(); + if (dpj != null && dpj.getPower() != null && (dpj.getPowerToken() == -1851459567 || dpj.getPowerToken() == -1851489518)) { + float attackRange = getWeaponRange(wb); + dpj.attack(target, attackRange); + } + } + + errorTrack = 7; + + //Hit, check if passive kicked in + boolean passiveFired = false; + if (!skipPassives && tarAc != null) { + if (target.getObjectType().equals(GameObjectType.PlayerCharacter)) { + + //Handle Block passive + if (testPassive(ac, tarAc, "Block") && canTestBlock(ac, target)) { + + if (!target.isAlive()) + return; + + sendPassiveDefenseMessage(ac, wb, target, MBServerStatics.COMBAT_SEND_BLOCK, dpj,mainHand); + passiveFired = true; + } + + //Handle Parry passive + if (!passiveFired) { + if (canTestParry(ac, target) && testPassive(ac, tarAc, "Parry")) { + if (!target.isAlive()) + return; + sendPassiveDefenseMessage(ac, wb, target, MBServerStatics.COMBAT_SEND_PARRY, dpj,mainHand); + passiveFired = true; + } + } + } + + errorTrack = 8; + + //Handle Dodge passive + if (!passiveFired) { + if (testPassive(ac, tarAc, "Dodge")) { + + if (!target.isAlive()) + return; + + sendPassiveDefenseMessage(ac, wb, target, MBServerStatics.COMBAT_SEND_DODGE, dpj,mainHand); + passiveFired = true; + } + } + } + + //return if passive (Block, Parry, Dodge) fired + + if (passiveFired) + return; + + errorTrack = 9; + + //Hit and no passives + //if target is player, set last attack timestamp + if (target.getObjectType().equals(GameObjectType.PlayerCharacter)) { + updateAttackTimers((PlayerCharacter) target, ac, false); + } + + //Get damage Type + DamageType damageType; + if (wb != null) { + damageType = wb.getDamageType(); + } + else if (ac.getObjectType().equals(GameObjectType.Mob) && ((Mob) ac).isSiege()) { + damageType = DamageType.Siege; + } + else { + damageType = DamageType.Crush; + } + + errorTrack = 10; + + //Get target resists + Resists resists = null; + + if (tarAc != null) { + resists = tarAc.getResists(); + } + else if (target.getObjectType().equals(GameObjectType.Building)) { + resists = ((Building) target).getResists(); + } + + //make sure target is not immune to damage type; + if (resists != null && resists.immuneTo(damageType)) { + sendCombatMessage(ac, target, 0f, wb, dpj,mainHand); + return; + } + + // PowerProjectileMsg ppm = new PowerProjectileMsg(ac,tarAc); + // DispatchMessage.dispatchMsgToInterestArea(ac, ppm, DispatchChannel.SECONDARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); + // + + errorTrack = 11; + + //Calculate Damage done + + float damage; + + if (wb != null) { + damage = calculateDamage(ac, tarAc, minDamage, maxDamage, damageType, resists); + } + else { + damage = calculateDamage(ac, tarAc, minDamage, maxDamage, damageType, resists); + } + + float d = 0f; + + errorTrack = 12; + + //Subtract Damage from target's health + if (tarAc != null) { + if (tarAc.isSit()) { + damage *= 2.5f; //increase damage if sitting + } + if (tarAc.getObjectType() == GameObjectType.Mob) { + ac.setHateValue(damage * MBServerStatics.PLAYER_COMBAT_HATE_MODIFIER); + ((Mob) tarAc).handleDirectAggro(ac); + } + + if (tarAc.getHealth() > 0) + d = tarAc.modifyHealth(-damage, ac, false); + + } + else if (target.getObjectType().equals(GameObjectType.Building)) { + + if (BuildingManager.getBuildingFromCache(target.getObjectUUID()) == null){ + ac.setCombatTarget(null); + return; + } + if (target.getHealth() > 0) + d = ((Building) target).modifyHealth(-damage, ac); + } + + errorTrack = 13; + + //Test to see if any damage needs done to weapon or armor + testItemDamage(ac, target, weapon, wb); + + // if target is dead, we got the killing blow, remove attack timers on our weapons + if (tarAc != null && !tarAc.isAlive()) { + removeAttackTimers(ac); + } + + //test double death fix + if (d != 0) { + sendCombatMessage(ac, target, damage, wb, dpj,mainHand); //send damage message + } + + errorTrack = 14; + + //handle procs + if (weapon != null && tarAc != null && tarAc.isAlive()) { + ConcurrentHashMap effects = weapon.getEffects(); + for (Effect eff : effects.values()) { + if (eff == null) { + continue; + } + HashSet aems = eff.getEffectModifiers(); + if (aems != null) { + for (AbstractEffectModifier aem : aems) { + if (!tarAc.isAlive()) { + break; + } + if (aem instanceof WeaponProcEffectModifier) { + int procChance = ThreadLocalRandom.current().nextInt(100); + if (procChance < MBServerStatics.PROC_CHANCE) { + ((WeaponProcEffectModifier) aem).applyProc(ac, target); + } + } + } + } + } + } + + errorTrack = 15; + + //handle damage shields + if (ac.isAlive() && tarAc != null && tarAc.isAlive()) { + handleDamageShields(ac, tarAc, damage); + } + } + else { + int animationOverride = 0; + // Apply Weapon power effect if any. + // don't try to apply twice if dual wielding. + if (ac.getObjectType().equals(GameObjectType.PlayerCharacter) && (mainHand || wb.isTwoHanded())) { + dpj = null; + dpj = ((PlayerCharacter) ac).getWeaponPower(); + + if (dpj != null) { + PowersBase wp = dpj.getPower(); + if (wp.requiresHitRoll() == false) { + float attackRange = getWeaponRange(wb); + dpj.attack(target,attackRange); + } + else { + ((PlayerCharacter) ac).setWeaponPower(null); + } + + } + } + if (target.getObjectType() == GameObjectType.Mob) { + ((Mob) target).handleDirectAggro(ac); + } + + errorTrack = 17; + + //miss, Send miss message + sendCombatMessage(ac, target, 0f, wb, dpj,mainHand); + + //if attacker is player, set last attack timestamp + if (ac.getObjectType().equals(GameObjectType.PlayerCharacter)) { + updateAttackTimers((PlayerCharacter) ac, target, true); + } + } + + errorTrack = 18; + + //cancel effects that break on attack or attackSwing + ac.cancelOnAttack(); + + } catch (Exception e) { + Logger.error(ac.getName() + ' ' + errorTrack + ' ' + e.toString()); + } + } + + public static boolean canTestParry(AbstractCharacter ac, AbstractWorldObject target) { + + if (ac == null || target == null || !AbstractWorldObject.IsAbstractCharacter(target)) + return false; + + AbstractCharacter tar = (AbstractCharacter) target; + + CharacterItemManager acItem = ac.getCharItemManager(); + CharacterItemManager tarItem = tar.getCharItemManager(); + + if (acItem == null || tarItem == null) + return false; + + Item acMain = acItem.getItemFromEquipped(1); + Item acOff = acItem.getItemFromEquipped(2); + Item tarMain = tarItem.getItemFromEquipped(1); + Item tarOff = tarItem.getItemFromEquipped(2); + + return !isRanged(acMain) && !isRanged(acOff) && !isRanged(tarMain) && !isRanged(tarOff); + } + + public static boolean canTestBlock(AbstractCharacter ac, AbstractWorldObject target) { + + if (ac == null || target == null || !AbstractWorldObject.IsAbstractCharacter(target)) + return false; + + AbstractCharacter tar = (AbstractCharacter) target; + + CharacterItemManager acItem = ac.getCharItemManager(); + CharacterItemManager tarItem = tar.getCharItemManager(); + + if (acItem == null || tarItem == null) + return false; + + + + Item tarOff = tarItem.getItemFromEquipped(2); + + + if (tarOff == null) + return false; + + return tarOff.getItemBase().isShield() != false; + } + + private static boolean isRanged(Item item) { + + if (item == null) + return false; + + ItemBase ib = item.getItemBase(); + + if (ib == null) + return false; + + if (ib.getType().equals(ItemType.WEAPON) == false) + return false; + + return ib.getRange() > MBServerStatics.RANGED_WEAPON_RANGE; + + + } + + private static float calculateDamage(AbstractCharacter source, AbstractCharacter target, float minDamage, float maxDamage, DamageType damageType, Resists resists) { + //get range between min and max + float range = maxDamage - minDamage; + + //Damage is calculated twice to average a more central point + float damage = ThreadLocalRandom.current().nextFloat() * range; + damage = (damage + (ThreadLocalRandom.current().nextFloat() * range)) *.5f; + + //put it back between min and max + damage += minDamage; + + //calculate resists in if any + if (resists != null) { + return resists.getResistedDamage(source, target, damageType, damage, 0); + } else { + return damage; + } + } + + private static void sendPassiveDefenseMessage(AbstractCharacter source, ItemBase wb, AbstractWorldObject target, int passiveType, DeferredPowerJob dpj, boolean mainHand) { + + int swingAnimation = getSwingAnimation(wb, dpj,mainHand); + + if (dpj != null){ + if(PowersManager.AnimationOverrides.containsKey(dpj.getAction().getEffectID())) + swingAnimation = PowersManager.AnimationOverrides.get(dpj.getAction().getEffectID()); + } + TargetedActionMsg cmm = new TargetedActionMsg(source,swingAnimation, target, passiveType); + DispatchMessage.sendToAllInRange(target, cmm); + + } + + private static void sendCombatMessage(AbstractCharacter source, AbstractWorldObject target, float damage, ItemBase wb, DeferredPowerJob dpj, boolean mainHand) { + + int swingAnimation = getSwingAnimation(wb, dpj,mainHand); + + if (dpj != null){ + if(PowersManager.AnimationOverrides.containsKey(dpj.getAction().getEffectID())) + swingAnimation = PowersManager.AnimationOverrides.get(dpj.getAction().getEffectID()); + } + + if (source.getObjectType() == GameObjectType.PlayerCharacter){ + for (Effect eff: source.getEffects().values()){ + if (eff.getPower() != null && (eff.getPower().getToken() == 429506943 || eff.getPower().getToken() == 429408639 || eff.getPower().getToken() == 429513599 ||eff.getPower().getToken() == 429415295)) + swingAnimation = 0; + } + } + TargetedActionMsg cmm = new TargetedActionMsg(source, target, damage, swingAnimation); + DispatchMessage.sendToAllInRange(target, cmm); + } + + public static int animation = 0; + + public static int getSwingAnimation(ItemBase wb, DeferredPowerJob dpj, boolean mainHand) { + int token = 0; + if (dpj != null) { + token = (dpj.getPower() != null) ? dpj.getPower().getToken() : 0; + } + + if (token == 563721004) //kick animation + { + return 79; + } + + if (CombatManager.animation != 0) { + return CombatManager.animation; + } + + if (wb == null) { + return 75; + } + if (mainHand){ + if (wb.getAnimations().size() > 0){ + int animation = wb.getAnimations().get(0); + int random = ThreadLocalRandom.current().nextInt(wb.getAnimations().size()); + try{ + animation = wb.getAnimations().get(random); + return animation; + }catch(Exception e){ + Logger.error( e.getMessage()); + return wb.getAnimations().get(0); + + } + + }else if (wb.getOffHandAnimations().size() > 0){ + int animation = wb.getOffHandAnimations().get(0); + int random = ThreadLocalRandom.current().nextInt(wb.getOffHandAnimations().size()); + try{ + animation = wb.getOffHandAnimations().get(random); + return animation; + }catch(Exception e){ + Logger.error( e.getMessage()); + return wb.getOffHandAnimations().get(0); + + } + } + }else{ + if (wb.getOffHandAnimations().size() > 0){ + int animation = wb.getOffHandAnimations().get(0); + int random = ThreadLocalRandom.current().nextInt(wb.getOffHandAnimations().size()); + try{ + animation = wb.getOffHandAnimations().get(random); + return animation; + }catch(Exception e){ + Logger.error( e.getMessage()); + return wb.getOffHandAnimations().get(0); + + } + }else + if (wb.getAnimations().size() > 0){ + int animation = wb.getAnimations().get(0); + int random = ThreadLocalRandom.current().nextInt(wb.getAnimations().size()); + try{ + animation = wb.getAnimations().get(random); + return animation; + }catch(Exception e){ + Logger.error( e.getMessage()); + return wb.getAnimations().get(0); + + } + + } + } + + + String required = wb.getSkillRequired(); + String mastery = wb.getMastery(); + if (required.equals("Unarmed Combat")) { + return 75; + } else if (required.equals("Sword")) { + if (wb.isTwoHanded()) { + return 105; + } else { + return 98; + } + } else if (required.equals("Staff") || required.equals("Pole Arm")) { + return 85; + } else if (required.equals("Spear")) { + return 92; + } else if (required.equals("Hammer") || required.equals("Axe")) { + if (wb.isTwoHanded()) { + return 105; + } else if (mastery.equals("Throwing")) { + return 115; + } else { + return 100; + } + } else if (required.equals("Dagger")) { + if (mastery.equals("Throwing")) { + return 117; + } else { + return 81; + } + } else if (required.equals("Crossbow")) { + return 110; + } else if (required.equals("Bow")) { + return 109; + } else if (wb.isTwoHanded()) { + return 105; + } else { + return 100; + } + } + + private static boolean testPassive(AbstractCharacter source, AbstractCharacter target, String type) { + + float chance = target.getPassiveChance(type, source.getLevel(), true); + + if (chance == 0f) + return false; + + + //max 75% chance of passive to fire + if (chance > 75f) + chance = 75f; + + int roll = ThreadLocalRandom.current().nextInt(100); + + //Passive fired + //Passive did not fire + return roll < chance; + + } + + private static void updateAttackTimers(PlayerCharacter pc, AbstractWorldObject target, boolean attack) { + + //Set Attack Timers + if (target.getObjectType().equals(GameObjectType.PlayerCharacter)) + pc.setLastPlayerAttackTime(); + else + pc.setLastMobAttackTime(); + } + + public static float getWeaponRange(ItemBase weapon) { + if (weapon == null) + return 0f; + + return weapon.getRange(); + } + + public static void toggleCombat(ToggleCombatMsg msg, ClientConnection origin) { + toggleCombat(msg.toggleCombat(), origin); + } + + public static void toggleCombat(SetCombatModeMsg msg, ClientConnection origin) { + toggleCombat(msg.getToggle(), origin); + } + + private static void toggleCombat(boolean toggle, ClientConnection origin) { + + PlayerCharacter pc = SessionManager.getPlayerCharacter(origin); + + if (pc == null) + return; + + pc.setCombat(toggle); + + if (!toggle) // toggle is move it to false so clear combat target + pc.setCombatTarget(null); //clear last combat target + + UpdateStateMsg rwss = new UpdateStateMsg(); + rwss.setPlayer(pc); + DispatchMessage.dispatchMsgToInterestArea(pc, rwss, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); + } + + private static void toggleSit(boolean toggle, ClientConnection origin) { + + PlayerCharacter pc = SessionManager.getPlayerCharacter(origin); + + if (pc == null) + return; + + pc.setSit(toggle); + + UpdateStateMsg rwss = new UpdateStateMsg(); + rwss.setPlayer(pc); + DispatchMessage.dispatchMsgToInterestArea(pc, rwss, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true,false); + } + + public static boolean NotInRange(AbstractCharacter ac, AbstractWorldObject target, float range) { + Vector3fImmutable sl = ac.getLoc(); + Vector3fImmutable tl = target.getLoc(); + //add Hitbox's to range. + range += (calcHitBox(ac) + calcHitBox(target)); + + float magnitudeSquared = tl.distanceSquared(sl); + + return magnitudeSquared > range * range; + + } + + //Called when character takes damage. + public static void handleRetaliate(AbstractCharacter tarAc, AbstractCharacter ac) { + if (ac == null || tarAc == null) { + return; + } + if (ac.equals(tarAc)) { + return; + } + + if (tarAc.isMoving() && tarAc.getObjectType().equals(GameObjectType.PlayerCharacter)) + return; + + if (!tarAc.isAlive() || !ac.isAlive()) + return; + boolean isCombat = tarAc.isCombat(); + //If target in combat and has no target, then attack back + AbstractWorldObject awoCombTar = tarAc.getCombatTarget(); + if ((tarAc.isCombat() && awoCombTar == null) || (isCombat && awoCombTar != null && (!awoCombTar.isAlive() ||tarAc.isCombat() && NotInRange(tarAc, awoCombTar, tarAc.getRange()))) || (tarAc != null && tarAc.getObjectType() == GameObjectType.Mob && ((Mob) tarAc).isSiege())) { + // we are in combat with no valid target + if (tarAc.getObjectType().equals(GameObjectType.PlayerCharacter)) { + PlayerCharacter pc = (PlayerCharacter) tarAc; + tarAc.setCombatTarget(ac); + pc.setLastTarget(ac.getObjectType(), ac.getObjectUUID()); + if (tarAc.getTimers() != null) { + if (!tarAc.getTimers().containsKey("Attack" + MBServerStatics.SLOT_MAINHAND)) { + CombatManager.AttackTarget((PlayerCharacter) tarAc, tarAc.getCombatTarget()); + } + } + } + } + + //Handle pet retaliate if assist is on and pet doesn't have a target. + if (tarAc.getObjectType().equals(GameObjectType.PlayerCharacter)) { + Mob pet = ((PlayerCharacter) tarAc).getPet(); + if (pet != null && pet.assist() && pet.getCombatTarget() == null) { + pet.setCombatTarget(ac); + pet.setState(STATE.Retaliate); + } + } + + //Handle Mob Retaliate. + if (tarAc.getObjectType() == GameObjectType.Mob) { + Mob retaliater = (Mob) tarAc; + if (retaliater.getCombatTarget() != null && !retaliater.isSiege()) + return; + if (ac.getObjectType() == GameObjectType.Mob && retaliater.isSiege()) + return; + retaliater.setCombatTarget(ac); + retaliater.setState(STATE.Retaliate); + + } + } + + public static void handleDamageShields(AbstractCharacter ac, AbstractCharacter target, float damage) { + if (ac == null || target == null) { + return; + } + PlayerBonuses bonuses = target.getBonuses(); + if (bonuses != null) { + ConcurrentHashMap damageShields = bonuses.getDamageShields(); + float total = 0; + for (DamageShield ds : damageShields.values()) { + //get amount to damage back + float amount; + if (ds.usePercent()) { + amount = damage * ds.getAmount() / 100; + } else { + amount = ds.getAmount(); + } + + //get resisted damage for damagetype + Resists resists = ac.getResists(); + if (resists != null) { + amount = resists.getResistedDamage(target, ac, ds.getDamageType(), amount, 0); + } + + total += amount; + } + if (total > 0) { + //apply Damage back + ac.modifyHealth(-total, target, true); + + TargetedActionMsg cmm = new TargetedActionMsg(ac,ac, total, 0); + DispatchMessage.sendToAllInRange(target, cmm); + + } + } + } + + public static float calcHitBox(AbstractWorldObject ac) { + //TODO Figure out how Str Affects HitBox + float hitBox = 1; + switch(ac.getObjectType()){ + case PlayerCharacter: + PlayerCharacter pc = (PlayerCharacter)ac; + if (MBServerStatics.COMBAT_TARGET_HITBOX_DEBUG) { + Logger.info("Hit box radius for " + pc.getFirstName() + " is " + ((int) pc.statStrBase / 20f)); + } + hitBox = 1.5f + (int) ((PlayerCharacter) ac).statStrBase / 20f; + break; + + case Mob: + Mob mob = (Mob)ac; + if (MBServerStatics.COMBAT_TARGET_HITBOX_DEBUG) + Logger.info( "Hit box radius for " + mob.getFirstName() + + " is " + ((Mob) ac).getMobBase().getHitBoxRadius()); + + hitBox = ((Mob) ac).getMobBase().getHitBoxRadius(); + break; + case Building: + Building building = (Building)ac; + if (building.getBlueprint() == null) + return 32; + hitBox = Math.max(building.getBlueprint().getBuildingGroup().getExtents().x, + building.getBlueprint().getBuildingGroup().getExtents().y); + if (MBServerStatics.COMBAT_TARGET_HITBOX_DEBUG) + Logger.info( "Hit box radius for " + building.getName() + " is " + hitBox); + break; + + } + return hitBox; + } + + private static void testItemDamage(AbstractCharacter ac, AbstractWorldObject awo, Item weapon, ItemBase wb) { + if (ac == null) { + return; + } + + //get chance to damage + int chance = 4500; + if (wb != null) { + if (wb.isGlass()) //glass used weighted so fast weapons don't break faster + { + chance = 9000 / wb.getWeight(); + } + } + //test damaging attackers weapon + int takeDamage = ThreadLocalRandom.current().nextInt(chance); + if (takeDamage == 0 && wb != null && (ac.getObjectType().equals(GameObjectType.PlayerCharacter))) { + ac.getCharItemManager().damageItem(weapon, 1); + } + + //test damaging targets gear + takeDamage = ThreadLocalRandom.current().nextInt(chance); + if (takeDamage == 0 && awo != null && (awo.getObjectType().equals(GameObjectType.PlayerCharacter))) { + ((AbstractCharacter) awo).getCharItemManager().damageRandomArmor(1); + } + } + +} diff --git a/src/engine/gameManager/ConfigManager.java b/src/engine/gameManager/ConfigManager.java new file mode 100644 index 00000000..d7f5041d --- /dev/null +++ b/src/engine/gameManager/ConfigManager.java @@ -0,0 +1,113 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.gameManager; + +/* This enumeration implements Magicbane's configuration data which + is loaded from environment variables. + */ + +import engine.Enum; +import engine.net.NetMsgHandler; +import engine.server.login.LoginServer; +import engine.server.world.WorldServer; +import org.pmw.tinylog.Logger; + +import java.util.HashMap; +import java.util.Map; + +public enum ConfigManager { + + // Bind address can differ from public address + // when running over a network bridge, etc. + + MB_PUBLIC_ADDR, + MB_BIND_ADDR, + + // Database connection config + + MB_DATABASE_ADDRESS, + MB_DATABASE_PORT, + MB_DATABASE_NAME, + MB_DATABASE_USER, + MB_DATABASE_PASS, + + // Data warehouse remote connection + + MB_WAREHOUSE_ADDR, + MB_WAREHOUSE_USER, + MB_WAREHOUSE_PASS, + + // Login server config + + MB_LOGIN_PORT, + MB_MAJOR_VER, + MB_MINOR_VER, + + // Worldserver configuration + + MB_WORLD_NAME, + MB_WORLD_MAPID, + MB_WORLD_PORT, + MB_WORLD_ACCESS_LVL, + MB_WORLD_UUID, + MB_WORLD_WAREHOUSE_PUSH, + MB_WORLD_MAINTENANCE, + MB_WORLD_MAINTENANCE_HOUR, + MB_WORLD_GREETING, + MB_WORLD_KEYCLONE_MAX, + + // MagicBot configuration. + + MB_MAGICBOT_SERVERID, + MB_MAGICBOT_BOTTOKEN, + MB_MAGICBOT_ROLEID, + MB_MAGICBOT_ANNOUNCE, + MB_MAGICBOT_SEPTIC, + MB_MAGICBOT_CHANGELOG, + MB_MAGICBOT_POLITICAL, + MB_MAGICBOT_GENERAL, + MB_MAGICBOT_FORTOFIX, + MB_MAGICBOT_RECRUIT, + MB_MAGICBOT_BOTVERSION, + MB_MAGICBOT_GAMEVERSION; + + // Map to hold our config pulled in from the environment + // We also use the config to point to the current message pump + // and determine the server type at runtime. + + public static Map configMap = new HashMap(System.getenv()); + public static Enum.ServerType serverType = Enum.ServerType.NONE; + public static NetMsgHandler handler; + public static WorldServer worldServer; + public static LoginServer loginServer; + + // Called at bootstrap: ensures that all config values are loaded. + + public static boolean init() { + + Logger.info("ConfigManager: init()"); + + for (ConfigManager configSetting : ConfigManager.values()) + if (configMap.containsKey(configSetting.name())) + Logger.info(configSetting.name() + ":" + configSetting.getValue()); + else { + Logger.error("Missing Config: " + configSetting.name()); + return false; + } + + return true; + } + + // Get the value associated with this enumeration + + public String getValue() { + return configMap.get(this.name()); + } + public void setValue(String value) { configMap.put(this.name(), value); } +} diff --git a/src/engine/gameManager/DbManager.java b/src/engine/gameManager/DbManager.java new file mode 100644 index 00000000..d99bbbc9 --- /dev/null +++ b/src/engine/gameManager/DbManager.java @@ -0,0 +1,314 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.gameManager; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.db.handlers.*; +import engine.objects.*; +import engine.pooling.ConnectionPool; +import engine.server.MBServerStatics; +import engine.util.Hasher; +import org.pmw.tinylog.Logger; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.EnumMap; +import java.util.concurrent.ConcurrentHashMap; + +public enum DbManager { + DBMANAGER; + + private static ConnectionPool connPool; + public static Hasher hasher; + + //Local Object Caching + + private static final EnumMap> objectCache = new EnumMap<>(GameObjectType.class); + + public static boolean configureDatabaseLayer() { + + boolean worked = true; + + try { + DbManager.connPool = new ConnectionPool(); + DbManager.connPool.fill(10); + DBMANAGER.hasher = new Hasher(); + } catch (Exception e ) { + e.printStackTrace(); + worked = false; + } + return worked; + } + + public static AbstractGameObject getObject(GameObjectType objectType, int objectUUID) { + + AbstractGameObject outObject = null; + + switch (objectType) { + case PlayerCharacter: + outObject = PlayerCharacter.getPlayerCharacter(objectUUID); + break; + case NPC: + outObject = NPC.getNPC(objectUUID); + break; + case Mob: + outObject = Mob.getFromCache(objectUUID); + break; + case Building: + outObject = BuildingManager.getBuilding(objectUUID); + break; + case Guild: + outObject = Guild.getGuild(objectUUID); + break; + case Item: + outObject = Item.getFromCache(objectUUID); + break; + case MobLoot: + outObject = MobLoot.getFromCache(objectUUID); + break; + case City: + outObject = City.getCity(objectUUID); + break; + default: + Logger.error("Attempt to retrieve nonexistant " + objectType + + " from object cache." ); + break; + + } + + return outObject; + } + + public static int getPoolSize(){ + return connPool.getPoolSize(); + } + + public static boolean inCache(GameObjectType gameObjectType, int uuid) { + + if (objectCache.get(gameObjectType) == null) + return false; + + return (objectCache.get(gameObjectType).containsKey(uuid)); + + } + + public static AbstractGameObject getFromCache(GameObjectType gameObjectType, int uuid) { + + if (objectCache.get(gameObjectType) == null) + return null; + + return objectCache.get(gameObjectType).get(uuid); + + } + + public static void removeFromCache(GameObjectType gameObjectType, int uuid) { + + AbstractGameObject abstractGameObject; + + if (objectCache.get(gameObjectType) == null) + return; + + abstractGameObject = objectCache.get(gameObjectType).get(uuid); + + if (abstractGameObject == null) + return; + + removeFromCache(abstractGameObject); + + } + + public static void removeFromCache(AbstractGameObject abstractGameObject) { + + if (abstractGameObject == null) + return; + + if (objectCache.get(abstractGameObject.getObjectType()) == null) + return; + + // Remove object from game cache + + objectCache.get(abstractGameObject.getObjectType()).remove(abstractGameObject.getObjectUUID()); + + // Release bounds as we're dispensing with this object. + + if (abstractGameObject instanceof AbstractWorldObject) { + AbstractWorldObject abstractWorldObject = (AbstractWorldObject)abstractGameObject; + + if (abstractWorldObject.getBounds() != null) { + abstractWorldObject.getBounds().release(); + abstractWorldObject.setBounds(null); + } + } + + } + + public static boolean addToCache(AbstractGameObject gameObject) { + + boolean isWorldServer = ConfigManager.serverType.equals(Enum.ServerType.WORLDSERVER); + + if (!isWorldServer) { + if (MBServerStatics.SKIP_CACHE_LOGIN) + return true; + if (MBServerStatics.SKIP_CACHE_LOGIN_PLAYER + && (gameObject.getObjectType() == GameObjectType.PlayerCharacter)) + return true; + if (MBServerStatics.SKIP_CACHE_LOGIN_ITEM && + (gameObject.getObjectType() == GameObjectType.Item)) + return true; + } + + // First time this object type has been cached. Create the hashmap. + + if (objectCache.get(gameObject.getObjectType()) == null) { + + int initialCapacity; + + // Provide initial sizing hints + + switch (gameObject.getObjectType()) { + case Building: + initialCapacity = 46900; + break; + case Mob: + initialCapacity = 11700; + break; + case NPC: + initialCapacity = 900; + break; + case Zone: + initialCapacity = 1070; + break; + case Account: + initialCapacity = 10000; + break; + case Guild: + initialCapacity = 100; + break; + case ItemContainer: + initialCapacity = 100; + break; + case Item: + initialCapacity = 1000; + break; + case MobLoot: + initialCapacity = 10000; + break; + case PlayerCharacter: + initialCapacity = 100; + break; + default: + initialCapacity = 100; // Lookup api default should be ok for small maps + break; + } + objectCache.put(gameObject.getObjectType(), new ConcurrentHashMap<>(initialCapacity)); + } + + // Add the object to the cache. This will overwrite the current map entry. + + objectCache.get(gameObject.getObjectType()).put(gameObject.getObjectUUID(), gameObject); + + return true; + } + + public static java.util.Collection getList(GameObjectType gameObjectType) { + + if (objectCache.get(gameObjectType) == null) + return null; + + return objectCache.get(gameObjectType).values(); + } + + public static PreparedStatement prepareStatement(String sql) throws SQLException { + return getConn().prepareStatement(sql, 1); + } + + // Omg refactor this out, somebody! + + public static ConcurrentHashMap getMap( + GameObjectType gameObjectType) { + + if (objectCache.get(gameObjectType) == null) + return null; + + return objectCache.get(gameObjectType); + + } + + public static void printCacheCount(PlayerCharacter pc) { + ChatManager.chatSystemInfo(pc, "Cache Lists"); + + for (GameObjectType gameObjectType : GameObjectType.values()) { + + if (objectCache.get(gameObjectType) == null) + continue; + + String ret = gameObjectType.name() + ": " + objectCache.get(gameObjectType).size(); + ChatManager.chatSystemInfo(pc, ret + '\n'); + } + } + + /** + * @return the conn + */ + //XXX I think we have a severe resource leak here! No one is putting the connections back! + public static Connection getConn() { + Connection conn = DbManager.connPool.get(); + try { + if (!conn.isClosed()) + DbManager.connPool.put(conn); + } catch (SQLException e) { + Logger.error( e.toString()); + } + return conn; + } + + public static final dbAccountHandler AccountQueries = new dbAccountHandler(); + public static final dbBaneHandler BaneQueries = new dbBaneHandler(); + public static final dbBaseClassHandler BaseClassQueries = new dbBaseClassHandler(); + public static final dbBuildingHandler BuildingQueries = new dbBuildingHandler(); + public static final dbBuildingLocationHandler BuildingLocationQueries = new dbBuildingLocationHandler(); + public static final dbCharacterPowerHandler CharacterPowerQueries = new dbCharacterPowerHandler(); + public static final dbCharacterRuneHandler CharacterRuneQueries = new dbCharacterRuneHandler(); + public static final dbCharacterSkillHandler CharacterSkillQueries = new dbCharacterSkillHandler(); + public static final dbCityHandler CityQueries = new dbCityHandler(); + public static final dbContractHandler ContractQueries = new dbContractHandler(); + public static final dbWarehouseHandler WarehouseQueries = new dbWarehouseHandler(); + public static final dbCSSessionHandler CSSessionQueries = new dbCSSessionHandler(); + public static final dbEnchantmentHandler EnchantmentQueries = new dbEnchantmentHandler(); + public static final dbEffectsResourceCostHandler EffectsResourceCostsQueries = new dbEffectsResourceCostHandler(); + public static final dbGuildHandler GuildQueries = new dbGuildHandler(); + public static final dbItemHandler ItemQueries = new dbItemHandler(); + public static final dbItemBaseHandler ItemBaseQueries = new dbItemBaseHandler(); + public static final dbKitHandler KitQueries = new dbKitHandler(); + public static final dbLootTableHandler LootQueries = new dbLootTableHandler(); + public static final dbMenuHandler MenuQueries = new dbMenuHandler(); + public static final dbMineHandler MineQueries = new dbMineHandler(); + public static final dbMobHandler MobQueries = new dbMobHandler(); + public static final dbMobBaseHandler MobBaseQueries = new dbMobBaseHandler(); + public static final dbNPCHandler NPCQueries = new dbNPCHandler(); + public static final dbPlayerCharacterHandler PlayerCharacterQueries = new dbPlayerCharacterHandler(); + public static final dbPromotionClassHandler PromotionQueries = new dbPromotionClassHandler(); + public static final dbRaceHandler RaceQueries = new dbRaceHandler(); + public static final dbResistHandler ResistQueries = new dbResistHandler(); + public static final dbRuneBaseAttributeHandler RuneBaseAttributeQueries = new dbRuneBaseAttributeHandler(); + public static final dbRuneBaseEffectHandler RuneBaseEffectQueries = new dbRuneBaseEffectHandler(); + public static final dbRuneBaseHandler RuneBaseQueries = new dbRuneBaseHandler(); + public static final dbSkillBaseHandler SkillsBaseQueries = new dbSkillBaseHandler(); + public static final dbSkillReqHandler SkillReqQueries = new dbSkillReqHandler(); + public static final dbSpecialLootHandler SpecialLootQueries = new dbSpecialLootHandler(); + public static final dbVendorDialogHandler VendorDialogQueries = new dbVendorDialogHandler(); + public static final dbZoneHandler ZoneQueries = new dbZoneHandler(); + public static final dbRealmHandler RealmQueries = new dbRealmHandler(); + public static final dbBlueprintHandler BlueprintQueries = new dbBlueprintHandler(); + public static final dbBoonHandler BoonQueries = new dbBoonHandler(); + public static final dbShrineHandler ShrineQueries = new dbShrineHandler(); + public static final dbHeightMapHandler HeightMapQueries = new dbHeightMapHandler(); +} diff --git a/src/engine/gameManager/DevCmdManager.java b/src/engine/gameManager/DevCmdManager.java new file mode 100644 index 00000000..6152e0d6 --- /dev/null +++ b/src/engine/gameManager/DevCmdManager.java @@ -0,0 +1,228 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.gameManager; + +import engine.Enum; +import engine.devcmd.AbstractDevCmd; +import engine.devcmd.cmds.*; +import engine.objects.AbstractGameObject; +import engine.objects.Account; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +public enum DevCmdManager { + DEV_CMD_MANAGER; + + public static ConcurrentHashMap devCmds; + + DevCmdManager() { + init(); + } + + public static void init() { + DevCmdManager.devCmds = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + DevCmdManager.registerCommands(); + } + + /** + * + */ + private static void registerCommands() { + + // Player + DevCmdManager.registerDevCmd(new DistanceCmd());; + DevCmdManager.registerDevCmd(new HelpCmd()); + DevCmdManager.registerDevCmd(new GetZoneCmd()); + DevCmdManager.registerDevCmd(new GetZoneMobsCmd()); + DevCmdManager.registerDevCmd(new PrintBankCmd()); + DevCmdManager.registerDevCmd(new PrintEquipCmd()); + DevCmdManager.registerDevCmd(new PrintInventoryCmd()); + DevCmdManager.registerDevCmd(new PrintVaultCmd()); + DevCmdManager.registerDevCmd(new PrintStatsCmd()); + DevCmdManager.registerDevCmd(new PrintSkillsCmd()); + DevCmdManager.registerDevCmd(new PrintPowersCmd()); + DevCmdManager.registerDevCmd(new PrintBonusesCmd()); + DevCmdManager.registerDevCmd(new PrintResistsCmd()); + DevCmdManager.registerDevCmd(new PrintLocationCmd()); + DevCmdManager.registerDevCmd(new InfoCmd()); + DevCmdManager.registerDevCmd(new GetHeightCmd()); + + // Tester + DevCmdManager.registerDevCmd(new JumpCmd()); + DevCmdManager.registerDevCmd(new GotoCmd()); + DevCmdManager.registerDevCmd(new SummonCmd()); + DevCmdManager.registerDevCmd(new SetHealthCmd()); + DevCmdManager.registerDevCmd(new SetManaCmd()); + DevCmdManager.registerDevCmd(new SetStaminaCmd()); + DevCmdManager.registerDevCmd(new FindBuildingsCmd()); + DevCmdManager.registerDevCmd(new TeleportModeCmd()); + DevCmdManager.registerDevCmd(new SetLevelCmd()); + DevCmdManager.registerDevCmd(new SetBaseClassCmd()); + DevCmdManager.registerDevCmd(new SetPromotionClassCmd()); + DevCmdManager.registerDevCmd(new EffectCmd()); + DevCmdManager.registerDevCmd(new SetRuneCmd()); + DevCmdManager.registerDevCmd(new GetOffsetCmd()); + DevCmdManager.registerDevCmd(new DebugCmd()); + DevCmdManager.registerDevCmd(new AddGoldCmd()); + DevCmdManager.registerDevCmd(new ZoneInfoCmd()); + DevCmdManager.registerDevCmd(new DebugMeleeSyncCmd()); + DevCmdManager.registerDevCmd(new HotzoneCmd()); + DevCmdManager.registerDevCmd(new SetActivateMineCmd()); + // Dev + DevCmdManager.registerDevCmd(new ApplyStatModCmd()); + DevCmdManager.registerDevCmd(new AddBuildingCmd()); + DevCmdManager.registerDevCmd(new AddNPCCmd()); + DevCmdManager.registerDevCmd(new AddMobCmd()); + DevCmdManager.registerDevCmd(new CopyMobCmd()); + DevCmdManager.registerDevCmd(new RemoveObjectCmd()); + DevCmdManager.registerDevCmd(new RotateCmd()); + DevCmdManager.registerDevCmd(new FlashMsgCmd()); + DevCmdManager.registerDevCmd(new SysMsgCmd()); + DevCmdManager.registerDevCmd(new GetBankCmd()); + DevCmdManager.registerDevCmd(new GetVaultCmd()); + DevCmdManager.registerDevCmd(new CombatMessageCmd()); + DevCmdManager.registerDevCmd(new RenameMobCmd()); + DevCmdManager.registerDevCmd(new RenameCmd()); + DevCmdManager.registerDevCmd(new CreateItemCmd()); + DevCmdManager.registerDevCmd(new GetMemoryCmd()); + DevCmdManager.registerDevCmd(new SetRankCmd()); + DevCmdManager.registerDevCmd(new MakeBaneCmd()); + DevCmdManager.registerDevCmd(new RemoveBaneCmd()); + DevCmdManager.registerDevCmd(new SetBaneActiveCmd()); + DevCmdManager.registerDevCmd(new SetAdminRuneCmd()); + DevCmdManager.registerDevCmd(new SetInvulCmd()); + DevCmdManager.registerDevCmd(new MakeItemCmd()); + DevCmdManager.registerDevCmd(new EnchantCmd()); + DevCmdManager.registerDevCmd(new SetSubRaceCmd()); + // Admin + DevCmdManager.registerDevCmd(new GetCacheCountCmd()); + DevCmdManager.registerDevCmd(new GetRuneDropRateCmd()); + DevCmdManager.registerDevCmd(new DecachePlayerCmd()); + DevCmdManager.registerDevCmd(new SetRateCmd()); + DevCmdManager.registerDevCmd(new AuditMobsCmd()); + DevCmdManager.registerDevCmd(new ChangeNameCmd()); + DevCmdManager.registerDevCmd(new GuildListCmd()); + DevCmdManager.registerDevCmd(new SetGuildCmd()); + DevCmdManager.registerDevCmd(new SetOwnerCmd()); + DevCmdManager.registerDevCmd(new NetDebugCmd()); + DevCmdManager.registerDevCmd(new SqlDebugCmd()); + DevCmdManager.registerDevCmd(new PullCmd()); + DevCmdManager.registerDevCmd(new PurgeObjectsCmd()); + DevCmdManager.registerDevCmd(new SplatMobCmd()); + DevCmdManager.registerDevCmd(new SlotNpcCmd()); + DevCmdManager.registerDevCmd(new SetAICmd()); + DevCmdManager.registerDevCmd(new GateInfoCmd()); + DevCmdManager.registerDevCmd(new ShowOffsetCmd()); + DevCmdManager.registerDevCmd(new RealmInfoCmd()); + DevCmdManager.registerDevCmd(new RebootCmd()); + DevCmdManager.registerDevCmd(new AddMobPowerCmd()); + DevCmdManager.registerDevCmd(new AddMobRuneCmd()); + DevCmdManager.registerDevCmd(new SetMineTypeCmd()); + DevCmdManager.registerDevCmd(new SetMineExpansion()); + DevCmdManager.registerDevCmd(new SetForceRenameCityCmd()); + DevCmdManager.registerDevCmd(new GotoObj()); + DevCmdManager.registerDevCmd(new convertLoc()); + DevCmdManager.registerDevCmd(new GetMobBaseLoot()); + DevCmdManager.registerDevCmd(new MBDropCmd()); + DevCmdManager.registerDevCmd(new GetDisciplineLocCmd()); + DevCmdManager.registerDevCmd(new AuditHeightMapCmd()); + DevCmdManager.registerDevCmd(new UnloadFurnitureCmd()); + DevCmdManager.registerDevCmd(new SetNPCSlotCmd()); + DevCmdManager.registerDevCmd(new SetNpcEquipSetCmd()); + DevCmdManager.registerDevCmd(new SetBuildingAltitudeCmd()); + DevCmdManager.registerDevCmd(new ResetLevelCmd()); + DevCmdManager.registerDevCmd(new HeartbeatCmd()); + DevCmdManager.registerDevCmd(new SetNpcNameCmd()); + DevCmdManager.registerDevCmd(new SetNpcMobbaseCmd()); + DevCmdManager.registerDevCmd(new DespawnCmd()); + DevCmdManager.registerDevCmd(new BoundsCmd()); + DevCmdManager.registerDevCmd(new GotoBoundsCmd()); + DevCmdManager.registerDevCmd(new RegionCmd()); + DevCmdManager.registerDevCmd(new SetMaintCmd()); + DevCmdManager.registerDevCmd(new ApplyBonusCmd()); + DevCmdManager.registerDevCmd(new setOpenDateCmd()); + DevCmdManager.registerDevCmd(new AuditFailedItemsCmd()); + + } + + private static void registerDevCmd(AbstractDevCmd cmd) { + ArrayList cmdStrings = cmd.getCmdStrings(); + for (String cmdString : cmdStrings) { + DevCmdManager.devCmds.put(cmdString, cmd); + } + } + + public static AbstractDevCmd getDevCmd(String cmd) { + String lowercase = cmd.toLowerCase(); + return DevCmdManager.devCmds.get(lowercase); + } + + public static boolean handleDevCmd(PlayerCharacter pcSender, String cmd, + String argString, AbstractGameObject target) { + + if (pcSender == null) { + return false; + } + + Account a = SessionManager.getAccount(pcSender); + + if (a == null) { + return false; + } + + AbstractDevCmd adc = DevCmdManager.getDevCmd(cmd); + + if (adc == null) { + return false; + } + + //kill any commands not available to everyone on production server + //only admin level can run dev commands on production + + if (a.status.equals(Enum.AccountStatus.ADMIN) == false) { + Logger.info("Account " + a.getUname() + "attempted to use dev command " + cmd); + return false; + } + + // TODO add a job here to separate calling thread form executing thread? + // Log + + String accName = a.getUname(); + String pcName = pcSender.getCombinedName(); + String logString = pcName + '(' + accName + + ") '"; + logString += cmd + ' ' + argString + '\''; + Logger.info( logString); + + // execute command; + try { + adc.doCmd(pcSender, argString, target); + } catch (Exception e) { + Logger.error(e.toString()); + e.printStackTrace(); + } + + return true; + } + + public static String getCmdsForAccessLevel() { + String out = ""; + + for (Entry e : DevCmdManager.devCmds.entrySet()) + out += e.getKey() + ", "; + + return out; + } + +} diff --git a/src/engine/gameManager/GroupManager.java b/src/engine/gameManager/GroupManager.java new file mode 100644 index 00000000..a451d4c9 --- /dev/null +++ b/src/engine/gameManager/GroupManager.java @@ -0,0 +1,393 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.gameManager; + +import engine.Enum; +import engine.exception.MsgSendException; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.UpdateGoldMsg; +import engine.net.client.msg.group.GroupUpdateMsg; +import engine.objects.*; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public enum GroupManager { + + GROUPMANAGER; + + // used for quick lookup of groups by the ID of the group sent in the msg + private static final ConcurrentHashMap groupsByID = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_HIGH); + + // an index for playercharacters to group membership + private static final ConcurrentHashMap groupsByAC = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_HIGH); + private static int groupCount = 0; + + /* + * Class Implementation + */ + public static void removeFromGroups(AbstractCharacter ac) { + Group gr = null; + + synchronized (GroupManager.groupsByAC) { + gr = GroupManager.groupsByAC.remove(ac); + } + + } + + public static void LeaveGroup(ClientConnection origin) throws MsgSendException { + PlayerCharacter source = SessionManager.getPlayerCharacter(origin); + LeaveGroup(source); + } + + public static void LeaveGroup(PlayerCharacter source) throws MsgSendException { + + if (source == null) + return; + + Group group = GroupManager.groupsByAC.get(source); + + if (group == null) // source is not in a group + return; + + // Cleanup group window for player quiting + GroupUpdateMsg groupUpdateMsg = new GroupUpdateMsg(); + groupUpdateMsg.setGroup(group); + groupUpdateMsg.setPlayer(source); + groupUpdateMsg.setMessageType(3); + + Set groupMembers = group.getMembers(); + + for (PlayerCharacter groupMember : groupMembers) { + + if (groupMember == null) + continue; + + groupUpdateMsg = new GroupUpdateMsg(); + groupUpdateMsg.setGroup(group); + groupUpdateMsg.setPlayer(source); + groupUpdateMsg.setMessageType(3); + groupUpdateMsg.setPlayer(groupMember); + Dispatch dispatch = Dispatch.borrow(source, groupUpdateMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + + } + + // Remove from group + int size = group.removeGroupMember(source); + // remove from the group -> ac mapping list + GroupManager.groupsByAC.remove(source); + + if (size == 0) { + GroupManager.deleteGroup(group); + return; // group empty so cleanup group and we're done + } + + // set new group lead if needed + if (group.getGroupLead() == source) { + PlayerCharacter newLead = group.getMembers().iterator().next(); + group.setGroupLead(newLead.getObjectUUID()); + groupUpdateMsg = new GroupUpdateMsg(); + groupUpdateMsg.setGroup(group); + groupUpdateMsg.setPlayer(newLead); + groupUpdateMsg.addPlayer(source); + groupUpdateMsg.setMessageType(2); + group.sendUpdate(groupUpdateMsg); + + // Disable Formation + newLead.setFollow(false); + groupUpdateMsg = new GroupUpdateMsg(); + groupUpdateMsg.setGroup(group); + groupUpdateMsg.setPlayer(newLead); + groupUpdateMsg.setMessageType(8); + group.sendUpdate(groupUpdateMsg); + } + + //send message to group + PlayerCharacter pc = group.getGroupLead(); + //Fixed + String text = source.getFirstName() + " has left the group."; + ChatManager.chatGroupInfo(pc, text); + + // cleanup other group members screens + groupUpdateMsg = new GroupUpdateMsg(); + groupUpdateMsg.setGroup(group); + groupUpdateMsg.setPlayer(source); + groupUpdateMsg.setMessageType(3); + group.sendUpdate(groupUpdateMsg); + + } + + //This updates health/stamina/mana and loc of all players in group + + public static void RefreshWholeGroupList(PlayerCharacter source, ClientConnection origin, Group gexp) { + + if (source == null || origin == null) + return; + + Group group = GroupManager.groupsByAC.get(source); + + if (group == null) + return; + + if (gexp.getObjectUUID() != group.getObjectUUID()) + return; + + Set groupMembers = group.getMembers(); + + if (groupMembers.size() < 2) + return; + + // Send all group members health/mana/stamina/loc. + + for (PlayerCharacter groupMember : groupMembers) { + + if (groupMember == null) + continue; + + GroupUpdateMsg gum = new GroupUpdateMsg(5, 1, groupMembers, group); + gum.setPlayerUUID(groupMember.getObjectUUID()); + + Dispatch dispatch = Dispatch.borrow(groupMember, gum); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + } + } + + public static void RefreshMyGroupList(PlayerCharacter source, ClientConnection origin) { + + if (source == null || origin == null) + return; + + Group group = GroupManager.groupsByAC.get(source); + + if (group == null) + return; + + Set members = group.getMembers(); + + + // Send all group members to player added + for (PlayerCharacter groupMember : members) { + + if (groupMember == null) + continue; + + GroupUpdateMsg gum = new GroupUpdateMsg(); + gum.setGroup(group); + gum.setMessageType(1); + gum.setPlayer(groupMember); + Dispatch dispatch = Dispatch.borrow(groupMember, gum); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + + } + } + + public static void RefreshMyGroupListSinglePlayer(PlayerCharacter source, ClientConnection origin, PlayerCharacter playerToRefresh) { + + // send msg type 1 to the source player on this connection to update the group + // list stats for the player that has just been loaded + + if (source == null || origin == null || playerToRefresh == null) + return; + + Group group = GroupManager.groupsByAC.get(source); + + if (group == null) + return; + + // only send if the 2 players are in the same group + if (group != GroupManager.groupsByAC.get(playerToRefresh)) + return; + + GroupUpdateMsg gum = new GroupUpdateMsg(); + gum.setGroup(group); + gum.setMessageType(1); + gum.setPlayer(playerToRefresh); + + Dispatch dispatch = Dispatch.borrow(source, gum); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + } + + public static void RefreshOthersGroupList(PlayerCharacter source) { + + // refresh my stats on everyone elses group list + + if (source == null) + return; + + Group group = GroupManager.groupsByAC.get(source); + + if (group == null) + return; + + //construct message + GroupUpdateMsg gim = new GroupUpdateMsg(); + gim.setGroup(group); + gim.setMessageType(1); + gim.setPlayer(source); + group.sendUpdate(gim); + + } + + public static int incrGroupCount() { + GroupManager.groupCount++; + return GroupManager.groupCount; + } + + public static boolean deleteGroup(Group g) { + + // remove all players from the mapping + Set members = g.getMembers(); + + for (PlayerCharacter pc : members) { + if (pc != null) { + GroupManager.removeFromGroups(pc); + } + } + // remove the group ID from the list + GroupManager.groupsByID.remove(g.getObjectUUID()); + g.clearMembers(); + + g.removeUpdateGroupJob(); + + return true; + } + + public static Group addNewGroup(Group group) { + + PlayerCharacter pc = group.getGroupLead(); + + GroupManager.addGroup(group); + + if (pc != null) { + GroupManager.addPlayerGroupMapping(pc, group); + return group; + } + return null; + + } + + private static Group addGroup(Group group) { + + if (GroupManager.groupsByID.containsKey(group.getObjectUUID())) { + return null; + } + + GroupManager.groupsByID.put(group.getObjectUUID(), group); + return group; + } + + public static Group getGroup(int groupID) { + return GroupManager.groupsByID.get(groupID); + } + + public static Group getGroup(PlayerCharacter pc) { + + return GroupManager.groupsByAC.get(pc); + } + + public static void addPlayerGroupMapping(PlayerCharacter pc, Group grp) { + GroupManager.groupsByAC.put(pc, grp); + } + + public static boolean goldSplit(PlayerCharacter pc, Item item, ClientConnection origin, AbstractWorldObject tar) { + if (item == null || pc == null || tar == null || item.getItemBase() == null) { + Logger.error( "null something"); + return false; + } + + if (item.getItemBase().getUUID() != 7) //only split goldItem + return false; + + Group group = getGroup(pc); + + if (group == null || !group.getSplitGold()) //make sure player is grouped and split is on + return false; + + + ArrayList playersSplit = new ArrayList<>(); + + //get group members + + for (PlayerCharacter groupMember: group.getMembers()){ + if (pc.getLoc().distanceSquared2D(groupMember.getLoc()) > MBServerStatics.CHARACTER_LOAD_RANGE * MBServerStatics.CHARACTER_LOAD_RANGE) + continue; + + if (!groupMember.isAlive()) + continue; + + playersSplit.add(groupMember); + } + + + //make sure more then one group member in loot range + int size = playersSplit.size(); + + if (size < 2) + return false; + + int total = item.getNumOfItems(); + int amount = total / size; + int dif = total - (size * amount); + + if (AbstractWorldObject.IsAbstractCharacter(tar)) { + } + else if (tar.getObjectType().equals(Enum.GameObjectType.Corpse)) { + Corpse corpse = (Corpse) tar; + corpse.getInventory().remove(item); + } + else { + Logger.error("target not corpse or character"); + return false; + } + + if (item.getObjectType() == Enum.GameObjectType.MobLoot){ + if (tar.getObjectType() == Enum.GameObjectType.Mob){ + ((Mob)tar).getCharItemManager().delete(item); + }else + item.setNumOfItems(0); + }else + item.setNumOfItems(0); + for (PlayerCharacter splitPlayer : playersSplit) { + + + + int amt = (group.isGroupLead(splitPlayer)) ? (amount + dif) : amount; + if (amt > 0) + splitPlayer.getCharItemManager().addGoldToInventory(amt, false); + } + + for (PlayerCharacter splitPlayer : playersSplit) { + + + UpdateGoldMsg ugm = new UpdateGoldMsg(splitPlayer); + ugm.configure(); + + Dispatch dispatch = Dispatch.borrow(splitPlayer, ugm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + UpdateGoldMsg updateTargetGold = new UpdateGoldMsg(tar); + updateTargetGold.configure(); + DispatchMessage.dispatchMsgToInterestArea(tar, updateTargetGold, Enum.DispatchChannel.SECONDARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + + + // //TODO send group split message + String text = "Group Split: " + amount; + ChatManager.chatGroupInfo(pc, text); + + return true; + } +} diff --git a/src/engine/gameManager/GuildManager.java b/src/engine/gameManager/GuildManager.java new file mode 100644 index 00000000..c69cf920 --- /dev/null +++ b/src/engine/gameManager/GuildManager.java @@ -0,0 +1,206 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.gameManager; + +import engine.Enum; +import engine.Enum.BuildingGroup; +import engine.Enum.GuildHistoryType; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.guild.AcceptInviteToGuildMsg; +import engine.net.client.msg.guild.GuildInfoMsg; +import engine.objects.*; +import org.joda.time.DateTime; + +public enum GuildManager { + + GUILDMANAGER; + + //Guild Error Message + public static final int FAILURE_TO_SWEAR_GUILD = 45; //45: Failure to swear guild + public static final int MUST_LEAVE_GUILD = 75;//75: You must leave your current guild before you can repledge + public static final int NO_CHARTER_FOUND = 148; //148: Unable to find a matching petition to complete guild creation + public static final int PROFANE_NAME = 149; //149: Guild name fails profanity check + public static final int PROFANE_MOTTO = 150; //150: Guild motto fails profanity check + public static final int UNIQUE_NAME = 151;//151: Guild name is not unique + public static final int UNIQUE_CREST = 152;//152: Guild crest is not unique + public static final int CREST_RESERVED = 153; //153: Guild crest is reserved + public static final int CREST_COLOR_ERROR = 154; //154: All three crest colors cannot be the same + + public static boolean joinGuild(PlayerCharacter pc, Guild guild, GuildHistoryType historyType) { + return joinGuild(pc, guild, 0, historyType); + } + + //Used when repledging + public static boolean joinGuild(PlayerCharacter pc, Guild guild, int cityID, GuildHistoryType historyType) { + return joinGuild(pc, guild, cityID, true,historyType); + } + + public static boolean joinGuild(PlayerCharacter playerCharacter, Guild guild, int cityID, boolean fromTeleportScreen, GuildHistoryType historyType) { + + // Member variable delcaration + + ClientConnection origin; + AcceptInviteToGuildMsg msg; + Dispatch dispatch; + + if (playerCharacter == null || guild == null) + return false; + + // Member variable assignment + + origin = SessionManager.getClientConnection(playerCharacter); + + if (origin == null) + return false; + + if (playerCharacter.getGuild().isErrant() == false && GuildStatusController.isGuildLeader(playerCharacter.getGuildStatus())) + return false; + + if (playerCharacter.getGuild() != null && playerCharacter.getGuild().isGuildLeader(playerCharacter.getObjectUUID())) + return false; + + if (playerCharacter.getGuild() != null && !playerCharacter.getGuild().isErrant()){ + if (DbManager.GuildQueries.ADD_TO_GUILDHISTORY(playerCharacter.getGuildUUID(), playerCharacter, DateTime.now(), GuildHistoryType.LEAVE)){ + GuildHistory guildHistory = new GuildHistory(playerCharacter.getGuildUUID(),playerCharacter.getGuild().getName(),DateTime.now(), GuildHistoryType.LEAVE) ; + playerCharacter.getGuildHistory().add(guildHistory); + } + } + + playerCharacter.setInnerCouncil(false); + playerCharacter.setGuildLeader(false); + playerCharacter.setGuild(guild); + + // Cleanup guild stuff + playerCharacter.resetGuildStatuses(); + + // send success message to client + if (fromTeleportScreen && guild.isNPCGuild()) + playerCharacter.setFullMember(true); + + msg = new AcceptInviteToGuildMsg(guild.getObjectUUID(), 1, 0); + + if (fromTeleportScreen) { + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + if (DbManager.GuildQueries.ADD_TO_GUILDHISTORY(guild.getObjectUUID(), playerCharacter, DateTime.now(), historyType)){ + GuildHistory guildHistory = new GuildHistory(guild.getObjectUUID(),guild.getName(),DateTime.now(), historyType) ; + playerCharacter.getGuildHistory().add(guildHistory); + } + + DispatchMessage.sendToAllInRange(playerCharacter, new GuildInfoMsg(playerCharacter, guild, 2)); + + // Send guild join message + ChatManager.chatGuildInfo(playerCharacter, + playerCharacter.getFirstName() + " has joined the guild"); + + playerCharacter.incVer(); + + return true; + // TODO update player to world + } + + public static void enterWorldMOTD(PlayerCharacter pc) { + + Guild guild; + Guild nation; + + if (pc == null) { + return; + } + + guild = pc.getGuild(); + + if (guild == null || guild.getObjectUUID() == 0) // Don't send to errant + return; + + // Send Guild MOTD + String motd = guild.getMOTD(); + if (motd.length() > 0) { + ChatManager.chatGuildMOTD(pc, motd); + } + + // Send Nation MOTD + nation = guild.getNation(); + + if (nation != null) { + if (nation.getObjectUUID() != 0) { // Don't send to errant nation + motd = nation.getMOTD(); + if (motd.length() > 0) { + ChatManager.chatNationMOTD(pc, motd); + } + } + } + + // Send IC MOTD if player is IC + if (GuildStatusController.isInnerCouncil(pc.getGuildStatus())) { + motd = guild.getICMOTD(); + if (motd.length() > 0) { + ChatManager.chatICMOTD(pc, motd); + } + } + } + + //Updates the bind point for everyone in guild + + public static void updateAllGuildBinds(Guild guild, City city) { + + if (guild == null) + return; + + int cityID = (city != null) ? city.getObjectUUID() : 0; + + + + //update binds ingame + + + for (PlayerCharacter playerCharacter : Guild.GuildRoster(guild)) { + boolean updateBindBuilding = false; + + Building oldBoundBuilding = BuildingManager.getBuildingFromCache(playerCharacter.getBindBuildingID()); + + if (oldBoundBuilding == null || oldBoundBuilding.getBlueprint() == null || oldBoundBuilding.getBlueprint().getBuildingGroup().equals(BuildingGroup.TOL)) + updateBindBuilding = true; + + + + if (updateBindBuilding){ + Building bindBuilding = null; + if (city != null) + if (city.getTOL() != null) + bindBuilding = city.getTOL(); + + if (bindBuilding == null) + bindBuilding = PlayerCharacter.getBindBuildingForGuild(playerCharacter); + + playerCharacter.setBindBuildingID(bindBuilding != null ? bindBuilding.getObjectUUID() : 0); + } + + + } + } + + //This updates tags for all online players in a guild. + public static void updateAllGuildTags(Guild guild) { + + if (guild == null) + return; + + for (PlayerCharacter player : SessionManager.getAllActivePlayerCharacters()) { + + if (player.getGuild().equals(guild)) + DispatchMessage.sendToAllInRange(player, new GuildInfoMsg(player , guild, 2)); + + } + } + +} diff --git a/src/engine/gameManager/MaintenanceManager.java b/src/engine/gameManager/MaintenanceManager.java new file mode 100644 index 00000000..35185ebd --- /dev/null +++ b/src/engine/gameManager/MaintenanceManager.java @@ -0,0 +1,359 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.gameManager; + +// Defines static methods which comprise the magicbane +// building maintenance system. + +import engine.Enum; +import engine.objects.*; +import org.pmw.tinylog.Logger; + +import java.time.LocalDateTime; +import java.util.ArrayList; + +public enum MaintenanceManager { + + MAINTENANCEMANAGER; + + public static void setMaintDateTime(Building building, LocalDateTime maintDate) { + + building.maintDateTime = maintDate; + DbManager.BuildingQueries.updateMaintDate(building); + + } + + public static void processBuildingMaintenance() { + + ArrayList buildingList; + ArrayList maintList; + ArrayList derankList = new ArrayList<>(); + + Logger.info("Starting Maintenance on Player Buildings"); + + // Build list of buildings to apply maintenance on. + + buildingList = new ArrayList(DbManager.getList(Enum.GameObjectType.Building)); + maintList = buildMaintList(buildingList); + + // Deduct upkeep and build list of buildings + // which did not have funds available + + for (Building building : maintList) { + + if (chargeUpkeep(building) == false) + derankList.add(building); + } + + // Reset maintenance dates for these buildings + + for (Building building : maintList) + setMaintDateTime(building, building.maintDateTime.plusDays(7)); + + // Derak or destroy buildings that did not + // have funds available. + + for (Building building : derankList) + building.destroyOrDerank(null); + + Logger.info("Structures: " + buildingList.size() + " Maint: " + maintList.size() + " Derank: " + derankList.size()); + } + + // Iterate over all buildings in game and apply exclusion rules + // returning a list of building for which maintenance is due. + + private static ArrayList buildMaintList(ArrayList buildingList) { + + ArrayList maintList = new ArrayList<>(); + + for (AbstractGameObject gameObject : buildingList) { + + Building building = (Building) gameObject; + + // No Maintenance on fidelity structures + + if (building.getProtectionState() == Enum.ProtectionState.NPC) + continue; + + // No maintenance on constructing meshes + + if (building.getRank() < 1) + continue; + + // No Maintenance on furniture + + if (building.parentBuildingID != 0) + continue; + + // No Blueprint? + + if (building.getBlueprint() == null) { + Logger.error("Blueprint missing for uuid: " + building.getObjectUUID()); + continue; + } + + // No maintenance on banestones omfg + + if (building.getBlueprint().getBuildingGroup().equals(Enum.BuildingGroup.BANESTONE)) + continue; + + // no maintenance on Mines omfg + + if (building.getBlueprint().getBuildingGroup().equals(Enum.BuildingGroup.MINE)) + continue; + + // Null Maintenance date? + + if (building.maintDateTime == null) { + Logger.error("Null maint date for building UUID: " + building.getObjectUUID()); + continue; + } + + // Maintenance date is in the future + + if (building.maintDateTime.isAfter(LocalDateTime.now())) + continue; + + // Add building to maintenance queue + + maintList.add(building); + } + + return maintList; + } + + // Method removes the appropriate amount of gold/resources from + // a building according to it's maintenance schedule. True/False + // is returned indicating if the building had enough funds to cover. + + public static boolean chargeUpkeep(Building building) { + + City city = null; + Warehouse warehouse = null; + int maintCost = 0; + int overDraft = 0; + boolean hasFunds = false; + boolean hasResources = false; + int resourceValue = 0; + + city = building.getCity(); + + if (city != null) + warehouse = city.getWarehouse(); + + // Cache maintenance cost value + + maintCost = building.getMaintCost(); + + // Something went wrong. Missing buildinggroup from switch? + + if (maintCost == 0) { + Logger.error("chargeUpkeep", "Error retrieving rankcost for " + building.getName() + " uuid:" + building.getObjectUUID() + "buildinggroup:" + building.getBlueprint().getBuildingGroup().name()); + // check if there is enough gold on the building + return true; + } + + if (building.getStrongboxValue() >= maintCost) + hasFunds = true; + + // If we cannot cover with just the strongbox + // see if there is a warehouse that will cover + // the overdraft for us. + + + if (hasFunds == false && (building.assetIsProtected() || building.getBlueprint().getBuildingGroup() == Enum.BuildingGroup.WAREHOUSE)) { + overDraft = maintCost - building.getStrongboxValue(); + } + + if ((overDraft > 0)) + if ((building.getBlueprint().getBuildingGroup().equals(Enum.BuildingGroup.SHRINE) == false) && + (warehouse != null) && building.assetIsProtected() == true && + (warehouse.getResources().get(ItemBase.GOLD_ITEM_BASE)) >= overDraft) { + hasFunds = true; + } + + // If this is an R8 tree, validate that we can + // cover the resources required + + if (building.getRank() == 8) { + + hasResources = true; + + if (warehouse == null) + hasResources = false; + else { + + resourceValue = warehouse.getResources().get(Warehouse.stoneIB); + + if (resourceValue < 1500) + hasResources = false; + + resourceValue = warehouse.getResources().get(Warehouse.lumberIB); + + if (resourceValue < 1500) + hasResources = false; + + resourceValue = warehouse.getResources().get(Warehouse.galvorIB); + + if (resourceValue < 5) + hasResources = false; + + resourceValue = warehouse.getResources().get(Warehouse.wormwoodIB); + + if (resourceValue < 5) + hasResources = false; + + } + } + // Validation completed but has failed. We can derank + // the target building and early exit + + if ((hasFunds == false) || + ((building.getRank() == 8) && !hasResources)) { + + // Add cash back to strongbox for lost rank if the building isn't being destroyed + // and it's not an R8 deranking + + if ((building.getRank() > 1) && (building.getRank() < 8)) { + building.setStrongboxValue(building.getStrongboxValue() + building.getBlueprint().getRankCost(Math.min(building.getRank(), 7))); + } + + return false; // Early exit for having failed to meet maintenance + } + + // Remove cash and resources + + // withdraw what we can from the building + + building.setStrongboxValue(building.getStrongboxValue() - (maintCost - overDraft)); + + // withdraw overdraft from the whorehouse + + if (overDraft > 0) { + + resourceValue = warehouse.getResources().get(Warehouse.goldIB); + + if (DbManager.WarehouseQueries.updateGold(warehouse, resourceValue - overDraft) == true) { + warehouse.getResources().put(Warehouse.goldIB, resourceValue - overDraft); + warehouse.AddTransactionToWarehouse(Enum.GameObjectType.Building, building.getObjectUUID(), Enum.TransactionType.WITHDRAWL, Resource.GOLD, overDraft); + } else { + Logger.error("gold update failed for warehouse of UUID:" + warehouse.getObjectUUID()); + return true; + } + } + + // Early exit as we're done if we're not an R8 tree + + if (building.getRank() < 8) + return true; + + // Now for the resources if it's an R8 tree + + // Withdraw Stone + + resourceValue = warehouse.getResources().get(Warehouse.stoneIB); + + if (DbManager.WarehouseQueries.updateStone(warehouse, resourceValue - 1500) == true) { + warehouse.getResources().put(Warehouse.stoneIB, resourceValue - 1500); + warehouse.AddTransactionToWarehouse(Enum.GameObjectType.Building, building.getObjectUUID(), Enum.TransactionType.WITHDRAWL, Resource.STONE, 1500); + } else { + Logger.error("stone update failed for warehouse of UUID:" + warehouse.getObjectUUID()); + return true; + } + + // Withdraw Lumber + + resourceValue = warehouse.getResources().get(Warehouse.lumberIB); + + if (DbManager.WarehouseQueries.updateLumber(warehouse, resourceValue - 1500) == true) { + warehouse.getResources().put(Warehouse.lumberIB, resourceValue - 1500); + warehouse.AddTransactionToWarehouse(Enum.GameObjectType.Building, building.getObjectUUID(), Enum.TransactionType.WITHDRAWL, Resource.LUMBER, 1500); + } else { + Logger.error("lumber update failed for warehouse of UUID:" + warehouse.getObjectUUID()); + return true; + } + + // Withdraw Galvor + + resourceValue = warehouse.getResources().get(Warehouse.galvorIB); + + if (DbManager.WarehouseQueries.updateGalvor(warehouse, resourceValue - 5) == true) { + warehouse.getResources().put(Warehouse.galvorIB, resourceValue - 5); + warehouse.AddTransactionToWarehouse(Enum.GameObjectType.Building, building.getObjectUUID(), Enum.TransactionType.WITHDRAWL, Resource.GALVOR, 5); + } else { + Logger.error("galvor update failed for warehouse of UUID:" + warehouse.getObjectUUID()); + return true; + } + + resourceValue = warehouse.getResources().get(Warehouse.wormwoodIB); + + if (DbManager.WarehouseQueries.updateWormwood(warehouse, resourceValue - 5) == true) { + warehouse.getResources().put(Warehouse.wormwoodIB, resourceValue - 5); + warehouse.AddTransactionToWarehouse(Enum.GameObjectType.Building, building.getObjectUUID(), Enum.TransactionType.WITHDRAWL, Resource.WORMWOOD, 5); + } else { + Logger.error("wyrmwood update failed for warehouse of UUID:" + warehouse.getObjectUUID()); + } + + return true; + } + + public static void dailyMaintenance() { + + ArrayList shrineList = new ArrayList<>(); + + Logger.info("Maintenance has started"); + + // Update shrines to proper city owner + + for (Shrine shrine : Shrine.shrinesByBuildingUUID.values()) { + try { + Building shrineBuilding = (Building) DbManager.getObject(Enum.GameObjectType.Building, shrine.getBuildingID()); + + if (shrineBuilding == null) + continue; + + + if (shrineBuilding.getOwner().equals(shrineBuilding.getCity().getOwner()) == false) + shrineBuilding.claim(shrineBuilding.getCity().getOwner()); + } catch (Exception e) { + Logger.info("Shrine " + shrine.getBuildingID() + " Error " + e); + } + } + + // Grab list of top two shrines of each type + + for (Shrine shrine : Shrine.shrinesByBuildingUUID.values()) { + + if (shrine.getRank() == 0 || shrine.getRank() == 1) + shrineList.add(shrine); + } + + Logger.info("Decaying " + shrineList.size() + " shrines..."); + + // Top 2 shrines decay by 10% a day + + for (Shrine shrine : shrineList) { + + try { + shrine.decay(); + } catch (Exception e) { + Logger.info("Shrine " + shrine.getBuildingID() + " Error " + e); + } + } + + // Run maintenance on player buildings + + if ((boolean) ConfigManager.MB_WORLD_MAINTENANCE.getValue().equals("true")) + processBuildingMaintenance(); + else + Logger.info("Maintenance Costings: DISABLED"); + + Logger.info("process has completed!"); + } +} diff --git a/src/engine/gameManager/MovementManager.java b/src/engine/gameManager/MovementManager.java new file mode 100644 index 00000000..b66ba50a --- /dev/null +++ b/src/engine/gameManager/MovementManager.java @@ -0,0 +1,650 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.gameManager; + +import engine.Enum.DispatchChannel; +import engine.Enum.GameObjectType; +import engine.Enum.ModType; +import engine.Enum.SourceType; +import engine.InterestManagement.InterestManager; +import engine.InterestManagement.WorldGrid; +import engine.exception.MsgSendException; +import engine.job.JobContainer; +import engine.job.JobScheduler; +import engine.jobs.ChangeAltitudeJob; +import engine.jobs.FlightJob; +import engine.math.Bounds; +import engine.math.Vector3f; +import engine.math.Vector3fImmutable; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ChangeAltitudeMsg; +import engine.net.client.msg.MoveToPointMsg; +import engine.net.client.msg.TeleportToPointMsg; +import engine.net.client.msg.UpdateStateMsg; +import engine.objects.*; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.util.HashSet; +import java.util.Set; + +import static engine.math.FastMath.sqr; + +public enum MovementManager { + + MOVEMENTMANAGER; + + private static final String changeAltitudeTimerJobName = "ChangeHeight"; + private static final String flightTimerJobName = "Flight"; + + public static void sendOOS(PlayerCharacter pc) { + pc.setWalkMode(true); + MovementManager.sendRWSSMsg(pc); + } + + public static void sendRWSSMsg(AbstractCharacter ac) { + + if (!ac.isAlive()) + return; + UpdateStateMsg rssm = new UpdateStateMsg(); + rssm.setPlayer(ac); + if (ac.getObjectType() == GameObjectType.PlayerCharacter) + DispatchMessage.dispatchMsgToInterestArea(ac, rssm, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + else + DispatchMessage.sendToAllInRange(ac, rssm); + } + + /* + * Sets the first combat target for the AbstractCharacter. Used to clear the + * combat + * target upon each move, unless something has set the firstHitCombatTarget + * Also used to determine the size of a monster's hitbox + */ + public static void movement(MoveToPointMsg msg, AbstractCharacter toMove) throws MsgSendException { + + // check for stun/root + if (!toMove.isAlive()) + return; + + if (toMove.getObjectType().equals(GameObjectType.PlayerCharacter)){ + if (((PlayerCharacter)toMove).isCasting()) + ((PlayerCharacter)toMove).update(); + } + + + + toMove.setIsCasting(false); + toMove.setItemCasting(false); + + if (toMove.getBonuses().getBool(ModType.Stunned, SourceType.None) || toMove.getBonuses().getBool(ModType.CannotMove, SourceType.None)) { + return; + } + + if (msg.getEndLat() > MBServerStatics.MAX_WORLD_WIDTH) + msg.setEndLat((float) MBServerStatics.MAX_WORLD_WIDTH); + + if (msg.getEndLon() < MBServerStatics.MAX_WORLD_HEIGHT){ + msg.setEndLon((float) MBServerStatics.MAX_WORLD_HEIGHT); + } + +// if (msg.getEndLat() < 0) +// msg.setEndLat(0); +// +// if (msg.getEndLon() > 0) +// msg.setEndLon(0); + + + + + + if (!toMove.isMoving()) + toMove.resetLastSetLocUpdate(); + else + toMove.update(); + + // Update movement for the player + + + // else if (toMove.getObjectType() == GameObjectType.Mob) + // ((Mob)toMove).updateLocation(); + // get start and end locations for the move + Vector3fImmutable startLocation = new Vector3fImmutable(msg.getStartLat(), msg.getStartAlt(), msg.getStartLon()); + Vector3fImmutable endLocation = new Vector3fImmutable(msg.getEndLat(), msg.getEndAlt(), msg.getEndLon()); + + // if (toMove.getObjectType() == GameObjectType.PlayerCharacter) + // if (msg.getEndAlt() == 0 && msg.getTargetID() == 0){ + // MovementManager.sendRWSSMsg(toMove); + // } + + //If in Building, let's see if we need to Fix + + // if inside a building, convert both locations from the building local reference frame to the world reference frame + + if (msg.getTargetID() > 0) { + Building building = BuildingManager.getBuildingFromCache(msg.getTargetID()); + if (building != null) { + + Vector3fImmutable convertLocEnd = new Vector3fImmutable(ZoneManager.convertLocalToWorld(building, endLocation)); + // if (!Bounds.collide(convertLocEnd, b) || !b.loadObjectsInside()) { + // toMove.setInBuilding(-1); + // toMove.setInFloorID(-1); + // toMove.setInBuildingID(0); + // } + // else { + toMove.setInBuilding(msg.getInBuilding()); + toMove.setInFloorID(msg.getUnknown01()); + toMove.setInBuildingID(msg.getTargetID()); + msg.setStartCoord(ZoneManager.convertWorldToLocal(building, toMove.getLoc())); + + if (toMove.getObjectType() == GameObjectType.PlayerCharacter) { + if (convertLocEnd.distanceSquared2D(toMove.getLoc()) > 6000 * 6000) { + + Logger.info( "ENDLOC:" + convertLocEnd.x + ',' + convertLocEnd.y + ',' + convertLocEnd.z + + ',' + "GETLOC:" + toMove.getLoc().x + ',' + toMove.getLoc().y + ',' + toMove.getLoc().z + " Name " + ((PlayerCharacter) toMove).getCombinedName()); + toMove.teleport(toMove.getLoc()); + + return; + } + } + + startLocation = toMove.getLoc(); + endLocation = convertLocEnd; + + } else { + + toMove.setInBuilding(-1); + toMove.setInFloorID(-1); + toMove.setInBuildingID(0); + //SYNC PLAYER + toMove.teleport(toMove.getLoc()); + return; + } + + } else { + toMove.setInBuildingID(0); + toMove.setInFloorID(-1); + toMove.setInBuilding(-1); + msg.setStartCoord(toMove.getLoc()); + } + + //make sure we set the correct player. + msg.setSourceType(toMove.getObjectType().ordinal()); + msg.setSourceID(toMove.getObjectUUID()); + + //if player in region, modify location to local location of building. set target to building. + if (toMove.getRegion() != null){ + Building regionBuilding = Regions.GetBuildingForRegion(toMove.getRegion()); + if (regionBuilding != null){ + msg.setStartCoord(ZoneManager.convertWorldToLocal(Regions.GetBuildingForRegion(toMove.getRegion()), toMove.getLoc())); + msg.setEndCoord(ZoneManager.convertWorldToLocal(regionBuilding, endLocation)); + msg.setInBuilding(toMove.getRegion().level); + msg.setUnknown01(toMove.getRegion().room); + msg.setTargetType(GameObjectType.Building.ordinal()); + msg.setTargetID(regionBuilding.getObjectUUID()); + } + + }else{ + toMove.setInBuildingID(0); + toMove.setInFloorID(-1); + toMove.setInBuilding(-1); + msg.setStartCoord(toMove.getLoc()); + msg.setEndCoord(endLocation); + msg.setTargetType(0); + msg.setTargetID(0); + } + + //checks sync between character and server, if out of sync, teleport player to original position and return. + if (toMove.getObjectType() == GameObjectType.PlayerCharacter) { + boolean startLocInSync = checkSync(toMove, startLocation, toMove.getLoc()); + + if (!startLocInSync){ + syncLoc(toMove, toMove.getLoc(), startLocInSync); + return; + } + + } + + // set direction, based on the current location which has just been sync'd + // with the client and the calc'd destination + toMove.setFaceDir(endLocation.subtract2D(toMove.getLoc()).normalize()); + + boolean collide = false; + if (toMove.getObjectType().equals(GameObjectType.PlayerCharacter)) { + Vector3fImmutable collidePoint = Bounds.PlayerBuildingCollisionPoint((PlayerCharacter)toMove, toMove.getLoc(), endLocation); + + if (collidePoint != null) { + msg.setEndCoord(collidePoint); + endLocation = collidePoint; + collide = true; + } + + } + + if (toMove.getObjectType() == GameObjectType.PlayerCharacter && ((PlayerCharacter) toMove).isTeleportMode()) { + toMove.teleport(endLocation); + return; + } + + // move to end location, this can interrupt the current move + toMove.setEndLoc(endLocation); + + // ChatManager.chatSystemInfo((PlayerCharacter)toMove, "Moving to " + Vector3fImmutable.toString(endLocation)); + + // make sure server knows player is not sitting + toMove.setSit(false); + + // cancel any effects that break upon movement + toMove.cancelOnMove(); + + //cancel any attacks for manual move. + if ((toMove.getObjectType() == GameObjectType.PlayerCharacter) && msg.getUnknown02() == 0) + toMove.setCombatTarget(null); + + + // If it's not a player moving just send the message + + if ((toMove.getObjectType() == GameObjectType.PlayerCharacter) == false) { + DispatchMessage.sendToAllInRange(toMove, msg); + return; + } + + // If it's a player who is moving then we need to handle characters + // who should see the message via group follow + + PlayerCharacter player = (PlayerCharacter) toMove; + + player.setTimeStamp("lastMoveGate", System.currentTimeMillis()); + + if (collide) + DispatchMessage.dispatchMsgToInterestArea(player, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + else + DispatchMessage.dispatchMsgToInterestArea(player, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); + + + // Handle formation movement if needed + + if (player.getFollow() == false) + return; + + + City cityObject = null; + Zone serverZone = null; + + serverZone = ZoneManager.findSmallestZone(player.getLoc()); + cityObject = (City) DbManager.getFromCache(GameObjectType.City, serverZone.getPlayerCityUUID()); + + // Do not send group messages if player is on grid + + if (cityObject != null) + return; + + // If player is not in a group we can exit here + + Group group = GroupManager.getGroup(player); + + if (group == null) + return; + + // Echo group movement messages + + if (group.getGroupLead().getObjectUUID() == player.getObjectUUID()) + moveGroup(player, player.getClientConnection(), msg); + + } + + /** + * compare client and server location to verify that the two are in sync + * + * @param ac the player character + * @param clientLoc location as reported by the client + * @param serverLoc location known to the server + * @return true if the two are in sync + */ + private static boolean checkSync(AbstractCharacter ac, Vector3fImmutable clientLoc, Vector3fImmutable serverLoc) { + + float desyncDist = clientLoc.distanceSquared2D(serverLoc); + + // desync logging + if (MBServerStatics.MOVEMENT_SYNC_DEBUG) + if (desyncDist > MBServerStatics.MOVEMENT_DESYNC_TOLERANCE * MBServerStatics.MOVEMENT_DESYNC_TOLERANCE) + // our current location server side is a calc of last known loc + direction + speed and known time of last update + Logger.debug("Movement out of sync for " + ac.getFirstName() + + ", Server Loc: " + serverLoc.getX() + ' ' + serverLoc.getZ() + + " , Client loc: " + clientLoc.getX() + ' ' + clientLoc.getZ() + + " desync distance " + desyncDist + + " moving=" + ac.isMoving()); + else + Logger.debug( "Movement sync is good - desyncDist = " + desyncDist); + + if (ac.getDebug(1) && ac.getObjectType().equals(GameObjectType.PlayerCharacter)) + if (desyncDist > MBServerStatics.MOVEMENT_DESYNC_TOLERANCE * MBServerStatics.MOVEMENT_DESYNC_TOLERANCE) { + PlayerCharacter pc = (PlayerCharacter) ac; + ChatManager.chatSystemInfo(pc, + "Movement out of sync for " + ac.getFirstName() + + ", Server Loc: " + serverLoc.getX() + ' ' + serverLoc.getZ() + + " , Client loc: " + clientLoc.getX() + ' ' + clientLoc.getZ() + + " desync distance " + desyncDist + + " moving=" + ac.isMoving()); + } + + // return indicator that the two are in sync or not + return (desyncDist < 100f * 100f); + + } + + //Update for when the character is in flight + public static void updateFlight(PlayerCharacter pc, ChangeAltitudeMsg msg, int duration) { + if (pc == null) + return; + + // clear flight timer job as we are about to update stuff and submit a new job + pc.clearTimer(flightTimerJobName); + + if (!pc.isActive()) { + pc.setAltitude(0); + pc.setDesiredAltitude(0); + pc.setTakeOffTime(0); + return; + } + + // Check to see if we are mid height change + JobContainer cjc = pc.getTimers().get(changeAltitudeTimerJobName); + if (cjc != null) { + addFlightTimer(pc, msg, MBServerStatics.FLY_FREQUENCY_MS); + return; + } + + // Altitude is zero, do nothing + if (pc.getAltitude() < 1) + return; + + //make sure player is still allowed to fly + boolean canFly = false; + PlayerBonuses bonus = pc.getBonuses(); + + if (bonus != null && !bonus.getBool(ModType.NoMod, SourceType.Fly) && bonus.getBool(ModType.Fly, SourceType.None) && pc.isAlive()) + canFly = true; + + // if stam less that 2 - time to force a landing + if (pc.getStamina() < 10f || !canFly) { + + // dont call stop movement here as we want to + // preserve endloc + //pc.stopMovement(); + // sync world location + pc.setLoc(pc.getLoc()); + // force a landing + msg.setStartAlt(pc.getAltitude()); + msg.setTargetAlt(0); + msg.setAmountToMove(pc.getAltitude()); + msg.setUp(false); + DispatchMessage.dispatchMsgToInterestArea(pc, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + MovementManager.addChangeAltitudeTimer(pc, msg.getStartAlt(), msg.getTargetAlt(), (int) (MBServerStatics.HEIGHT_CHANGE_TIMER_MS * pc.getAltitude())); + pc.setAltitude(msg.getStartAlt() - 10); + + } else //Add a new flight timer to check stam / force land + if (pc.getAltitude() > 0) + addFlightTimer(pc, msg, MBServerStatics.FLY_FREQUENCY_MS); + + } + + public static void finishChangeAltitude(AbstractCharacter ac, float targetAlt) { + + if (ac.getObjectType().equals(GameObjectType.PlayerCharacter) == false) + return; + + //reset the getLoc timer before we clear other timers + // otherwise the next call to getLoc will not be correct + ac.resetLastSetLocUpdate(); + + // call getLoc once as it processes loc to the ms + Vector3fImmutable curLoc = ac.getLoc(); + + if (MBServerStatics.MOVEMENT_SYNC_DEBUG) + Logger.info("Finished Alt change, setting the end location to " + + ac.getEndLoc().getX() + ' ' + ac.getEndLoc().getZ() + + " moving=" + ac.isMoving() + + " and current location is " + curLoc.getX() + ' ' + curLoc.getZ()); + + if (ac.getDebug(1) && ac.getObjectType().equals(GameObjectType.PlayerCharacter)) + ChatManager.chatSystemInfo((PlayerCharacter) ac, "Finished Alt change, setting the end location to " + ac.getEndLoc().getX() + ' ' + ac.getEndLoc().getZ() + " moving=" + ac.isMoving() + " and current location is " + curLoc.getX() + ' ' + curLoc.getZ()); + + //Send run/walk/sit/stand to tell the client we are flying / landing etc + ac.update(); + ac.stopMovement(ac.getLoc()); + if (ac.isAlive()) + MovementManager.sendRWSSMsg(ac); + + //Check collision again + } + + + // Handle formation movement in group + + public static void moveGroup(PlayerCharacter pc, ClientConnection origin, MoveToPointMsg msg) throws MsgSendException { + // get forward vector + Vector3f faceDir = new Vector3f(pc.getFaceDir().x, 0, pc.getFaceDir().z).normalize(); + // get perpendicular vector + Vector3f crossDir = new Vector3f(faceDir.z, 0, -faceDir.x); + + //get source loc with altitude + Vector3f sLoc = new Vector3f(pc.getLoc().x, pc.getAltitude(), pc.getLoc().z); + + Group group = GroupManager.getGroup(pc); + Set members = group.getMembers(); + int pos = 0; + for (PlayerCharacter member : members) { + + if (member == null) + continue; + if (member.getObjectUUID() == pc.getObjectUUID()) + continue; + + MoveToPointMsg groupMsg = new MoveToPointMsg(msg); + + // Verify group member should be moved + + pos++; + if (member.getFollow() != true) + continue; + + //get member loc with altitude, then range against source loc + Vector3f mLoc = new Vector3f(member.getLoc().x, member.getAltitude(), member.getLoc().z); + + if (sLoc.distanceSquared2D(mLoc) > sqr(MBServerStatics.FORMATION_RANGE)) + continue; + + //don't move if player has taken damage from another player in last 60 seconds + long lastAttacked = System.currentTimeMillis() - pc.getLastPlayerAttackTime(); + if (lastAttacked < 60000) + continue; + + if (!member.isAlive()) + continue; + + //don't move if player is stunned or rooted + PlayerBonuses bonus = member.getBonuses(); + if (bonus.getBool(ModType.Stunned, SourceType.None) || bonus.getBool(ModType.CannotMove, SourceType.None)) + continue; + + member.update(); + + + // All checks passed, let's move the player + // First get the offset position + Vector3f offset = Formation.getOffset(group.getFormation(), pos); + Vector3fImmutable destination = pc.getEndLoc(); + // offset forwards or backwards + destination = destination.add(faceDir.mult(offset.z)); + // offset left or right + destination = destination.add(crossDir.mult(offset.x)); + // ArrayList awoList = WorldGrid.INSTANCE.getObjectsInRangePartial(member, member.getLoc().distance2D(destination) +1000, MBServerStatics.MASK_BUILDING); + // + // boolean skip = false; + // + // for (AbstractWorldObject awo: awoList){ + // Building building = (Building)awo; + // + // if (building.getBounds() != null){ + // if (Bounds.collide(building, member.getLoc(), destination)){ + // skip = true; + // break; + // } + // + // } + // + // } + // + // if (skip) + // continue; + // if (member.isMoving()) + // member.stopMovement(); + + // Update player speed to match group lead speed and make standing + if (member.isSit() || (member.isWalk() != pc.isWalk())) { + member.setSit(false); + member.setWalkMode(pc.isWalk()); + MovementManager.sendRWSSMsg(member); + } + + //cancel any effects that break upon movement + member.cancelOnMove(); + + // send movement for other players to see + groupMsg.setSourceID(member.getObjectUUID()); + groupMsg.setStartCoord(member.getLoc()); + groupMsg.setEndCoord(destination); + groupMsg.clearTarget(); + DispatchMessage.sendToAllInRange(member, groupMsg); + + // update group member + member.setFaceDir(destination.subtract2D(member.getLoc()).normalize()); + member.setEndLoc(destination); + } + } + + //Getting rid of flgith timer. + + public static void addFlightTimer(PlayerCharacter pc, ChangeAltitudeMsg msg, int duration) { + if (pc == null || pc.getTimers() == null) + return; + if (!pc.getTimers().containsKey(flightTimerJobName)) { + FlightJob ftj = new FlightJob(pc, msg, duration); + JobContainer jc = JobScheduler.getInstance().scheduleJob(ftj, duration); + pc.getTimers().put(flightTimerJobName, jc); + } + } + + public static void addChangeAltitudeTimer(PlayerCharacter pc, float startAlt, float targetAlt, int duration) { + if (pc == null || pc.getTimers() == null) + return; + ChangeAltitudeJob catj = new ChangeAltitudeJob(pc, startAlt, targetAlt); + JobContainer jc = JobScheduler.getInstance().scheduleJob(catj, duration); + pc.getTimers().put(changeAltitudeTimerJobName, jc); + } + + + public static void translocate(AbstractCharacter teleporter, Vector3fImmutable targetLoc, Regions region) { + + + if (targetLoc == null) + return; + + + Vector3fImmutable oldLoc = new Vector3fImmutable(teleporter.getLoc()); + + + teleporter.stopMovement(targetLoc); + + teleporter.setRegion(region); + + + + //mobs ignore region sets for now. + if (teleporter.getObjectType().equals(GameObjectType.Mob)){ + teleporter.setInBuildingID(0); + teleporter.setInBuilding(-1); + teleporter.setInFloorID(-1); + TeleportToPointMsg msg = new TeleportToPointMsg(teleporter, targetLoc.getX(), targetLoc.getY(), targetLoc.getZ(), 0, -1, -1); + DispatchMessage.dispatchMsgToInterestArea(oldLoc, teleporter, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); + return; + } + TeleportToPointMsg msg = new TeleportToPointMsg(teleporter, targetLoc.getX(), targetLoc.getY(), targetLoc.getZ(), 0, -1, -1); + //we shouldnt need to send teleport message to new area, as loadjob should pick it up. + // DispatchMessage.dispatchMsgToInterestArea(teleporter, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + DispatchMessage.dispatchMsgToInterestArea(oldLoc, teleporter, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + + if (teleporter.getObjectType().equals(GameObjectType.PlayerCharacter)) + InterestManager.INTERESTMANAGER.HandleLoadForTeleport((PlayerCharacter)teleporter); + + } + + public static void translocateToObject(AbstractCharacter teleporter, AbstractWorldObject worldObject) { + + + + Vector3fImmutable targetLoc = teleporter.getLoc(); + + Vector3fImmutable oldLoc = new Vector3fImmutable(teleporter.getLoc()); + + teleporter.stopMovement(teleporter.getLoc()); + + + + + + //mobs ignore region sets for now. + if (teleporter.getObjectType().equals(GameObjectType.Mob)){ + teleporter.setInBuildingID(0); + teleporter.setInBuilding(-1); + teleporter.setInFloorID(-1); + TeleportToPointMsg msg = new TeleportToPointMsg(teleporter, targetLoc.getX(), targetLoc.getY(), targetLoc.getZ(), 0, -1, -1); + DispatchMessage.dispatchMsgToInterestArea(oldLoc, teleporter, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); + return; + } + boolean collide = false; + int maxFloor = -1; + int buildingID = 0; + boolean isGroundLevel = false; + HashSet buildings = WorldGrid.getObjectsInRangePartial(teleporter, 200, MBServerStatics.MASK_BUILDING); + for (AbstractWorldObject awo : buildings) { + Building building = (Building) awo; + if (collide) + break; + } + if (!collide) { + teleporter.setInBuildingID(0); + teleporter.setInBuilding(-1); + teleporter.setInFloorID(-1); + } else { + if (isGroundLevel) { + teleporter.setInBuilding(0); + teleporter.setInFloorID(-1); + } else { + teleporter.setInBuilding(maxFloor - 1); + teleporter.setInFloorID(0); + } + } + + + TeleportToPointMsg msg = new TeleportToPointMsg(teleporter, targetLoc.getX(), targetLoc.getY(), targetLoc.getZ(), 0, -1, -1); + //we shouldnt need to send teleport message to new area, as loadjob should pick it up. + // DispatchMessage.dispatchMsgToInterestArea(teleporter, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + DispatchMessage.dispatchMsgToInterestArea(oldLoc, teleporter, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + + if (teleporter.getObjectType().equals(GameObjectType.PlayerCharacter)) + InterestManager.INTERESTMANAGER.HandleLoadForTeleport((PlayerCharacter)teleporter); + + } + + private static void syncLoc(AbstractCharacter ac, Vector3fImmutable clientLoc, boolean useClientLoc) { + ac.teleport(ac.getLoc()); + } +} diff --git a/src/engine/gameManager/PowersManager.java b/src/engine/gameManager/PowersManager.java new file mode 100644 index 00000000..4dbca015 --- /dev/null +++ b/src/engine/gameManager/PowersManager.java @@ -0,0 +1,2790 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.gameManager; + +import engine.Enum.*; +import engine.InterestManagement.HeightMap; +import engine.InterestManagement.WorldGrid; +import engine.job.AbstractJob; +import engine.job.AbstractScheduleJob; +import engine.job.JobContainer; +import engine.job.JobScheduler; +import engine.jobs.*; +import engine.math.Vector3fImmutable; +import engine.net.ByteBufferWriter; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.*; +import engine.objects.*; +import engine.powers.*; +import engine.powers.effectmodifiers.AbstractEffectModifier; +import engine.powers.poweractions.AbstractPowerAction; +import engine.powers.poweractions.TrackPowerAction; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; + +import static engine.math.FastMath.sqr; + +public enum PowersManager { + + POWERMANAGER; + + public static HashMap powersBaseByIDString = new HashMap<>(); + public static HashMap powersBaseByToken = new HashMap<>(); + public static HashMap effectsBaseByIDString = new HashMap<>(); + public static HashMap effectsBaseByToken = new HashMap<>(); + public static HashMap powerActionsByIDString = new HashMap<>(); + public static HashMap powerActionsByID = new HashMap<>(); + public static HashMap ActionTokenByIDString = new HashMap<>(); + public static HashMap modifiersByToken = new HashMap<>(); + public static HashMap AnimationOverrides = new HashMap<>(); + private static JobScheduler js; + + public static void initPowersManager(boolean fullPowersLoad) { + + if (fullPowersLoad) + PowersManager.InitializePowers(); + else + PowersManager.InitializeLoginPowers(); + + PowersManager.js = JobScheduler.getInstance(); + + } + + private PowersManager() { + + } + + public static PowersBase getPowerByToken(int token) { + return PowersManager.powersBaseByToken.get(token); + } + + public static PowersBase getPowerByIDString(String IDString) { + return PowersManager.powersBaseByIDString.get(IDString); + } + + public static EffectsBase getEffectByIDString(String IDString) { + return PowersManager.effectsBaseByIDString.get(IDString); + } + + public static AbstractPowerAction getPowerActionByID(Integer id) { + return PowersManager.powerActionsByID.get(id); + } + + public static AbstractPowerAction getPowerActionByIDString(String IDString) { + return PowersManager.powerActionsByIDString.get(IDString); + } + + public static EffectsBase getEffectByToken(int token) { + return PowersManager.effectsBaseByToken.get(token); + } + + // This pre-loads only PowersBase for login + public static void InitializeLoginPowers() { + + // get all PowersBase + ArrayList pbList = PowersBase.getAllPowersBase(); + + for (PowersBase pb : pbList) { + if (pb.getToken() != 0) + PowersManager.powersBaseByToken.put(pb.getToken(), pb); + } + } + + // This pre-loads all powers and effects + public static void InitializePowers() { + + // Add EffectsBase + ArrayList ebList = EffectsBase.getAllEffectsBase(); + + for (EffectsBase eb : ebList) { + PowersManager.effectsBaseByToken.put(eb.getToken(), eb); + PowersManager.effectsBaseByIDString.put(eb.getIDString(), eb); + + } + + // Add Fail Conditions + EffectsBase.getFailConditions(PowersManager.effectsBaseByIDString); + + // Add Modifiers to Effects + AbstractEffectModifier.getAllEffectModifiers(); + + // Add Source Types to Effects + PowersManager.addAllSourceTypes(); + PowersManager.addAllAnimationOverrides(); + + // Add PowerActions + AbstractPowerAction.getAllPowerActions(PowersManager.powerActionsByIDString, PowersManager.powerActionsByID, PowersManager.effectsBaseByIDString); + + // Load valid Item Flags + // AbstractPowerAction.loadValidItemFlags(PowersManager.powerActionsByIDString); + + // get all PowersBase + ArrayList pbList = PowersBase.getAllPowersBase(); + for (PowersBase pb : pbList) { + if (pb.getToken() != 0) { + PowersManager.powersBaseByIDString.put(pb.getIDString(), pb); + PowersManager.powersBaseByToken.put(pb.getToken(), pb); + } + } + + // Add Power Prereqs + PowerPrereq.getAllPowerPrereqs(PowersManager.powersBaseByIDString); + // Add Fail Conditions + PowersBase.getFailConditions(PowersManager.powersBaseByIDString); + // Add Actions Base + ActionsBase.getActionsBase(PowersManager.powersBaseByIDString, + PowersManager.powerActionsByIDString); + + } + + private static void addAllSourceTypes() { + PreparedStatementShared ps = null; + try { + ps = new PreparedStatementShared("SELECT * FROM static_power_sourcetype"); + ResultSet rs = ps.executeQuery(); + String IDString, source; + while (rs.next()) { + IDString = rs.getString("IDString"); + int token = DbManager.hasher.SBStringHash(IDString); + + + source = rs.getString("source").replace("-", "").trim(); + EffectSourceType effectSourceType = EffectSourceType.GetEffectSourceType(source); + + if (EffectsBase.effectSourceTypeMap.containsKey(token) == false) + EffectsBase.effectSourceTypeMap.put(token, new HashSet<>()); + + EffectsBase.effectSourceTypeMap.get(token).add(effectSourceType); + } + rs.close(); + } catch (Exception e) { + Logger.error( e); + } finally { + ps.release(); + } + } + + private static void addAllAnimationOverrides() { + PreparedStatementShared ps = null; + try { + ps = new PreparedStatementShared("SELECT * FROM static_power_animation_override"); + ResultSet rs = ps.executeQuery(); + String IDString; + int animation; + while (rs.next()) { + IDString = rs.getString("IDString"); + + EffectsBase eb = PowersManager.getEffectByIDString(IDString); + if (eb != null) + IDString = eb.getIDString(); + + animation = rs.getInt("animation"); + PowersManager.AnimationOverrides.put(IDString, animation); + + } + rs.close(); + } catch (Exception e) { + Logger.error( e); + } finally { + ps.release(); + } + } + + public static EffectsBase setEffectToken(int ID, int token) { + for (EffectsBase eb : PowersManager.effectsBaseByIDString.values()) { + if (eb.getUUID() == ID) { + eb.setToken(token); + return eb; + } + } + return null; + } + + public static void usePower(final PerformActionMsg msg, ClientConnection origin, + boolean sendCastToSelf) { + + if (usePowerA(msg, origin, sendCastToSelf)) { + // Cast failed for some reason, reset timer + + RecyclePowerMsg recyclePowerMsg = new RecyclePowerMsg(msg.getPowerUsedID()); + Dispatch dispatch = Dispatch.borrow(origin.getPlayerCharacter(), recyclePowerMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + + // Send Fail to cast message + PlayerCharacter pc = SessionManager + .getPlayerCharacter(origin); + + if (pc != null) { + sendPowerMsg(pc, 2, msg); + if (pc.isCasting()){ + pc.update(); + } + + pc.setIsCasting(false); + } + + } + } + + public static void useMobPower(Mob caster, AbstractCharacter target, PowersBase pb, int rank) { + + PerformActionMsg msg = createPowerMsg(pb, rank, caster, target); + msg.setUnknown04(1); + + if (useMobPowerA(msg, caster)) { + //sendMobPowerMsg(caster,2,msg); //Lol wtf was i thinking sending msg's to mobs... ZZZZ + } + } + + public static boolean usePowerA(final PerformActionMsg msg, ClientConnection origin, + boolean sendCastToSelf) { + PlayerCharacter playerCharacter = SessionManager.getPlayerCharacter( + origin); + if (playerCharacter == null) + return false; + + boolean CSRCast = false; + + + if (MBServerStatics.POWERS_DEBUG) { + ChatManager.chatSayInfo( + playerCharacter, + "Using Power: " + Integer.toHexString(msg.getPowerUsedID()) + + " (" + msg.getPowerUsedID() + ')'); + Logger.info( "Using Power: " + + Integer.toHexString(msg.getPowerUsedID()) + " (" + + msg.getPowerUsedID() + ')'); + } + + //Sending recycle message to player if died while casting. + if (!playerCharacter.isAlive() && msg.getPowerUsedID() != 428589216) { //succor + + RecyclePowerMsg recyclePowerMsg = new RecyclePowerMsg(msg.getPowerUsedID()); + Dispatch dispatch = Dispatch.borrow(playerCharacter, recyclePowerMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + + return false; + } + + + + + // if (!pc.getPowers().contains(msg.getPowerUsedID())) { + // sendPowerMsg(pc, 10, msg); + // return false; + // } + // verify recycle timer is not active for power, skip for a CSR + if (playerCharacter.getRecycleTimers().containsKey(msg.getPowerUsedID()) && !playerCharacter.isCSR()) { + // ChatManager.chatSayInfo(pc, "Recycle timer not finished!"); + + Logger.warn("usePowerA(): Cheat attempted? '" + msg.getPowerUsedID() + "' recycle timer not finished " + playerCharacter.getName()); + return false; + } + + // get power + PowersBase pb = PowersManager.powersBaseByToken.get(msg.getPowerUsedID()); + if (pb == null) { + ChatManager.chatSayInfo(playerCharacter, + "This power is not implemented yet."); + + // Logger.error("usePowerA(): Power '" + + // msg.getPowerUsedID() + // + "' was not found on powersBaseByToken map."); + return true; + // return false; + } + + if (playerCharacter.getLastPower() != null) + return true; + + //Check if Power Target is allowed to cast. + + + // Check powers for normal users + if (playerCharacter.getPowers() == null || !playerCharacter.getPowers().containsKey(msg.getPowerUsedID())) + if (!playerCharacter.isCSR()) { + if (!MBServerStatics.POWERS_DEBUG) { + // ChatManager.chatSayInfo(pc, "You may not cast that spell!"); + + Logger.info("usePowerA(): Cheat attempted? '" + msg.getPowerUsedID() + "' was not associated with " + playerCharacter.getName()); + return true; + } + } else + CSRCast = true; + + // get numTrains for power + int trains = msg.getNumTrains(); + + // can't go over the max trains for the power, unless CSR + if (trains > pb.getMaxTrains() && !playerCharacter.isCSR()) { + trains = pb.getMaxTrains(); + msg.setNumTrains(trains); + } + + // can't go over total trains by player + if (playerCharacter.getPowers() != null && playerCharacter.getPowers().containsKey(msg.getPowerUsedID())) { + CharacterPower cp = playerCharacter.getPowers().get(msg.getPowerUsedID()); + if (cp != null) { + int tot = cp.getTotalTrains(); + if (tot == 0 && !playerCharacter.isCSR()) + return false; + if (trains != tot && !playerCharacter.isCSR()) { + trains = tot; + msg.setNumTrains(trains); + } + } + } + + // get recycle time in ms + int time = pb.getRecycleTime(trains); + + // verify player is in correct mode (combat/nonCombat) + if (playerCharacter.isCombat()) { + if (!pb.allowedInCombat()) + // ChatManager.chatPowerError(pc, + // "This power is not allowed in combat mode."); + return true; + } else if (!pb.allowedOutOfCombat()) + // ChatManager.chatPowerError(pc, + // "You must be in combat mode to use this power."); + return true; + + // verify player is not stunned or prohibited from casting + PlayerBonuses bonus = playerCharacter.getBonuses(); +SourceType sourceType = SourceType.GetSourceType(pb.getCategory()); + if (bonus != null && (bonus.getBool(ModType.Stunned,SourceType.None) || bonus.getBool(ModType.CannotCast, SourceType.None) || bonus.getBool(ModType.BlockedPowerType, sourceType))) + return true; + + // if moving make sure spell valid for movement + Vector3fImmutable endLoc = playerCharacter.getEndLoc(); + + + if (!pb.canCastWhileMoving()) + if (playerCharacter.isMoving()){ + + // if movement left is less then 1 seconds worth then let cast + // go through. + float distanceLeftSquared = endLoc.distanceSquared2D(playerCharacter.getLoc()); + + if (distanceLeftSquared > sqr(playerCharacter.getSpeed())) + return true; + } + // if flying, make sure spell valid for flying. + // if (pc.getAltitude() > 0) + // if (!pb.canCastWhileFlying()) + // return true; + + int targetLiveCounter = -1; + + // get target based on targetType; + if (pb.targetFromLastTarget() || pb.targetPet()) // use msg's target + if (pb.isAOE()) { + if (!pb.usePointBlank()) { + AbstractWorldObject target = getTarget(msg); + + + + if (target != null && target.getObjectType() == GameObjectType.Building && !pb.targetBuilding()) { + PowersManager.sendPowerMsg(playerCharacter, 9, new PerformActionMsg(msg)); + return true; + } + + if (target == null) { + if (playerCharacter.getLoc().distanceSquared2D(msg.getTargetLoc()) > sqr(pb + .getRange())) + return true; + } else if (verifyInvalidRange(playerCharacter, target, pb.getRange())) + // pc.getLoc().distance(target.getLoc()) > + // pb.getRange()) + return true; + + + } + } else { + // get target + AbstractWorldObject target = getTarget(msg); + + if (target == null) + return true; + + if (!target.isAlive() && target.getObjectType().equals(GameObjectType.Building) == false && msg.getPowerUsedID() != 428589216) + return true; + + float range = pb.getRange(); + // verify target is in range + + + if (verifyInvalidRange(playerCharacter, target, range)) + // (pc.getLoc().distance(target.getLoc()) > pb.getRange()) { + // TODO send message that target is out of range + return true; + + // verify target is valid type + if (!validateTarget(target, playerCharacter, pb)) + return true; + + + if (AbstractWorldObject.IsAbstractCharacter(target)) + targetLiveCounter = ((AbstractCharacter) target).getLiveCounter(); + } + + // verify regular player can cast spell, otherwise authenticate + if (!pb.regularPlayerCanCast()) { + Account a = SessionManager.getAccount(playerCharacter); + if (a.status.equals(AccountStatus.ADMIN) == false) { + Logger.warn("Player '" + playerCharacter.getName() + + "' is attempting to cast a spell outside of their own access level."); + return true; + } + } + + // verify player has proper effects applied to use power + if (pb.getEffectPrereqs().size() > 0 && playerCharacter.getEffects() != null) { + + boolean passed = false; + for (PowerPrereq pp : pb.getEffectPrereqs()) { + + EffectsBase eb = PowersManager.getEffectByIDString(pp.getEffect()); + + if (playerCharacter.containsEffect(eb.getToken())) { + passed = true; + break; + } + } + + if (!passed) + return true; + } + + //verify player has proper equipment to use power + if (pb.getEquipPrereqs().size() > 0) { + + boolean passed = false; + + for (PowerPrereq pp : pb.getEquipPrereqs()) { + + int slot = pp.mainHand() ? MBServerStatics.SLOT_MAINHAND : MBServerStatics.SLOT_OFFHAND; + + if (playerCharacter.validEquip(slot, pp.getMessage())) { + passed = true; //should have item in slot + break; + } else if (!pp.isRequired()) { + passed = true; //should not have item in slot + break; + } + } + if (!passed) + return true; + } + + // TODO if target immune to powers, cancel unless aoe + // Also make sure No.ImmuneToPowers is false for target + // if there's a different power waiting to finish, stop here + + + //get power cost and calculate any power cost modifiers + float cost = pb.getCost(trains); + + if (playerCharacter.isCSR()) + cost = 0; + + if (bonus != null) + cost *= (1 + bonus.getFloatPercentAll(ModType.PowerCost, SourceType.None)); + + if (playerCharacter.getAltitude() > 0) + cost *= 1.5f; + + // Verify player can afford to use power + //CCR toons dont use cost + + if (!playerCharacter.isCSR()){ + if (cost > 0) + if ((playerCharacter.getObjectTypeMask() & MBServerStatics.MASK_UNDEAD) != 0) + if (playerCharacter.getHealth() <= cost) + return true; + else { + playerCharacter.modifyHealth(-cost, playerCharacter, true); + ModifyHealthMsg mhm = new ModifyHealthMsg(playerCharacter, playerCharacter, -cost, + 0f, 0f, 0, null, + 9999, 0); + mhm.setOmitFromChat(1); + DispatchMessage.dispatchMsgToInterestArea(playerCharacter, mhm, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + } + else if (pb.useMana()) + if (playerCharacter.getMana() < cost) + return true; + else + playerCharacter.modifyMana(-cost, playerCharacter, true); + else if (pb.useStamina()) + if (playerCharacter.getStamina() < cost) + return true; + else + playerCharacter.modifyStamina(-cost, playerCharacter, true); + else if (playerCharacter.getHealth() <= cost) + return true; + else + playerCharacter.modifyHealth(-cost, playerCharacter, true); + } + + + // Validity checks passed, move on to casting spell + //get caster's live counter + int casterLiveCounter = playerCharacter.getLiveCounter(); + + // run recycle job for when cast is available again, don't bother adding the timer for CSRs + if (time > 0) { + FinishRecycleTimeJob frtj = new FinishRecycleTimeJob(playerCharacter, msg); + playerCharacter.getRecycleTimers().put(msg.getPowerUsedID(), js.scheduleJob(frtj, time)); + } else { + // else send recycle message to unlock power + RecyclePowerMsg recyclePowerMsg = new RecyclePowerMsg(msg.getPowerUsedID()); + Dispatch dispatch = Dispatch.borrow(playerCharacter, recyclePowerMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + } + + //what the fuck? +// // Send cast to other players +// if ((playerCharacter.getObjectTypeMask() & MBServerStatics.MASK_UNDEAD) != 0) +// msg.setUnknown04(2); // Vampire Race, use health? +// else +// msg.setUnknown04(1); // Regular Race, use mana? + int tr = msg.getNumTrains(); + DispatchMessage.dispatchMsgToInterestArea(playerCharacter, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, sendCastToSelf, false); + + //Make new msg.. + PerformActionMsg copyMsg = new PerformActionMsg(msg); + copyMsg.setNumTrains(tr); + + // make person casting stand up if spell (unless they're casting a chant which does not make them stand up) + if (pb.isSpell() && !pb.isChant() && playerCharacter.isSit()) { + playerCharacter.update(); + playerCharacter.setSit(false); + UpdateStateMsg updateStateMsg = new UpdateStateMsg(playerCharacter); + DispatchMessage.dispatchMsgToInterestArea(playerCharacter, updateStateMsg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + + } + + // update cast (use skill) fail condition + playerCharacter.cancelOnCast(); + + // update castSpell (use spell) fail condition if spell + if (pb.isSpell()) + playerCharacter.cancelOnSpell(); + + // get cast time in ms. + time = pb.getCastTime(trains); + + // set player is casting for regens + + + if (time > 100){ + playerCharacter.update(); + playerCharacter.setIsCasting(true); + } + + + playerCharacter.setLastMovementState(playerCharacter.getMovementState()); + // update used power timer + playerCharacter.setLastUsedPowerTime(); + + // run timer job to end cast + if (time < 1) // run immediately + finishUsePower(copyMsg, playerCharacter, casterLiveCounter, targetLiveCounter); + else { + UsePowerJob upj = new UsePowerJob(playerCharacter, copyMsg, copyMsg.getPowerUsedID(), pb, casterLiveCounter, targetLiveCounter); + JobContainer jc = js.scheduleJob(upj, time); + + // make lastPower + playerCharacter.setLastPower(jc); + } + + if (CSRCast) + Logger.info("CSR " + playerCharacter.getName() + " cast power " + msg.getPowerUsedID() + '.'); + + return false; + } + + public static void testPowers(ByteBufferWriter writer) { + writer.putInt(powersBaseByToken.size()); + for (int token : powersBaseByToken.keySet()) { + writer.putInt(token); + writer.putInt(40); + } + } + + public static boolean useMobPowerA(PerformActionMsg msg, Mob caster) { + if (caster == null) + return false; + + + if (!caster.isAlive() && msg.getPowerUsedID() != 428589216) //succor + return false; + + // get power + PowersBase pb = PowersManager.powersBaseByToken.get(msg.getPowerUsedID()); + if (pb == null) + return true; + + // Check powers for normal users + // get numTrains for power + int trains = msg.getNumTrains(); + + // can't go over the max trains for the power, unless CSR + // can't go over total trains by player + // get recycle time in ms + int time = pb.getRecycleTime(trains); + + // verify player is in correct mode (combat/nonCombat) + // verify player is not stunned or prohibited from casting + PlayerBonuses bonus = caster.getBonuses(); + SourceType sourceType = SourceType.GetSourceType(pb.getCategory()); + if (bonus != null && (bonus.getBool(ModType.Stunned, SourceType.None) || bonus.getBool(ModType.CannotCast, SourceType.None) || bonus.getBool(ModType.BlockedPowerType, sourceType))) + return true; + + // if moving make sure spell valid for movement + // if flying, make sure spell valid for flying. + // if (pc.getAltitude() > 0) + // if (!pb.canCastWhileFlying()) + // return true; + int targetLiveCounter = -1; + + // get target based on targetType; + if (pb.targetFromLastTarget() || pb.targetPet()) // use msg's target + if (pb.isAOE()) { + if (!pb.usePointBlank()) { + AbstractWorldObject target = getTarget(msg); + + + if (target == null) { + + if (caster.getLoc().distanceSquared2D(msg.getTargetLoc()) > sqr(pb + .getRange())) + return true; + } else if (verifyInvalidRange(caster, target, pb.getRange())) + // pc.getLoc().distance(target.getLoc()) > + // pb.getRange()) + return true; + } + } else { + // get target + AbstractWorldObject target = getTarget(msg); + + if (target == null) + return true; + + // verify target is in range + if (verifyInvalidRange(caster, target, pb.getRange())) + // (pc.getLoc().distance(target.getLoc()) > pb.getRange()) { + // TODO send message that target is out of range + return true; + + // verify target is valid type + if (AbstractWorldObject.IsAbstractCharacter(target)) + targetLiveCounter = ((AbstractCharacter) target).getLiveCounter(); + } + + // TODO if target immune to powers, cancel unless aoe + // Also make sure No.ImmuneToPowers is false for target + // if there's a different power waiting to finish, stop here + if (caster.getLastMobPowerToken() != 0) + return true; + + //get power cost and calculate any power cost modifiers + // Validity checks passed, move on to casting spell + //get caster's live counter + int casterLiveCounter = caster.getLiveCounter(); + + // run recycle job for when cast is available again, don't bother adding the timer for CSRs + // Send cast to other players + if (caster.getObjectTypeMask() == MBServerStatics.MASK_UNDEAD) + msg.setUnknown05(0); // Regular Race, use mana? + else + msg.setUnknown05(0); + + int tr = msg.getNumTrains(); + + msg.setNumTrains(9999); + + DispatchMessage.sendToAllInRange(caster, msg); + DispatchMessage.sendToAllInRange(caster, msg); + + msg.setNumTrains(tr); + + // make person casting stand up if spell (unless they're casting a chant which does not make them stand up) + // update cast (use skill) fail condition + caster.cancelOnCast(); + + // update castSpell (use spell) fail condition if spell + if (pb.isSpell()) + caster.cancelOnSpell(); + + // get cast time in ms. + time = pb.getCastTime(trains); + + // set player is casting for regens + caster.setIsCasting(true); + caster.setLastMobPowerToken(pb.getToken()); + + // run timer job to end cast + if (time < 1 || pb.getToken() == -1994153779){ + // run immediately + finishUseMobPower(msg, caster, casterLiveCounter, targetLiveCounter); + caster.setLastMobPowerToken(0); + } + + else { + caster.setLastMobPowerToken(pb.getToken()); + caster.setTimeStamp("FinishCast", System.currentTimeMillis() + (pb.getCastTime(trains))); + } + // finishUseMobPower(msg, caster, casterLiveCounter, targetLiveCounter); // UseMobPowerJob upj = new UseMobPowerJob(caster, msg, msg.getPowerUsedID(), pb, casterLiveCounter, targetLiveCounter); + // JobContainer jc = js.scheduleJob(upj, time); + // // make lastPower + + + return false; + } + + // called when a spell finishes casting. perform actions + public static void finishUsePower(final PerformActionMsg msg, PlayerCharacter playerCharacter, int casterLiveCounter, int targetLiveCounter) { + + PerformActionMsg performActionMsg; + Dispatch dispatch; + + if (playerCharacter == null || msg == null) + return; + + if (playerCharacter.isCasting()){ + playerCharacter.update(); + playerCharacter.updateStamRegen(-100); + } + + playerCharacter.resetLastSetLocUpdate(); + playerCharacter.setIsCasting(false); + // can't go over total trains by player + + + if (!playerCharacter.isAlive() || playerCharacter.getLiveCounter() != casterLiveCounter) { + playerCharacter.clearLastPower(); + finishRecycleTime(msg.getPowerUsedID(), playerCharacter, true); + + + // Let's do good OO. Clone message don't modify it. + + performActionMsg = new PerformActionMsg(msg); + performActionMsg.setNumTrains(9999); + performActionMsg.setUnknown04(2); + + dispatch = Dispatch.borrow(playerCharacter, performActionMsg); + DispatchMessage.dispatchMsgToInterestArea(playerCharacter, performActionMsg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + return; + } + + // set player is not casting for regens + + + // clear power. + playerCharacter.clearLastPower(); + + PowersBase pb = PowersManager.powersBaseByToken.get(msg.getPowerUsedID()); + + if (pb == null) { + Logger.error( + "finishUsePower(): Power '" + msg.getPowerUsedID() + + "' was not found on powersBaseByToken map."); + return; + } + + int trains = msg.getNumTrains(); + + // update used power timer + playerCharacter.setLastUsedPowerTime(); + + // verify player is not stunned or power type is blocked + PlayerBonuses bonus = playerCharacter.getBonuses(); + + if (bonus != null) { + if (bonus.getBool(ModType.Stunned, SourceType.None)) + return; + + SourceType sourceType = SourceType.GetSourceType(pb.getCategory()); + if (bonus.getBool(ModType.BlockedPowerType, sourceType)) { + finishRecycleTime(msg.getPowerUsedID(), playerCharacter, true); + return; + } + } + + // get target loc + Vector3fImmutable targetLoc = msg.getTargetLoc(); + + + if (pb.targetFromLastTarget() || pb.targetPet()) // use msg's target + if (pb.isAOE()) { + if (!pb.usePointBlank()) { + AbstractWorldObject mainTarget = getTarget(msg); + + float speedRange = 0; + if (AbstractWorldObject.IsAbstractCharacter(mainTarget)) { + speedRange = ((AbstractCharacter) mainTarget).getSpeed() * (pb.getCastTime(trains) * .001f); + } + + if (mainTarget != null && mainTarget.getObjectType() == GameObjectType.Building && !pb.targetBuilding()) { + PowersManager.sendPowerMsg(playerCharacter, 8, new PerformActionMsg(msg)); + return; + } + + if (mainTarget == null) { + + if (playerCharacter.getLoc().distanceSquared2D(msg.getTargetLoc()) > sqr(pb + .getRange())) { + sendPowerMsg(playerCharacter, 8, msg); + return; + } + } else if (verifyInvalidRange(playerCharacter, mainTarget, speedRange + pb.getRange())) { + + sendPowerMsg(playerCharacter, 8, msg); + return; + } + } + } else { + + // get target + AbstractWorldObject mainTarget = getTarget(msg); + + if (mainTarget == null) + return; + + float speedRange = 0; + if (AbstractWorldObject.IsAbstractCharacter(mainTarget)) { + speedRange = ((AbstractCharacter) mainTarget).getSpeed() * (pb.getCastTime(trains) * .001f); + } + float range = pb.getRange() + speedRange; + + + + if (verifyInvalidRange(playerCharacter, mainTarget, range)) { + + sendPowerMsg(playerCharacter, 8, msg); + return; + } + // (pc.getLoc().distance(target.getLoc()) > pb.getRange()) { + // TODO send message that target is out of range + + + } + + if (targetLoc.x == 0f || targetLoc.z == 0f) { + AbstractWorldObject tar = getTarget(msg); + if (tar != null) + targetLoc = tar.getLoc(); + } + + + // get list of targets + HashSet allTargets = getAllTargets( + getTarget(msg), msg.getTargetLoc(), playerCharacter, pb); + + // no targets found. send error message + + if (allTargets.size() == 0) { + sendPowerMsg(playerCharacter, 9, msg); + return; + } + + playerCharacter.setHateValue(pb.getHateValue(trains)); + + //Send Cast Message. +// PerformActionMsg castMsg = new PerformActionMsg(msg); +// castMsg.setNumTrains(9999); +// castMsg.setUnknown04(3); +// DispatchMessage.dispatchMsgToInterestArea(playerCharacter, castMsg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); +// + boolean msgCasted = false; + + for (AbstractWorldObject target : allTargets) { + + if (target == null) + continue; + + //Hacky Pyschic healing cross heal + + //make sure target hasn't respawned since we began casting + //skip this if liveCounter = -1 (from aoe) + + if (targetLiveCounter != -1) + if (AbstractWorldObject.IsAbstractCharacter(target)) + if (targetLiveCounter != ((AbstractCharacter) target).getLiveCounter()) + continue; + + if (!target.isAlive() && target.getObjectType() != GameObjectType.Building && pb.getToken() != 428589216 && pb.getToken() != 429425915) + continue; + + //make sure mob is awake to respond. + //if (target instanceof AbstractIntelligenceAgent) + //((AbstractIntelligenceAgent)target).enableIntelligence(); + // If Hit roll required, test hit + + boolean miss = false; + if (pb.requiresHitRoll() && !pb.isWeaponPower() && testAttack(playerCharacter, target, pb, msg)) { + miss = true; + //aggro mob even on a miss + if (target.getObjectType() == GameObjectType.Mob) { + Mob mobTarget = (Mob) target; + if (pb.isHarmful()) + mobTarget.handleDirectAggro(playerCharacter); + } + continue; + } + if (target.getObjectType() == GameObjectType.Mob) { + Mob mobTarget = (Mob) target; + if (pb.isHarmful()) + mobTarget.handleDirectAggro(playerCharacter); + } + //Power is aiding a target, handle aggro if combat target is a Mob. + if (!pb.isHarmful() && target.getObjectType() == GameObjectType.PlayerCharacter) { + PlayerCharacter pcTarget = (PlayerCharacter) target; + if (!pb.isHarmful()) + Mob.HandleAssistedAggro(playerCharacter, pcTarget); + } + + // update target of used power timer + + if (pb.isHarmful()) + if (target.getObjectType().equals(GameObjectType.PlayerCharacter) && target.getObjectUUID() != playerCharacter.getObjectUUID()) { + + ((PlayerCharacter) target).setLastTargetOfUsedPowerTime(); + ((PlayerCharacter) target).setTimeStamp("LastCombatPlayer", System.currentTimeMillis()); + playerCharacter.setTimeStamp("LastCombatPlayer", System.currentTimeMillis()); + } + + + //Player didn't miss power, send finish cast. Miss cast already sent. + + + // finally Apply actions + for (ActionsBase ab : pb.getActions()) { + // get numTrains for power, skip action if invalid + + if (trains < ab.getMinTrains() || trains > ab.getMaxTrains()) + continue; + // If something blocks the action, then stop + + if (ab.blocked(target, pb, trains)) { + + PowersManager.sendEffectMsg(playerCharacter, 5, ab, pb); + continue; + } + + // TODO handle overwrite stack order here + String stackType = ab.getStackType(); + stackType = (stackType.equals("IgnoreStack")) ? Integer.toString(ab.getUUID()) : stackType; + // if (!stackType.equals("IgnoreStack")) { + if (target.getEffects().containsKey(stackType)) { + // remove any existing power that overrides + Effect ef = target.getEffects().get(stackType); + AbstractEffectJob effect = null; + if (ef != null) { + JobContainer jc = ef.getJobContainer(); + if (jc != null) + effect = (AbstractEffectJob) jc.getJob(); + } + ActionsBase overwrite = effect.getAction(); + + if (overwrite == null) { + Logger.error("NULL ACTION FOR POWER " + effect.getPowerToken()); + continue; + } + + if (ab.getStackOrder() < overwrite.getStackOrder()) + continue; // not high enough to overwrite + else if (ab.getStackOrder() > overwrite.getStackOrder()) { + effect.setNoOverwrite(true); + removeEffect(target, overwrite, true, false); + } else if (ab.getStackOrder() == overwrite.getStackOrder()) + if (ab.greaterThanEqual() + && (trains >= effect.getTrains())) { + effect.setNoOverwrite(true); + removeEffect(target, overwrite, true, false); + } + + else if (ab.always()) + removeEffect(target, overwrite, true, false); + else if (ab.greaterThan() + && (trains > effect.getTrains())) { + effect.setNoOverwrite(true); + removeEffect(target, overwrite, true, false); + } else if (ab.greaterThan() && pb.getToken() == effect.getPowerToken()) + removeEffect(target, overwrite, true, false); + + else + continue; // trains not high enough to overwrite + + } + + // } + + + runPowerAction(playerCharacter, target, targetLoc, ab, trains, pb); + if (!miss && !msgCasted){ + PerformActionMsg castMsg = new PerformActionMsg(msg); + castMsg.setNumTrains(9999); + castMsg.setUnknown04(2); + DispatchMessage.dispatchMsgToInterestArea(playerCharacter, castMsg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + msgCasted = true; + } + } + } + + if ( !msgCasted){ + PerformActionMsg castMsg = new PerformActionMsg(msg); + castMsg.setNumTrains(9999); + castMsg.setUnknown04(2); + DispatchMessage.dispatchMsgToInterestArea(playerCharacter, castMsg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + msgCasted = true; + } + + //Handle chant + if (pb != null && pb.isChant()) + for (ActionsBase ab : pb.getActions()) { + AbstractPowerAction pa = ab.getPowerAction(); + if (pa != null) + if (pb.getToken() != 428950414 && pb.getToken() != 428884878) + pa.handleChant(playerCharacter, playerCharacter, targetLoc, trains, ab, pb); + else if (PowersManager.getTarget(msg) != null && PowersManager.getTarget(msg).isAlive()) + pa.handleChant(playerCharacter, PowersManager.getTarget(msg), targetLoc, trains, ab, pb); + else + pa.handleChant(playerCharacter, null, targetLoc, trains, ab, pb); + } + + + + + + //DispatchMessage.dispatchMsgToInterestArea(playerCharacter, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + + + } + + public static void finishUseMobPower(PerformActionMsg msg, Mob caster, int casterLiveCounter, int targetLiveCounter) { + + if (caster == null || msg == null) + return; + + if (!caster.isAlive() || caster.getLiveCounter() != casterLiveCounter) + return; + + // set player is not casting for regens + caster.setIsCasting(false); + + + PowersBase pb = PowersManager.powersBaseByToken.get(msg.getPowerUsedID()); + // clear power. + caster.setLastMobPowerToken(0); + + if (pb == null) { + Logger.error( + "finishUsePower(): Power '" + msg.getPowerUsedID() + + "' was not found on powersBaseByToken map."); + return; + } + + int trains = msg.getNumTrains(); + + // update used power timer + // verify player is not stunned or power type is blocked + PlayerBonuses bonus = caster.getBonuses(); + if (bonus != null) { + if (bonus.getBool(ModType.Stunned,SourceType.None)) + return; + SourceType sourceType = SourceType.GetSourceType(pb.getCategory()); + if (bonus.getBool(ModType.BlockedPowerType, sourceType)) + return; + } + + msg.setNumTrains(9999); + msg.setUnknown04(2); + DispatchMessage.sendToAllInRange(caster, msg); + + // get target loc + Vector3fImmutable targetLoc = msg.getTargetLoc(); + if (targetLoc.x == 0f || targetLoc.z == 0f) { + AbstractWorldObject tar = getTarget(msg); + if (tar != null) + targetLoc = tar.getLoc(); + } + + // get list of targets + HashSet allTargets = getAllTargets( + getTarget(msg), msg.getTargetLoc(), caster, pb); + for (AbstractWorldObject target : allTargets) { + + if (target == null) + continue; + + + //make sure target hasn't respawned since we began casting + //skip this if liveCounter = -1 (from aoe) + if (targetLiveCounter != -1) + if (AbstractWorldObject.IsAbstractCharacter(target)) + if (targetLiveCounter != ((AbstractCharacter) target).getLiveCounter()) + continue; + + //make sure mob is awake to respond. + //if (target instanceof AbstractIntelligenceAgent) + //((AbstractIntelligenceAgent)target).enableIntelligence(); + // If Hit roll required, test hit + if (pb.requiresHitRoll() && !pb.isWeaponPower() && testAttack(caster, target, pb, msg)) + //aggro mob even on a miss + continue; + + // update target of used power timer + + if (target.getObjectType().equals(GameObjectType.PlayerCharacter)) { + + ((PlayerCharacter) target).setLastTargetOfUsedPowerTime(); + ((PlayerCharacter) target).setTimeStamp("LastCombatPlayer", System.currentTimeMillis()); + } + + + // finally Apply actions + for (ActionsBase ab : pb.getActions()) { + // get numTrains for power, skip action if invalid + + if (trains < ab.getMinTrains() || trains > ab.getMaxTrains()) + continue; + // If something blocks the action, then stop + + if (ab.blocked(target, pb, trains)) + continue; + // TODO handle overwrite stack order here + String stackType = ab.getStackType(); + stackType = (stackType.equals("IgnoreStack")) ? Integer.toString(ab.getUUID()) : stackType; + // if (!stackType.equals("IgnoreStack")) { + if (target.getEffects().containsKey(stackType)) { + // remove any existing power that overrides + Effect ef = target.getEffects().get(stackType); + AbstractEffectJob effect = null; + if (ef != null) { + JobContainer jc = ef.getJobContainer(); + if (jc != null) + effect = (AbstractEffectJob) jc.getJob(); + } + ActionsBase overwrite = effect.getAction(); + + if (overwrite == null){ + Logger.error("NULL ACTION FOR EFFECT " + effect.getPowerToken()); + continue; + } + if (ab.getStackOrder() < overwrite.getStackOrder()) + continue; // not high enough to overwrite + else if (ab.getStackOrder() > overwrite.getStackOrder()) { + effect.setNoOverwrite(true); + removeEffect(target, overwrite, true, false); + } else if (ab.getStackOrder() == overwrite.getStackOrder()) + if (ab.greaterThanEqual() + && (trains >= effect.getTrains())) { + effect.setNoOverwrite(true); + removeEffect(target, overwrite, true, false); + } else if (ab.always()) + removeEffect(target, overwrite, true, false); + else if (ab.greaterThan() + && (trains > effect.getTrains())) { + effect.setNoOverwrite(true); + removeEffect(target, overwrite, true, false); + } else if (ab.greaterThan() && pb.getToken() == effect.getPowerToken()) + removeEffect(target, overwrite, true, false); + + else + continue; // trains not high enough to overwrite + } + + // } + runPowerAction(caster, target, targetLoc, ab, trains, pb); + } + } + + //Handle chant + if (pb != null && pb.isChant()) + for (ActionsBase ab : pb.getActions()) { + AbstractPowerAction pa = ab.getPowerAction(); + if (pa != null) + pa.handleChant(caster, caster, targetLoc, trains, ab, pb); + } + + // TODO echo power use to everyone else + msg.setNumTrains(9999); + msg.setUnknown04(2); + DispatchMessage.sendToAllInRange(caster, msg); + + } + + // *** Refactor : Wtf is this mess? + + private static boolean validMonsterType(AbstractWorldObject target, PowersBase pb) { + + if (pb == null || target == null) + return false; + + String mtp = pb.getMonsterTypePrereq(); + + if (mtp.length() == 0) + return true; + + if (target.getObjectType().equals(GameObjectType.PlayerCharacter)) { + + PlayerCharacter pc = (PlayerCharacter) target; + int raceID = 0; + + if (pc.getRace() != null) + raceID = pc.getRace().getRaceRuneID(); + + switch (mtp) { + case "Shade": + return raceID == 2015 || raceID == 2016; + case "Elf": + return raceID == 2008 || raceID == 2009; + case "Dwarf": + return raceID == 2006; + case "Aracoix": + return raceID == 2002 || raceID == 2003; + case "Irekei": + return raceID == 2013 || raceID == 2014; + case "Vampire": + return raceID == 2028 || raceID == 2029; + } + } else if (target.getObjectType().equals(GameObjectType.Mob)) { + Mob mob = (Mob) target; + + if (pb.targetMob() && !mob.isMob() && !mob.isSiege()) + return false; + else if (pb.targetPet() && !mob.isPet() && !mob.isSiege()) + return false; + + switch (mtp) { + case "Animal": + if ((mob.getObjectTypeMask() & MBServerStatics.MASK_BEAST) == 0) + return false; + break; + case "NPC": + if ((mob.getObjectTypeMask() & MBServerStatics.MASK_HUMANOID) == 0) + return false; + break; + case "Rat": + if ((mob.getObjectTypeMask() & MBServerStatics.MASK_RAT) == 0) + return false; + break; + case "Siege": + if (!mob.isSiege()) + return false; + break; + case "Undead": + if ((mob.getObjectTypeMask() & MBServerStatics.MASK_UNDEAD) == 0) + return false; + break; + } + return true; + } else return target.getObjectType().equals(GameObjectType.Building) && mtp.equals("Siege"); + return false; + } + + public static void summon(SendSummonsRequestMsg msg, ClientConnection origin) { + PlayerCharacter pc = SessionManager.getPlayerCharacter( + origin); + if (pc == null) + return; + + PlayerCharacter target = SessionManager + .getPlayerCharacterByLowerCaseName(msg.getTargetName()); + if (target == null || target.equals(pc) || target.isCombat()) { + + if (target == null) // Player not found. Send not found message + ChatManager.chatInfoError(pc, + "Cannot find that player to summon."); + else if (target.isCombat()) + ChatManager.chatInfoError(pc, + "Cannot summon player in combat."); + // else trying to summon self, just fail + + // recycle summon + sendRecyclePower(msg.getPowerToken(), origin); + + // TODO: client already subtracted 200 mana.. need to correct it + // end cast + PerformActionMsg pam = new PerformActionMsg(msg.getPowerToken(), + msg.getTrains(), msg.getSourceType(), msg.getSourceID(), 0, + 0, 0f, 0f, 0f, 1, 0); + sendPowerMsg(pc, 2, pam); + + return; + } + + PerformActionMsg pam = new PerformActionMsg(msg.getPowerToken(), msg + .getTrains(), msg.getSourceType(), msg.getSourceID(), target + .getObjectType().ordinal(), target.getObjectUUID(), 0f, 0f, 0f, 1, 0); + + // Client removes 200 mana on summon use.. so don't send message to self + target.addSummoner(pc.getObjectUUID(), System.currentTimeMillis() + MBServerStatics.FOURTYFIVE_SECONDS); + usePower(pam, origin, false); + } + + public static void recvSummon(RecvSummonsRequestMsg msg, ClientConnection origin) { + PlayerCharacter pc = SessionManager.getPlayerCharacter(origin); + if (pc == null) + return; + + PlayerCharacter source = PlayerCharacter.getFromCache(msg.getSourceID()); + if (source == null) + return; + + long tooLate = pc.getSummoner(source.getObjectUUID()); + if (tooLate < System.currentTimeMillis()) { + ChatManager.chatInfoError(pc, "You waited too long to " + (msg.accepted() ? "accept" : "decline") + " the summons."); + pc.removeSummoner(source.getObjectUUID()); + return; + } + + if (pc.getBonuses() != null && pc.getBonuses().getBool(ModType.BlockedPowerType, SourceType.SUMMON)) { + ErrorPopupMsg.sendErrorMsg(pc, "You have been blocked from receiving summons!"); + ErrorPopupMsg.sendErrorMsg(source, "Target is blocked from receiving summons!"); + pc.removeSummoner(source.getObjectUUID()); + return; + } + pc.removeSummoner(source.getObjectUUID()); + + // Handle Accepting or Denying a summons. + // set timer based on summon type. + boolean wentThrough = false; + if (msg.accepted()) + // summons accepted, let's move the player if within time + if (source.isAlive()) { + + // //make sure summons handled in time + ConcurrentHashMap timers = source.getTimers(); + // if (timers == null || !timers.containsKey("SummonSend")) { + // ChatManager.chatInfoError(pc, "You waited too long to " + (msg.accepted() ? "accept" : "decline") + " the summons."); + // return; + // } + + // // clear last summons accept timer + // timers.get("SummonSend").cancelJob(); + //timers.remove("SummonSend"); + // cancel any other summons waiting + timers = pc.getTimers(); + if (timers != null && timers.containsKey("Summon")) + timers.get("Summon").cancelJob(); + + // get time to wait before summons goes through + BaseClass base = source.getBaseClass(); + PromotionClass promo = source.getPromotionClass(); + int duration; + + + //determine if in combat with another player + + + //comment out this block to disable combat timer + // if (lastAttacked < 60000) { + // if (pc.inSafeZone()) //player in safe zone, no need for combat timer + // combat = false; + // else if (source.inSafeZone()) //summoner in safe zone, apply combat timer + // combat = true; + // else if ((source.getLoc().distance2D(pc.getLoc())) > 6144f) + // combat = true; //more than 1.5x width of zone, not tactical summons + // } + + if (promo != null && promo.getObjectUUID() == 2519) + duration = 10000; // Priest summons, 10 seconds + else if (base != null && base.getObjectUUID() == 2501) + duration = 15000; // Healer Summons, 15 seconds + else + duration = 45000; // Belgosh Summons, 45 seconds + + + // Teleport to summoners location + FinishSummonsJob fsj = new FinishSummonsJob(source, pc); + JobContainer jc = JobScheduler.getInstance().scheduleJob(fsj, + duration); + if (timers != null) + timers.put("Summon", jc); + wentThrough = true; + } + + // Summons failed + if (!wentThrough) + // summons refused. Let's be nice and reset recycle timer + if (source != null) { + + // Send summons refused Message + ErrorPopupMsg.sendErrorPopup(source, 29); + + // recycle summons power + //finishRecycleTime(428523680, source, true); + } + } + + public static void trackWindow(TrackWindowMsg msg, ClientConnection origin) { + + PlayerCharacter playerCharacter = SessionManager.getPlayerCharacter( + origin); + + if (playerCharacter == null) + return; + + if (MBServerStatics.POWERS_DEBUG) { + ChatManager.chatSayInfo( + playerCharacter, + "Using Power: " + Integer.toHexString(msg.getPowerToken()) + + " (" + msg.getPowerToken() + ')'); + Logger.info( "Using Power: " + + Integer.toHexString(msg.getPowerToken()) + " (" + + msg.getPowerToken() + ')'); + } + + // get track power used + PowersBase pb = PowersManager.powersBaseByToken.get(msg.getPowerToken()); + + if (pb == null || !pb.isTrack()) + return; + + //check track threshold timer to prevent spam + long currentTime = System.currentTimeMillis(); + long timestamp = playerCharacter.getTimeStamp("trackWindow"); + long dif = currentTime - timestamp; + if (dif < MBServerStatics.TRACK_WINDOW_THRESHOLD) + return; + playerCharacter.setTimeStamp("trackWindow", currentTime); + + ArrayList ablist = pb.getActions(); + if (ablist == null) + return; + + TrackPowerAction tpa = null; + for (ActionsBase ab : ablist) { + AbstractPowerAction apa = ab.getPowerAction(); + if (apa != null && apa instanceof TrackPowerAction) + tpa = (TrackPowerAction) apa; + } + if (tpa == null) + return; + + // Check powers for normal users + if (playerCharacter.getPowers() == null || !playerCharacter.getPowers().containsKey(msg.getPowerToken())) + if (!playerCharacter.isCSR()) + if (!MBServerStatics.POWERS_DEBUG) { + // ChatManager.chatSayInfo(pc, "You may not cast that spell!"); + // this.logEXPLOIT("usePowerA(): Cheat attempted? '" + msg.getPowerToken() + "' was not associated with " + pc.getName()); + return; + } + + // Get search mask for track + int mask = 0; + if (pb.targetPlayer()) + if (tpa.trackVampire()) // track vampires + mask = MBServerStatics.MASK_PLAYER | MBServerStatics.MASK_UNDEAD; + else + // track all players + mask = MBServerStatics.MASK_PLAYER; + else if (pb.targetCorpse()) // track corpses + mask = MBServerStatics.MASK_CORPSE; + else if (tpa.trackNPC()) // Track NPCs + mask = MBServerStatics.MASK_NPC; + else if (tpa.trackUndead()) // Track Undead + mask = MBServerStatics.MASK_MOB | MBServerStatics.MASK_UNDEAD; + else + // Track All + mask = MBServerStatics.MASK_MOB | MBServerStatics.MASK_NPC; + + // Find characters in range + HashSet allTargets; + allTargets = WorldGrid.getObjectsInRangeContains(playerCharacter.getLoc(), + pb.getRange(), mask); + + //remove anyone who can't be tracked + Iterator it = allTargets.iterator(); + while (it.hasNext()) { + AbstractWorldObject awo = it.next(); + if (awo == null) + continue; + else if (!awo.isAlive()) + it.remove(); + else if (awo.getObjectType().equals(GameObjectType.PlayerCharacter)) { + PlayerBonuses bonus = ((PlayerCharacter) awo).getBonuses(); + if (bonus != null && bonus.getBool(ModType.CannotTrack, SourceType.None)) + it.remove(); + } + } + + // get max charcters for window + int maxTargets = 20; + PromotionClass promo = playerCharacter.getPromotionClass(); + if (promo != null) { + int tableID = promo.getObjectUUID(); + if (tableID == 2512 || tableID == 2514 || tableID == 2515) + maxTargets = 40; + } + + // create list of characters + HashSet trackChars = RangeBasedAwo.getTrackList( + allTargets, playerCharacter, maxTargets); + + TrackWindowMsg trackWindowMsg = new TrackWindowMsg(msg); + + // send track window + trackWindowMsg.setSource(playerCharacter); + trackWindowMsg.setCharacters(trackChars); + + Dispatch dispatch = Dispatch.borrow(playerCharacter, trackWindowMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + } + + private static void sendRecyclePower(int token, ClientConnection origin) { + RecyclePowerMsg recyclePowerMsg = new RecyclePowerMsg(token); + + Dispatch dispatch = Dispatch.borrow(origin.getPlayerCharacter(), recyclePowerMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + + } + + public static boolean verifyInvalidRange(AbstractCharacter ac, + AbstractWorldObject target, float range) { + Vector3fImmutable sl = ac.getLoc(); + Vector3fImmutable tl = target.getLoc(); + if (target.getObjectType().equals(GameObjectType.Item)) { + + Item item = (Item) target; + AbstractGameObject owner = item.getOwner(); + + if (owner == null || owner.getObjectType().equals(GameObjectType.Account)) + return true; + + if (owner.getObjectType().equals(GameObjectType.PlayerCharacter) || owner.getObjectType().equals(GameObjectType.Mob)) { + AbstractCharacter acOwner = (AbstractCharacter) owner; + CharacterItemManager itemMan = acOwner.getCharItemManager(); + if (itemMan == null) + return true; + if (itemMan.inventoryContains(item)) { + tl = acOwner.getLoc(); + return !(sl.distanceSquared(tl) <= sqr(range)); + } + return true; + } + return true; + } + + range += (calcHitBox(ac) + calcHitBox(target)); + + + float distanceToTarget = sl.distanceSquared(tl);//distance to center of target + + return distanceToTarget > range * range; + + } + + public static float calcHitBox(AbstractWorldObject ac) { + //TODO Figure out how Str Affects HitBox + float hitBox = 1; + switch (ac.getObjectType()) { + case PlayerCharacter: + PlayerCharacter pc = (PlayerCharacter) ac; + if (MBServerStatics.COMBAT_TARGET_HITBOX_DEBUG) + Logger.info( "Hit box radius for " + pc.getFirstName() + " is " + ((int) pc.statStrBase / 200f)); + hitBox = 2f + (int) ((PlayerCharacter) ac).statStrBase / 50f; + break; + + case Mob: + Mob mob = (Mob) ac; + if (MBServerStatics.COMBAT_TARGET_HITBOX_DEBUG) + Logger.info( "Hit box radius for " + mob.getFirstName() + + " is " + ((Mob) ac).getMobBase().getHitBoxRadius()); + + hitBox = ((Mob) ac).getMobBase().getHitBoxRadius(); + break; + case Building: + Building building = (Building) ac; + if (building.getBlueprint() == null) + return 32; + hitBox = Math.max(building.getBlueprint().getBuildingGroup().getExtents().x, + building.getBlueprint().getBuildingGroup().getExtents().y); + if (MBServerStatics.COMBAT_TARGET_HITBOX_DEBUG) + Logger.info("Hit box radius for " + building.getName() + " is " + hitBox); + break; + + } + return hitBox; + } + + // Apply a power based on it's IDString + public static void applyPower(AbstractCharacter ac, AbstractWorldObject target, + Vector3fImmutable targetLoc, String ID, int trains, boolean fromItem) { + if (ac == null || target == null || !ac.isAlive()) + return; + PowersBase pb = powersBaseByIDString.get(ID); + if (pb == null) { + Logger.error( + "applyPower(): Got NULL on powersBaseByIDString table lookup for: " + + ID); + return; + } + applyPowerA(ac, target, targetLoc, pb, trains, fromItem); + } + + // Apply a power based on it's Token + public static void applyPower(AbstractCharacter ac, AbstractWorldObject target, + Vector3fImmutable targetLoc, int token, int trains, boolean fromItem) { + if (ac == null || target == null) + return; + + //Don't apply power if ac is dead, unless death shroud or safe mode + if (!ac.isAlive()) + if (!(token == -1661758934 || token == 1672601862)) + return; + + PowersBase pb = powersBaseByToken.get(token); + if (pb == null) { + Logger.error( + "applyPower(): Got NULL on powersBaseByToken table lookup for: " + + token); + return; + } + applyPowerA(ac, target, targetLoc, pb, trains, fromItem); + } + + private static void applyPowerA(AbstractCharacter ac, AbstractWorldObject target, + Vector3fImmutable targetLoc, PowersBase pb, int trains, + boolean fromItem) { + int time = pb.getCastTime(trains); + if (!fromItem) + finishApplyPowerA(ac, target, targetLoc, pb, trains, false); + else if (time == 0) + finishApplyPower(ac, target, targetLoc, pb, trains, ac.getLiveCounter()); + else { + + ac.setItemCasting(true); + int tarType = (target == null) ? 0 : target.getObjectType().ordinal(); + int tarID = (target == null) ? 0 : target.getObjectUUID(); + + // start the action animation + PerformActionMsg msg = new PerformActionMsg(pb.getToken(), + trains, ac.getObjectType().ordinal(), ac.getObjectUUID(), tarType, tarID, 0, + 0, 0, 1, 0); + DispatchMessage.sendToAllInRange(target, msg); + + + ConcurrentHashMap timers = ac.getTimers(); + + if (timers.containsKey(Integer.toString(pb.getToken()))) { + JobContainer jc = timers.get(Integer.toString(pb.getToken())); + if (jc != null) + jc.cancelJob(); + } + + // // clear any other items being used + // JobContainer jc = ac.getLastItem(); + // if (jc != null) { + // jc.cancelJob(); + // ac.clearLastItem(); + // } + // run timer job to end cast + UseItemJob uij = new UseItemJob(ac, target, pb, trains, ac.getLiveCounter()); + JobContainer jc = js.scheduleJob(uij, time); + + // make lastItem + timers.put(Integer.toString(pb.getToken()), jc); + } + } + + public static void finishApplyPower(AbstractCharacter ac, + AbstractWorldObject target, Vector3fImmutable targetLoc, + PowersBase pb, int trains, int liveCounter) { + + if (ac != null) + ac.setItemCasting(false); + if (ac == null || target == null || pb == null) + return; + + ac.clearTimer(Integer.toString(pb.getToken())); + if (liveCounter == ac.getLiveCounter()) + finishApplyPowerA(ac, target, targetLoc, pb, trains, false); + } + + public static void finishApplyPowerA(AbstractCharacter ac, + AbstractWorldObject target, Vector3fImmutable targetLoc, + PowersBase pb, int trains, boolean fromChant) { + // finally Apply actions + ArrayList actions = pb.getActions(); + for (ActionsBase ab : actions) { + // get numTrains for power, skip action if invalid + if (trains < ab.getMinTrains() || trains > ab.getMaxTrains()) + continue; + // If something blocks the action, then stop + if (ab.blocked(target, pb, trains)) + // sendPowerMsg(pc, 5, msg); + continue; + // TODO handle overwrite stack order here + String stackType = ab.getStackType(); + stackType = (stackType.equals("IgnoreStack")) ? Integer.toString(ab.getUUID()) : stackType; + if (target.getEffects().containsKey(stackType)) { + // remove any existing power that overrides + Effect ef = target.getEffects().get(stackType); + AbstractEffectJob effect = null; + if (ef != null) { + JobContainer jc = ef.getJobContainer(); + if (jc != null) + effect = (AbstractEffectJob) jc.getJob(); + } + ActionsBase overwrite = effect.getAction(); + PowersBase pbOverwrite = effect.getPower(); + if (pbOverwrite != null && pbOverwrite.equals(pb) + && (trains >= effect.getTrains())) + removeEffect(target, overwrite, true, fromChant); + else if (ab.getStackOrder() < overwrite.getStackOrder()) + continue; // not high enough to overwrite + else if (ab.getStackOrder() > overwrite.getStackOrder()) + removeEffect(target, overwrite, true, false); + else if (ab.getStackOrder() == overwrite.getStackOrder()) + if (ab.greaterThanEqual() + && (trains >= effect.getTrains())) + removeEffect(target, overwrite, true, false); + else if (ab.always()) + removeEffect(target, overwrite, true, false); + else if (ab.greaterThan() + && (trains > effect.getTrains())) + removeEffect(target, overwrite, true, false); + else if (ab.greaterThan() && pb.getToken() == effect.getPowerToken()) + removeEffect(target, overwrite, true, false); + else + continue; // trains not high enough to overwrite + } + if (fromChant) + targetLoc = Vector3fImmutable.ZERO; + runPowerAction(ac, target, targetLoc, ab, trains, pb); + } + + //Handle chant + if (pb != null && pb.isChant()) + for (ActionsBase ab : pb.getActions()) { + AbstractPowerAction pa = ab.getPowerAction(); + if (pa != null) + pa.handleChant(ac, target, targetLoc, trains, ab, pb); + } + + // for chants, only send the animation if the character is not is not moving or casting + boolean doAnimation = true; + + if (target.getObjectType().equals(GameObjectType.PlayerCharacter)) { + PlayerCharacter pc = (PlayerCharacter) target; + if (pb != null && pb.isChant() && (pc.isMoving() || pc.isCasting())) + doAnimation = false; + } + + if (pb.getToken() == 428950414) + doAnimation = true; + + if (doAnimation) { + PerformActionMsg msg = new PerformActionMsg(pb.getToken(), 9999, ac + .getObjectType().ordinal(), ac.getObjectUUID(), target.getObjectType().ordinal(), + target.getObjectUUID(), 0, 0, 0, 2, 0); + + DispatchMessage.sendToAllInRange(ac, msg); + + } + } + + public static void runPowerAction(AbstractCharacter source, + AbstractWorldObject awo, Vector3fImmutable targetLoc, + ActionsBase ab, int trains, PowersBase pb) { + AbstractPowerAction pa = ab.getPowerAction(); + if (pa == null) { + Logger.error( + "runPowerAction(): PowerAction not found of IDString: " + + ab.getEffectID()); + return; + } + pa.startAction(source, awo, targetLoc, trains, ab, pb); + } + + public static void runPowerAction(AbstractCharacter source, + AbstractWorldObject awo, Vector3fImmutable targetLoc, + ActionsBase ab, int trains, PowersBase pb, int duration) { + AbstractPowerAction pa = ab.getPowerAction(); + if (pa == null) { + Logger.error( + "runPowerAction(): PowerAction not found of IDString: " + + ab.getEffectID()); + return; + } + pa.startAction(source, awo, targetLoc, trains, ab, pb, duration); + } + + public static HashSet getAllTargets( + AbstractWorldObject target, Vector3fImmutable tl, + PlayerCharacter pc, PowersBase pb) { + HashSet allTargets; + if (pb.isAOE()) { + Vector3fImmutable targetLoc = null; + if (pb.usePointBlank()) { + targetLoc = pc.getLoc(); + } else { + if (target != null) { + targetLoc = target.getLoc(); + } else { + targetLoc = tl; + try{ + targetLoc = targetLoc.setY(HeightMap.getWorldHeight(targetLoc)); //on ground + }catch (Exception e){ + Logger.error(e); + targetLoc = tl; + } + + } + } + + if (targetLoc.x == 0f || targetLoc.z == 0f) + return new HashSet<>(); // invalid loc, + // return + // nothing + + //first find targets in range quickly with QTree + if (pb.targetPlayer() && pb.targetMob()) + // Player and mobs + allTargets = WorldGrid.getObjectsInRangePartial( + targetLoc, pb.getRadius(), MBServerStatics.MASK_MOBILE); + else if (pb.targetPlayer()) + // Player only + allTargets = WorldGrid.getObjectsInRangePartial( + targetLoc, pb.getRadius(), MBServerStatics.MASK_PLAYER); + else if (pb.targetMob()) + // Mob only + allTargets = WorldGrid.getObjectsInRangePartial( + targetLoc, pb.getRadius(), MBServerStatics.MASK_MOB + | MBServerStatics.MASK_PET); + else if (pb.targetPet()) + //Pet only + allTargets = WorldGrid.getObjectsInRangePartial( + targetLoc, pb.getRadius(), MBServerStatics.MASK_PET); + else if (pb.targetNecroPet()) + allTargets = WorldGrid.getObjectsInRangePartialNecroPets( + targetLoc, pb.getRadius()); + else + allTargets = WorldGrid.getObjectsInRangePartial( + targetLoc, pb.getRadius(), 0); + + // cleanup self, group and nation targets if needed + Iterator awolist = allTargets.iterator(); + while (awolist.hasNext()) { + AbstractWorldObject awo = awolist.next(); + if (awo == null) { + awolist.remove(); // won't hit a null + continue; + } + + //see if targets are within 3D range of each other + Vector3fImmutable tloc = awo.getLoc(); + + if (tloc.distanceSquared(targetLoc) > sqr(pb.getRadius())) { + awolist.remove(); // too far away + continue; + } + + if (pb.isCasterFriendly() && pc.equals(awo)) { + awolist.remove(); // won't hit self + continue; + } + + if (!awo.isAlive()) { + awolist.remove(); // too far away + continue; + } + + if (awo.getObjectType().equals(GameObjectType.PlayerCharacter)) { + + PlayerCharacter pcc = (PlayerCharacter) awo; + + if (pb.isGroupFriendly() && GroupManager.getGroup(pc) != null && GroupManager.getGroup(pcc) != null) + if (GroupManager.getGroup(pc).equals(GroupManager.getGroup(pcc))) { + awolist.remove(); // Won't hit group members + continue; + } + if (pb.isNationFriendly() && pc.getGuild() != null && + pc.getGuild().getNation() != null && pcc.getGuild() != null && + pc.getGuild().getNation() != null) + if (pc.getGuild().getNation().equals(pcc.getGuild().getNation())) { + awolist.remove(); // Won't hit nation members + continue; + } + + // Remove players for non-friendly spells in safe zone + if (pb.isHarmful() && (pcc.inSafeZone() || pc.inSafeZone())) { + awolist.remove(); + continue; + } + } + } + // Trim list down to max size closest targets, limited by max + // Player/Mob amounts + allTargets = RangeBasedAwo.getSortedList(allTargets, targetLoc, pb + .getMaxNumPlayerTargets(), pb.getMaxNumMobTargets()); + } else if (pb.targetGroup()) { + + if (GroupManager.getGroup(pc) != null) { + allTargets = WorldGrid.getObjectsInRangePartial(pc + .getLoc(), pb.getRange(), MBServerStatics.MASK_PLAYER); + Iterator awolist = allTargets.iterator(); + while (awolist.hasNext()) { + + AbstractWorldObject awo = awolist.next(); + + if (!(awo.getObjectType().equals(GameObjectType.PlayerCharacter))) { + awolist.remove(); // remove non players if there are any + continue; + } + PlayerCharacter pcc = (PlayerCharacter) awo; + + if (GroupManager.getGroup(pcc) == null) + awolist.remove(); // remove players not in a group + else if (!GroupManager.getGroup(pcc).equals(GroupManager.getGroup(pc))) + awolist.remove(); // remove if not same group + + } + } else { + allTargets = new HashSet<>(); + allTargets.add(pc); // no group, use only self + } + } else { + allTargets = new HashSet<>(); + if (pb.targetSelf()) + allTargets.add(pc); + else if (pb.targetFromLastTarget()) + allTargets.add(target); + else if (pb.targetFromNearbyMobs()) + allTargets.add(target); // need better way to do this later + else + // targetByName + allTargets.add(target); // need to get name later + // can't target self if caster friendly + if (pb.isCasterFriendly() && allTargets.contains(pc)) + allTargets.remove(0); + } + + Iterator awolist = allTargets.iterator(); + while (awolist.hasNext()) { + AbstractWorldObject awo = awolist.next(); + + //See if target is valid type + if (!validMonsterType(awo, pb)) { + awolist.remove(); + continue; + } + + if (awo != null && awo.getObjectType().equals(GameObjectType.PlayerCharacter)) { + + // Remove players who are in safe mode + PlayerCharacter pcc = (PlayerCharacter) awo; + PlayerBonuses bonuses = pcc.getBonuses(); + + if (bonuses != null && bonuses.getBool(ModType.ImmuneToPowers, SourceType.None)) { + awolist.remove(); + continue; + } + + //remove if power is harmful and caster or target is in safe zone + if (pb.isHarmful() && (pcc.inSafeZone() || pc.inSafeZone())) { + awolist.remove(); + continue; + } + } + } + + // verify target has proper effects applied to receive power + if (pb.getTargetEffectPrereqs().size() > 0) { + Iterator it = allTargets.iterator(); + while (it.hasNext()) { + boolean passed = false; + AbstractWorldObject awo = it.next(); + if (awo.getEffects() != null) { + for (PowerPrereq pp : pb.getTargetEffectPrereqs()) { + EffectsBase eb = PowersManager.getEffectByIDString(pp.getEffect()); + if (awo.containsEffect(eb.getToken())) { + passed = true; + break; + } + } + if (!passed) + it.remove(); + } else + it.remove(); //awo is missing it's effects list + } + } + return allTargets; + } + + public static HashSet getAllTargets( + AbstractWorldObject target, Vector3fImmutable tl, + AbstractCharacter caster, PowersBase pb) { + HashSet allTargets; + if (pb.isAOE()) { + Vector3fImmutable targetLoc = tl; + if (pb.usePointBlank()) { + targetLoc = caster.getLoc(); + } else { + if (target != null) { + targetLoc = target.getLoc(); + } else { + targetLoc = tl; + try{ + targetLoc = targetLoc.setY(HeightMap.getWorldHeight(targetLoc)); //on ground + }catch (Exception e){ + Logger.error(e); + } + + } + } + + if (targetLoc.x == 0f || targetLoc.z == 0f) + return new HashSet<>(); // invalid loc, + + //first find targets in range quickly with QTree + if (pb.targetPlayer() && pb.targetMob()) + // Player and mobs + allTargets = WorldGrid.getObjectsInRangePartial( + targetLoc, pb.getRadius(), MBServerStatics.MASK_MOBILE); + else if (pb.targetPlayer()) + // Player only + allTargets = WorldGrid.getObjectsInRangePartial( + targetLoc, pb.getRadius(), MBServerStatics.MASK_PLAYER); + else if (pb.targetMob()) + // Mob only + allTargets = WorldGrid.getObjectsInRangePartial( + targetLoc, pb.getRadius(), MBServerStatics.MASK_MOB + | MBServerStatics.MASK_PET); + else if (pb.targetPet()) + //Pet only + allTargets = WorldGrid.getObjectsInRangePartial( + targetLoc, pb.getRadius(), MBServerStatics.MASK_PET); + else if (pb.targetNecroPet()) + allTargets = WorldGrid.getObjectsInRangePartialNecroPets( + targetLoc, pb.getRadius()); + else + allTargets = WorldGrid.getObjectsInRangePartial( + targetLoc, pb.getRadius(), 0); + + // cleanup self, group and nation targets if needed + Iterator awolist = allTargets.iterator(); + while (awolist.hasNext()) { + AbstractWorldObject awo = awolist.next(); + if (awo == null) { + awolist.remove(); // won't hit a null + continue; + } + + //see if targets are within 3D range of each other + Vector3fImmutable tloc = awo.getLoc(); + + if (tloc.distanceSquared(targetLoc) > sqr(pb.getRadius())) { + awolist.remove(); // too far away + continue; + } + + if (pb.isCasterFriendly() && caster.equals(awo)) { + awolist.remove(); // won't hit self + continue; + } + + if (awo.getObjectType() == GameObjectType.Mob) { + awolist.remove(); // Won't hit other mobs. + continue; + } + } + // Trim list down to max size closest targets, limited by max + // Player/Mob amounts + allTargets = RangeBasedAwo.getSortedList(allTargets, targetLoc, pb + .getMaxNumPlayerTargets(), pb.getMaxNumMobTargets()); + } else if (pb.targetGroup()) { + allTargets = new HashSet<>(); + allTargets.add(caster); // no group, use only self + } else { + allTargets = new HashSet<>(); + if (pb.targetSelf()) + allTargets.add(caster); + else if (pb.targetFromLastTarget()) + allTargets.add(target); + else if (pb.targetFromNearbyMobs()) + allTargets.add(target); // need better way to do this later + else + // targetByName + allTargets.add(target); // need to get name later + // can't target self if caster friendly + if (pb.isCasterFriendly() && allTargets.contains(caster)) + allTargets.remove(caster); + } + + Iterator awolist = allTargets.iterator(); + while (awolist.hasNext()) { + AbstractWorldObject awo = awolist.next(); + + //See if target is valid type + if (!validMonsterType(awo, pb)) { + awolist.remove(); + continue; + } + + if (awo != null && awo.getObjectType().equals(GameObjectType.PlayerCharacter)) { + // Remove players who are in safe mode + PlayerCharacter pcc = (PlayerCharacter) awo; + PlayerBonuses bonuses = pcc.getBonuses(); + if (bonuses != null && bonuses.getBool(ModType.ImmuneToPowers, SourceType.None)) { + awolist.remove(); + continue; + } + } + } + + // verify target has proper effects applied to receive power + if (pb.getTargetEffectPrereqs().size() > 0) { + Iterator it = allTargets.iterator(); + while (it.hasNext()) { + boolean passed = false; + AbstractWorldObject awo = it.next(); + if (awo.getEffects() != null) { + for (PowerPrereq pp : pb.getTargetEffectPrereqs()) { + EffectsBase eb = PowersManager.getEffectByIDString(pp.getEffect()); + if (awo.containsEffect(eb.getToken())) { + passed = true; + break; + } + } + if (!passed) + it.remove(); + } else + it.remove(); //awo is missing it's effects list + } + } + return allTargets; + } + + // removes an effect before time is finished + public static void removeEffect(AbstractWorldObject awo, ActionsBase toRemove, + boolean overwrite, boolean fromChant) { + if (toRemove == null) + return; + + String stackType = toRemove.getStackType(); + stackType = (stackType.equals("IgnoreStack")) ? Integer + .toString(toRemove.getUUID()) : stackType; + if (fromChant) { + Effect eff = awo.getEffects().get(stackType); + if (eff != null) + eff.cancelJob(true); + } else + awo.cancelEffect(stackType, overwrite); + } + + // removes an effect when timer finishes + public static void finishEffectTime(AbstractWorldObject source, + AbstractWorldObject awo, ActionsBase toRemove, int trains) { + if (awo == null || toRemove == null) + return; + + // remove effect from player + String stackType = toRemove.getStackType(); + if (stackType.equals("IgnoreStack")) + stackType = Integer.toString(toRemove.getUUID()); + awo.endEffect(stackType); + } + + // removes an effect when timer is canceled + public static void cancelEffectTime(AbstractWorldObject source, + AbstractWorldObject awo, PowersBase pb, EffectsBase eb, + ActionsBase toRemove, int trains, AbstractEffectJob efj) { + if (awo == null || pb == null || eb == null || toRemove == null) + return; + eb.endEffect(source, awo, trains, pb, efj); + } + + // called when cooldown ends letting player cast next spell + public static void finishCooldownTime(PerformActionMsg msg, PlayerCharacter pc) { + // clear spell so player can cast again + // if (pc != null) + // pc.clearLastPower(); + } + + // called when recycle time ends letting player cast spell again + public static void finishRecycleTime(PerformActionMsg msg, PlayerCharacter pc, + boolean canceled) { + finishRecycleTime(msg.getPowerUsedID(), pc, canceled); + } + + public static void finishRecycleTime(int token, PlayerCharacter pc, + boolean canceled) { + if (pc == null) + return; + + ConcurrentHashMap recycleTimers = pc + .getRecycleTimers(); + // clear recycle time + if (recycleTimers != null) + if (recycleTimers.containsKey(token)) { + if (canceled) { + JobContainer jc = recycleTimers.get(token); + if (jc != null) + jc.cancelJob(); + } + recycleTimers.remove(token); + } + + // send recycle message to unlock power + + RecyclePowerMsg recyclePowerMsg = new RecyclePowerMsg(token); + Dispatch dispatch = Dispatch.borrow(pc, recyclePowerMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + + } + + // Called when a fail condition is met by player + // such as moving, taking damage, ect. + + public static void cancelUseLastPower(PlayerCharacter pc) { + + if (pc == null) + return; + + // set player is not casting for regens + if (pc.isCasting()){ + pc.update(); + } + pc.setIsCasting(false); + + UsePowerJob lastPower = null; + JobContainer jc = pc.getLastPower(); + + if (jc != null) + lastPower = ((UsePowerJob) jc.getJob()); + + if (lastPower == null) + return; + + // clear recycle timer + int token = lastPower.getToken(); + + if (pc.getRecycleTimers().contains(token)) + finishRecycleTime(token, pc, true); + + // pc.getRecycleTimers().remove(token); + // Cancel power + js.cancelScheduledJob(lastPower); + + // clear last power + pc.clearLastPower(); + + } + + private static AbstractWorldObject getTarget(PerformActionMsg msg) { + + int type = msg.getTargetType(); + int UUID = msg.getTargetID(); + + if (type == -1 || type == 0 || UUID == -1 || UUID == 0) + return null; + + return (AbstractWorldObject) DbManager.getObject(GameObjectType.values()[type], UUID); + } + + public static boolean testAttack(PlayerCharacter pc, AbstractWorldObject awo, + PowersBase pb, PerformActionMsg msg) { + // Get defense for target + float atr = CharacterSkill.getATR(pc, pb.getSkillName()); + float defense; + + if (AbstractWorldObject.IsAbstractCharacter(awo)) { + AbstractCharacter tar = (AbstractCharacter) awo; + defense = tar.getDefenseRating(); + } else + defense = 0f; + // Get hit chance + + if (pc.getDebug(16)) { + String smsg = "ATR: " + atr + ", Defense: " + defense; + ChatManager.chatSystemInfo(pc, smsg); + } + + int chance; + + if (atr > defense || defense == 0) + chance = 94; + else { + float dif = atr / defense; + if (dif <= 0.8f) + chance = 4; + else + chance = ((int) (450 * (dif - 0.8f)) + 4); + } + + // calculate hit/miss + int roll = ThreadLocalRandom.current().nextInt(100); + + boolean disable = true; + if (roll < chance) { + // Hit, check if dodge kicked in + if (awo instanceof AbstractCharacter) { + AbstractCharacter tarAc = (AbstractCharacter) awo; + // Handle Dodge passive + if (testPassive(pc, tarAc, "Dodge")) { + // Dodge fired, send dodge message + PerformActionMsg dodgeMsg = new PerformActionMsg(msg); + dodgeMsg.setTargetType(awo.getObjectType().ordinal()); + dodgeMsg.setTargetID(awo.getObjectUUID()); + sendPowerMsg(pc, 4, dodgeMsg); + return true; + } + } + return false; + } else { + // Miss. Send miss message + PerformActionMsg missMsg = new PerformActionMsg(msg); + + missMsg.setTargetType(awo.getObjectType().ordinal()); + missMsg.setTargetID(awo.getObjectUUID()); + sendPowerMsg(pc, 3, missMsg); + return true; + } + } + + public static boolean testAttack(Mob caster, AbstractWorldObject awo, + PowersBase pb, PerformActionMsg msg) { + // Get defense for target + float atr = 2000; + float defense; + + if (AbstractWorldObject.IsAbstractCharacter(awo)) { + AbstractCharacter tar = (AbstractCharacter) awo; + defense = tar.getDefenseRating(); + } else + defense = 0f; + // Get hit chance + + int chance; + + if (atr > defense || defense == 0) + chance = 94; + else { + float dif = atr / defense; + if (dif <= 0.8f) + chance = 4; + else + chance = ((int) (450 * (dif - 0.8f)) + 4); + } + + // calculate hit/miss + int roll = ThreadLocalRandom.current().nextInt(100); + + if (roll < chance) { + // Hit, check if dodge kicked in + if (AbstractWorldObject.IsAbstractCharacter(awo)) { + AbstractCharacter tarAc = (AbstractCharacter) awo; + // Handle Dodge passive + return testPassive(caster, tarAc, "Dodge"); + } + return false; + } else + return true; + } + + public static void sendPowerMsg(PlayerCharacter playerCharacter, int type, PerformActionMsg msg) { + + if (playerCharacter == null) + return; + + msg.setUnknown05(type); + + switch (type) { + case 3: + case 4: + msg.setUnknown04(2); + DispatchMessage.dispatchMsgToInterestArea(playerCharacter, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + break; + default: + msg.setUnknown04(1); + Dispatch dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + } + } + + public static void sendEffectMsg(PlayerCharacter pc, int type, ActionsBase ab, PowersBase pb) { + + if (pc == null) + return; + + try { + + EffectsBase eb = PowersManager.effectsBaseByIDString.get(ab.getEffectID()); + + if (eb == null) + return; + + ApplyEffectMsg aem = new ApplyEffectMsg(pc, pc, 0, eb.getToken(), 9, pb.getToken(), pb.getName()); + aem.setUnknown03(type); + DispatchMessage.dispatchMsgToInterestArea(pc, aem, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + + + } catch (Exception e) { + Logger.error( e.getMessage()); + } + + } + + public static void sendEffectMsg(PlayerCharacter pc, int type, EffectsBase eb) { + + if (pc == null) + return; + try { + + if (eb == null) + return; + ApplyEffectMsg aem = new ApplyEffectMsg(pc, pc, 0, eb.getToken(), 0, eb.getToken(), ""); + aem.setUnknown03(type); + aem.setUnknown05(1); + + DispatchMessage.dispatchMsgToInterestArea(pc, aem, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + + + } catch (Exception e) { + Logger.error( e.getMessage()); + } + + } + + public static void sendMobPowerMsg(Mob mob, int type, PerformActionMsg msg) { + + msg.setUnknown05(type); + switch (type) { + case 3: + case 4: + DispatchMessage.sendToAllInRange(mob, msg); + + } + } + + private static boolean testPassive(AbstractCharacter source, + AbstractCharacter target, String type) { + + float chance = target.getPassiveChance(type, source.getLevel(), false); + + if (chance == 0f) + return false; + + // max 75% chance of passive to fire + if (chance > 75f) + chance = 75f; + + int roll = ThreadLocalRandom.current().nextInt(100); + // Passive fired + // TODO send message + // Passive did not fire + return roll < chance; + } + + private static boolean validateTarget(AbstractWorldObject target, + PlayerCharacter pc, PowersBase pb) { + + //group target. uses pbaoe rules + if (pb.targetGroup()) + return true; + + // target is player + else if ((target.getObjectTypeMask() & MBServerStatics.MASK_PLAYER) != 0) { + if (pb.targetPlayer()) + if (pb.isGroupOnly()) { //single target group only power + PlayerCharacter trg = (PlayerCharacter) target; + + if (GroupManager.getGroup(trg) != null && GroupManager.getGroup(pc) != null) + if (GroupManager.getGroup(trg).getObjectUUID() == GroupManager.getGroup(pc).getObjectUUID()) + return true; // both in same group, good to go + return trg != null && pc.getObjectUUID() == trg.getObjectUUID(); + } else + return true; // can target player, good to go. + else if (target.getObjectUUID() == pc.getObjectUUID() && pb.targetSelf()) + return true; // can target self, good to go + else if (pb.targetCorpse()) { + //target is dead player + PlayerCharacter trg = (PlayerCharacter) target; + return !trg.isAlive(); + } else { + PlayerCharacter trg = (PlayerCharacter) target; + + if (pb.targetGroup()) + if (GroupManager.getGroup(trg) != null && GroupManager.getGroup(pc) != null) + if (GroupManager.getGroup(trg).getObjectUUID() == GroupManager.getGroup(pc) + .getObjectUUID()) + return true; // both in same group, good to go + if (pb.targetGuildLeader()) + if (pc.getGuild() != null) + if (pc.getGuild().getGuildLeaderUUID() == trg.getObjectUUID()) + return true; // can hit guild leader, good to go + } + String outmsg = "Invalid Target"; + ChatManager.chatSystemInfo(pc, outmsg); + return false; // can't target player, stop here + } // target is mob + else if ((target.getObjectTypeMask() & MBServerStatics.MASK_MOB) != 0) + return pb.targetMob(); + + // target is pet + else if ((target.getObjectTypeMask() & MBServerStatics.MASK_PET) != 0) + return pb.targetPet(); + + // target is Building + else if ((target.getObjectTypeMask() & MBServerStatics.MASK_BUILDING) != 0) + return pb.targetBuilding(); + + else if (target.getObjectType().equals(GameObjectType.Item)) { + Item item = (Item) target; + if (pb.targetItem()) + return true; + // TODO add these checks later + else if (pb.targetArmor() && item.getItemBase().getType().equals(ItemType.ARMOR)) + return true; + else if (pb.targetJewelry() && item.getItemBase().getType().equals(ItemType.JEWELRY)) + return true; + else return pb.targetWeapon() && item.getItemBase().getType().equals(ItemType.WEAPON); + } // How did we get here? all valid targets have been covered + else + return false; + } + + /* + * Cancel spell upon actions + */ + public static void cancelOnAttack(AbstractCharacter ac) { + ac.cancelTimer("Stuck"); + } + + public static void cancelOnAttackSwing(AbstractCharacter ac) { + } + + public static void cancelOnCast(AbstractCharacter ac) { + + } + + public static void cancelOnSpell(AbstractCharacter ac) { + + PowersBase power = getLastPower(ac); + + if (power != null && power.cancelOnCastSpell()) + cancelPower(ac, false); + ac.cancelLastChant(); + } + + public static void cancelOnEquipChange(AbstractCharacter ac) { + + } + + public static void cancelOnLogout(AbstractCharacter ac) { + + } + + public static void cancelOnMove(AbstractCharacter ac) { + + PowersBase power = getLastPower(ac); + + if (power != null && !power.canCastWhileMoving()) + cancelPower(ac, false); + + //cancel items + cancelItems(ac, true, false); + ac.cancelTimer("Stuck"); + } + + + + public static void cancelOnNewCharm(AbstractCharacter ac) { + + } + + public static void cancelOnSit(AbstractCharacter ac) { + cancelPower(ac, false); // Always cancel casts on sit + } + + public static void cancelOnTakeDamage(AbstractCharacter ac) { + + PowersBase power = getLastPower(ac); + + if (power != null && power.cancelOnTakeDamage()) + cancelPower(ac, true); + cancelItems(ac, false, true); + ac.cancelTimer("Stuck"); + } + + public static void cancelOnTerritoryClaim(AbstractCharacter ac) { + + } + + public static void cancelOnUnEquip(AbstractCharacter ac) { + + } + + public static void cancelOnStun(AbstractCharacter ac) { + + } + + private static PowersBase getLastPower(AbstractCharacter ac) { + if (ac == null) + return null; + + JobContainer jc = ac.getLastPower(); + + if (jc == null) + return null; + + AbstractJob aj = jc.getJob(); + + if (aj == null) + return null; + + if (aj instanceof UsePowerJob) { + UsePowerJob upj = (UsePowerJob) aj; + return upj.getPowersBase(); + } + return null; + } + + private static PowersBase getLastItem(AbstractCharacter ac) { + + if (ac == null) + return null; + + JobContainer jc = ac.getLastItem(); + + if (jc == null) + return null; + + AbstractJob aj = jc.getJob(); + + if (aj == null) + return null; + + if (aj instanceof UseItemJob) { + UseItemJob uij = (UseItemJob) aj; + return uij.getPowersBase(); + } + return null; + } + + //cancels last casted power + private static void cancelPower(AbstractCharacter ac, boolean cancelCastAnimation) { + + if (ac == null) + return; + + JobContainer jc = ac.getLastPower(); + + if (jc == null) + return; + + AbstractJob aj = jc.getJob(); + + if (aj == null) + return; + + if (aj instanceof AbstractScheduleJob) + ((AbstractScheduleJob) aj).cancelJob(); + + ac.clearLastPower(); + + //clear cast animation for everyone in view range + if (aj instanceof UsePowerJob && cancelCastAnimation) { + + PerformActionMsg pam = ((UsePowerJob) aj).getMsg(); + + if (pam != null) { + pam.setNumTrains(9999); + pam.setUnknown04(2); + DispatchMessage.sendToAllInRange(ac, pam); + } + } + } + + public static PerformActionMsg createPowerMsg(PowersBase pb, int trains, AbstractCharacter source, AbstractCharacter target) { + return new PerformActionMsg(pb.getToken(), trains, source.getObjectType().ordinal(), source.getObjectUUID(), target.getObjectType().ordinal(), target.getObjectUUID(), target.getLoc().x, target.getLoc().y, target.getLoc().z, 0, 0); + + } + + //cancels any casts from using an item + + private static void cancelItems(AbstractCharacter ac, boolean cancelOnMove, boolean cancelOnTakeDamage) { + JobContainer jc; + AbstractJob aj; + ConcurrentHashMap timers; + UseItemJob uij; + PowersBase pb; + AbstractWorldObject target; + + if (ac == null) + return; + + timers = ac.getTimers(); + + if (timers == null) + return; + + for (String name : timers.keySet()) { + + jc = timers.get(name); + + if (jc == null) + continue; + + aj = jc.getJob(); + + if (aj != null && aj instanceof UseItemJob) { + uij = (UseItemJob) aj; + pb = uij.getPowersBase(); + + if (pb == null) + continue; + + if (!pb.canCastWhileMoving() && cancelOnMove) { + uij.cancelJob(); + timers.remove(name); + continue; + } + + if ((pb.cancelOnTakeDamage() == false) && + (cancelOnTakeDamage == false)) + continue; + + uij.cancelJob(); + timers.remove(name); + + //clear cast animation for everyone in view range + target = uij.getTarget(); + + if (target != null) { + PerformActionMsg pam = new PerformActionMsg(pb.getToken(), 9999, ac + .getObjectType().ordinal(), ac.getObjectUUID(), target.getObjectType().ordinal(), + target.getObjectUUID(), 0, 0, 0, 2, 0); + DispatchMessage.sendToAllInRange(ac, pam); + + } + } + } + } +} + + + diff --git a/src/engine/gameManager/SessionManager.java b/src/engine/gameManager/SessionManager.java new file mode 100644 index 00000000..10682986 --- /dev/null +++ b/src/engine/gameManager/SessionManager.java @@ -0,0 +1,289 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.gameManager; + +import engine.net.client.ClientConnection; +import engine.objects.Account; +import engine.objects.Guild; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; +import engine.session.CSSession; +import engine.session.Session; +import engine.session.SessionID; +import engine.util.ByteUtils; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.ConcurrentHashMap; + +public enum SessionManager { + + SESSIONMANAGER; + + // TODO add session activity timestamping & timeout monitors + + private static ConcurrentHashMap sessionIDtoSession = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_HIGH); + private static ConcurrentHashMap pcToSession = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_HIGH); + private static ConcurrentHashMap accountToSession = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_HIGH); + private static ConcurrentHashMap connToSession = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_HIGH); + public static int _maxPopulation = 0; + + // 0 = login server + // 1 = gateway server + // 2 = all other servers + private static int crossServerBehavior = 2; + + public static Session getNewSession(SessionID sesID, Account a, ClientConnection c) { + Session ses = new Session(sesID, a, c); + + SessionManager.sessionIDtoSession.put(sesID, ses); + SessionManager.accountToSession.put(a, ses); + SessionManager.connToSession.put(c, ses); + + if (crossServerBehavior == 0) + if (!CSSession.addCrossServerSession(ByteUtils.byteArrayToSafeStringHex(c.getSecretKeyBytes()), a, c.getSocketChannel() + .socket().getInetAddress(), c.machineID)) + Logger.warn("Failed to create cross server session: " + a.getUname()); + + return ses; + } + + public static Session getNewSession(Account a, ClientConnection c) { + SessionID sesID = c.getSessionID(); + return SessionManager.getNewSession(sesID, a, c); + } + + + public static void cSessionCleanup(String key) { + if (!CSSession.deleteCrossServerSession(key)) + Logger.warn( + "Failed to remove cross server session for key: " + key); + } + + public static void remSession(Session s) { + + if (s == null) { + return; + } + + SessionManager.remSessionID(s); + SessionManager.remAccount(s); + SessionManager.remClientConnection(s); + SessionManager.remPlayerCharacter(s); + + //TODO LATER fix + s.setAccount(null); + s.setConn(null); + s.setPlayerCharacter(null); + s.setSessionID(null); + } + + /* + * Get Sessions + */ + public static Session getSession(SessionID id) { + return SessionManager.sessionIDtoSession.get(id); + } + + public static Session getSession(PlayerCharacter pc) { + return SessionManager.pcToSession.get(pc); + } + + public static Session getSession(Account a) { + return SessionManager.accountToSession.get(a); + } + + public static Session getSession(ClientConnection cc) { + return SessionManager.connToSession.get(cc); + } + + /* + * Get Connections + */ + public static ClientConnection getClientConnection(SessionID id) { + Session s = SessionManager.getSession(id); + return (s == null) ? null : s.getConn(); + } + + public static ClientConnection getClientConnection(PlayerCharacter pc) { + Session s = SessionManager.getSession(pc); + return (s == null) ? null : s.getConn(); + } + + public static ClientConnection getClientConnection(Account a) { + Session s = SessionManager.getSession(a); + return (s == null) ? null : s.getConn(); + } + + /* + * Get PlayerCharacter + */ + public static PlayerCharacter getPlayerCharacter(SessionID id) { + Session s = SessionManager.getSession(id); + return (s == null) ? null : s.getPlayerCharacter(); + } + + public static PlayerCharacter getPlayerCharacter(ClientConnection conn) { + Session s = SessionManager.getSession(conn); + return (s == null) ? null : s.getPlayerCharacter(); + } + + public static PlayerCharacter getPlayerCharacter(Account a) { + Session s = SessionManager.getSession(a); + return (s == null) ? null : s.getPlayerCharacter(); + } + + /* + * Get Account + */ + public static Account getAccount(SessionID id) { + Session s = SessionManager.getSession(id); + return (s == null) ? null : s.getAccount(); + } + + public static Account getAccount(ClientConnection conn) { + Session s = SessionManager.getSession(conn); + return (s == null) ? null : s.getAccount(); + } + + public static Account getAccount(PlayerCharacter pc) { + Session s = SessionManager.getSession(pc); + return (s == null) ? null : s.getAccount(); + } + + public static void setPlayerCharacter(Session s, PlayerCharacter pc) { + SessionManager.pcToSession.put(pc, s); + s.setPlayerCharacter(pc); + + // Update max player + SessionManager._maxPopulation = Math.max(_maxPopulation, SessionManager.pcToSession.size()); + + } + + public static void remPlayerCharacter(Session s) { + if (s.getPlayerCharacter() != null) { + SessionManager.pcToSession.remove(s.getPlayerCharacter()); + s.setPlayerCharacter(null); + } + } + + protected static void remAccount(Session s) { + if (s.getAccount() != null) { + SessionManager.accountToSession.remove(s.getAccount()); + s.setAccount(null); + } + } + + protected static void remSessionID(Session s) { + + if (s.getSessionID() != null) { + SessionManager.sessionIDtoSession.remove(s.getSessionID()); + s.setSessionID(null); + } + } + + protected static void remClientConnection(Session s) { + if (s.getConn() != null) { + SessionManager.connToSession.remove(s.getConn()); + s.setConn(null); + } + } + + + + /* + * Utils + */ + + public static void setCrossServerBehavior(int type) { + crossServerBehavior = type; + } + + /** + * + * @return a new HashSet object so the caller cannot + * modify the internal Set + */ + public static Collection getAllActiveClientConnections() { + return SessionManager.connToSession.keySet(); + } + + /** + * + * @return a new HashSet object so the caller cannot modify + * the internal Set + */ + public static Collection getAllActivePlayerCharacters() { + + return SessionManager.pcToSession.keySet(); + } + + public static Collection getAllActivePlayers() { + + return SessionManager.pcToSession.keySet(); + } + + public static int getActivePlayerCharacterCount() { + + return SessionManager.pcToSession.keySet().size(); + } + + public static ArrayList getActivePCsInGuildID(int id) { + ArrayList pcs = new ArrayList<>(); + + for (PlayerCharacter pc : SessionManager.getAllActivePlayerCharacters()) { + Guild g = pc.getGuild(); + if (g != null && g.getObjectUUID() == id) { + pcs.add(pc); + } + } + + return pcs; + } + + public static PlayerCharacter getPlayerCharacterByLowerCaseName(String name) { + + String queryName = name.toLowerCase(); + + for (PlayerCharacter playerCharacter : SessionManager.getAllActivePlayerCharacters()) { + + if ((playerCharacter.getFirstName().toLowerCase()).equals(queryName)) { + return playerCharacter; + } + } + return null; + } + + public static PlayerCharacter getPlayerCharacterByID(int UUID) { + + for (PlayerCharacter playerCharacter : SessionManager.getAllActivePlayerCharacters()) { + + if (playerCharacter.getObjectUUID() == UUID) { + return playerCharacter; + } + } + return null; + } + + public static Collection getAllActiveAccounts() { + return SessionManager.accountToSession.keySet(); + } + + public static Account getAccountByID(int UUID) { + + for (Account acc : SessionManager.getAllActiveAccounts()) { + + if (acc.getObjectUUID() == UUID) + return acc; + + } + return null; + } +} diff --git a/src/engine/gameManager/SimulationManager.java b/src/engine/gameManager/SimulationManager.java new file mode 100644 index 00000000..6905c099 --- /dev/null +++ b/src/engine/gameManager/SimulationManager.java @@ -0,0 +1,216 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.gameManager; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.objects.AbstractGameObject; +import engine.objects.City; +import engine.objects.PlayerCharacter; +import engine.objects.Runegate; +import org.pmw.tinylog.Logger; + +import java.util.Collection; + +/* + * This class contains all methods necessary to drive periodic + * updates of the game simulation from the main _exec loop. + */ +public enum SimulationManager { + + SERVERHEARTBEAT; + + private static SimulationManager instance = null; + + private static final long CITY_PULSE = 2000; + private static final long RUNEGATE_PULSE = 3000; + private static final long UPDATE_PULSE = 1000; + private static final long FlIGHT_PULSE = 100; + + private long _cityPulseTime = System.currentTimeMillis() + CITY_PULSE; + private long _runegatePulseTime = System.currentTimeMillis() + + RUNEGATE_PULSE; + private long _updatePulseTime = System.currentTimeMillis() + UPDATE_PULSE; + private long _flightPulseTime = System.currentTimeMillis() + FlIGHT_PULSE; + + public static long HeartbeatDelta = 0; + public static long currentHeartBeatDelta = 0; + + private SimulationManager() { + + // don't allow instantiation. + } + + public static String getPopulationString() { + String outString; + String newLine = System.getProperty("line.separator"); + outString = "[LUA_POPULATION()]" + newLine; + outString += DbManager.CSSessionQueries.GET_POPULATION_STRING(); + return outString; + } + + /* + * Update the simulation. *** Important: Whatever you do in here, do it damn + * quick! + */ + public void tick() { + + /* + * As we're on the main thread we must be sure to catch any possible + * errors. + * + * IF something does occur, disable that particular heartbeat. Better + * runegates stop working than the game itself! + */ + + long start = System.currentTimeMillis(); + + try { + if ((_flightPulseTime != 0) + && (System.currentTimeMillis() > _flightPulseTime)) + pulseFlight(); + } catch (Exception e) { + Logger.error( + "Fatal error in City Pulse: DISABLED. Error Message : " + + e.getMessage()); + } + try { + + if ((_updatePulseTime != 0) + && (System.currentTimeMillis() > _updatePulseTime)) + pulseUpdate(); + } catch (Exception e) { + Logger.error( + "Fatal error in Update Pulse: DISABLED"); + // _runegatePulseTime = 0; + } + + try { + if ((_runegatePulseTime != 0) + && (System.currentTimeMillis() > _runegatePulseTime)) + pulseRunegates(); + } catch (Exception e) { + Logger.error( + "Fatal error in Runegate Pulse: DISABLED"); + _runegatePulseTime = 0; + } + + try { + if ((_cityPulseTime != 0) + && (System.currentTimeMillis() > _cityPulseTime)) + pulseCities(); + } catch (Exception e) { + Logger.error( + "Fatal error in City Pulse: DISABLED. Error Message : " + + e.getMessage()); + e.printStackTrace(); + + } + + long end = System.currentTimeMillis(); + + long delta = end - start; + + if (delta > SimulationManager.HeartbeatDelta) + SimulationManager.HeartbeatDelta = delta; + + SimulationManager.currentHeartBeatDelta = delta; + + + + } + + /* + * Mainline simulation update method: handles movement and regen for all + * player characters + */ + + private void pulseUpdate() { + + Collection playerList; + + playerList = DbManager.getList(GameObjectType.PlayerCharacter); + + // Call update() on each player in game + + if (playerList == null) + return; + + for (AbstractGameObject ago : playerList) { + PlayerCharacter player = (PlayerCharacter)ago; + + if (player == null) + continue; + player.update(); + } + + _updatePulseTime = System.currentTimeMillis() + 500; + } + + private void pulseFlight() { + + Collection playerList; + + playerList = DbManager.getList(GameObjectType.PlayerCharacter); + + // Call update() on each player in game + + if (playerList == null) + return; + + for (AbstractGameObject ago : playerList) { + PlayerCharacter player = (PlayerCharacter)ago; + + if (player == null) + continue; + + + player.updateFlight(); + } + + _flightPulseTime = System.currentTimeMillis() + FlIGHT_PULSE; + } + + private void pulseCities() { + + City city; + + // *** Refactor: Need a list cached somewhere as it doesn't change very + // often at all. Have a cityListIsDirty boolean that gets set if it + // needs an update. Will speed up this method a great deal. + + Collection cityList = DbManager.getList(Enum.GameObjectType.City); + + if (cityList == null) { + Logger.info( "City List null"); + return; + } + + for (AbstractGameObject cityObject : cityList) { + city = (City) cityObject; + city.onEnter(); + } + + _cityPulseTime = System.currentTimeMillis() + CITY_PULSE; + } + + /* + * Method runs proximity collision detection for all active portals on the + * game's Runegates + */ + private void pulseRunegates() { + + for (Runegate runegate : Runegate.getRunegates()) { + runegate.collidePortals(); + } + + _runegatePulseTime = System.currentTimeMillis() + RUNEGATE_PULSE; + + } +} diff --git a/src/engine/gameManager/TradeManager.java b/src/engine/gameManager/TradeManager.java new file mode 100644 index 00000000..b202007d --- /dev/null +++ b/src/engine/gameManager/TradeManager.java @@ -0,0 +1,163 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.gameManager; + +import engine.Enum; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.*; +import engine.objects.CharacterItemManager; +import engine.objects.PlayerCharacter; +import org.pmw.tinylog.Logger; + +public enum TradeManager { + + TRADEMANAGER; + + public static void tradeRequest(TradeRequestMsg msg, ClientConnection origin) { + + PlayerCharacter source = origin.getPlayerCharacter(); + + if (source == null) + return; + + source.getCharItemManager().tradeRequest(msg); + + } + + + public static void acceptTradeRequest(AcceptTradeRequestMsg msg, ClientConnection origin) { + + PlayerCharacter source = origin.getPlayerCharacter(); + + if (source == null) + return; + + try { + source.getCharItemManager().acceptTradeRequest(msg); + } catch (Exception e) { + Logger.error(e); + // TODO Auto-generated catch block + } + + } + + public static void rejectTradeRequest(RejectTradeRequestMsg msg, ClientConnection origin) { + // TODO Do nothing? If so, delete this method & case above + } + + public static void addItemToTradeWindow(AddItemToTradeWindowMsg msg, ClientConnection origin) { + + + PlayerCharacter source = origin.getPlayerCharacter(); + if (source == null || !source.isAlive()) + return; + try{ + source.getCharItemManager().addItemToTradeWindow(msg); + + }catch(Exception e){ + Logger.error(e); + } + + } + + public static void addGoldToTradeWindow(AddGoldToTradeWindowMsg msg, ClientConnection origin) { + + PlayerCharacter source = origin.getPlayerCharacter(); + + if (source == null || !source.isAlive()) + return; + + + + CharacterItemManager sourceItemMan = source.getCharItemManager(); + + if (sourceItemMan == null) + return; + + try{ + sourceItemMan.addGoldToTradeWindow(msg); + }catch(Exception e){ + Logger.error(e); + } + } + + public static void commitToTrade(CommitToTradeMsg msg, ClientConnection origin) { + + PlayerCharacter source = origin.getPlayerCharacter(); + + if (source == null || !source.isAlive()) + return; + + CharacterItemManager sourceItemMan = source.getCharItemManager(); + + if (sourceItemMan == null) + return; + + try { + sourceItemMan.commitToTrade(msg); + } catch (Exception e) { + // TODO Auto-generated catch block + Logger.error(e); + } + } + + public static void uncommitToTrade(UncommitToTradeMsg msg, ClientConnection origin) { + + PlayerCharacter source = origin.getPlayerCharacter(); + + if (source == null || !source.isAlive()) + return; + + CharacterItemManager sourceItemMan = source.getCharItemManager(); + + if (sourceItemMan == null) + return; + + try { + sourceItemMan.uncommitToTrade(msg); + } catch (Exception e) { + // TODO Auto-generated catch block + Logger.error(e); + } + + } + + + + public static void closeTradeWindow(CloseTradeWindowMsg msg, ClientConnection origin) { + + PlayerCharacter source = origin.getPlayerCharacter(); + + if (source == null) + return; + + CharacterItemManager sourceItemMan = source.getCharItemManager(); + + if (sourceItemMan == null) + return; + + try { + sourceItemMan.closeTradeWindow(msg, true); + } catch (Exception e) { + // TODO Auto-generated catch block + Logger.error(e); + } + } + + public static void invalidTradeRequest(InvalidTradeRequestMsg msg) { + PlayerCharacter requester = PlayerCharacter.getFromCache(msg.getRequesterID()); + Dispatch dispatch; + + dispatch = Dispatch.borrow(requester, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + } +} \ No newline at end of file diff --git a/src/engine/gameManager/ZoneManager.java b/src/engine/gameManager/ZoneManager.java new file mode 100644 index 00000000..ef8c1c61 --- /dev/null +++ b/src/engine/gameManager/ZoneManager.java @@ -0,0 +1,437 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.gameManager; + +import engine.Enum; +import engine.math.Bounds; +import engine.math.Vector2f; +import engine.math.Vector3f; +import engine.math.Vector3fImmutable; +import engine.objects.Building; +import engine.objects.City; +import engine.objects.Zone; +import engine.server.MBServerStatics; +import engine.server.world.WorldServer; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; + +/* + * Class contains methods and structures which + * track in-game Zones + */ +public enum ZoneManager { + + ZONEMANAGER; + + /* Instance variables */ + private static Zone seaFloor = null; + private static Zone hotzone = null; + private static ConcurrentHashMap zonesByID = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD); + private static ConcurrentHashMap zonesByUUID = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD); + private static ConcurrentHashMap zonesByName = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD); + private static Set macroZones = Collections.newSetFromMap(new ConcurrentHashMap<>()); + private static Set npcCityZones = Collections.newSetFromMap(new ConcurrentHashMap<>()); + private static Set playerCityZones = Collections.newSetFromMap(new ConcurrentHashMap<>()); + + // Find all zones coordinates fit into, starting with Sea Floor + + public static ArrayList getAllZonesIn(final Vector3fImmutable loc) { + + ArrayList allIn = new ArrayList<>(); + Zone zone; + + zone = ZoneManager.findSmallestZone(loc); + + if (zone != null) { + allIn.add(zone); + while (zone.getParent() != null) { + zone = zone.getParent(); + allIn.add(zone); + } + } + return allIn; + } + + // Find smallest zone coordinates fit into. + + public static final Zone findSmallestZone(final Vector3fImmutable loc) { + + Zone zone = ZoneManager.seaFloor; + + if (zone == null) + return null; + + boolean childFound = true; + + while (childFound) { + + childFound = false; + + ArrayList nodes = zone.getNodes(); + + // Logger.info("soze", "" + nodes.size()); + if (nodes != null) + for (Zone child : nodes) { + + if (Bounds.collide(loc, child.getBounds()) == true) { + zone = child; + childFound = true; + break; + } + } + } + return zone; + } + + public static void addZone(final int zoneID, final Zone zone) { + + ZoneManager.zonesByID.put(zoneID, zone); + + if (zone != null) + ZoneManager.zonesByUUID.put(zone.getObjectUUID(), zone); + + ZoneManager.zonesByName.put(zone.getName().toLowerCase(), zone); + + } + + public static Zone getZoneByUUID(final int zoneUUID) { + return ZoneManager.zonesByUUID.get(zoneUUID); + } + + public static Zone getZoneByZoneID(final int zoneID) { + + return ZoneManager.zonesByID.get(zoneID); + } + + public static final Collection getAllZones() { + return ZoneManager.zonesByUUID.values(); + } + + public static final Zone getHotZone() { + return ZoneManager.hotzone; + } + + public static final void setHotZone(final Zone zone) { + if (!zone.isMacroZone()) + return; + ZoneManager.hotzone = zone; + } + + public static boolean inHotZone(final Vector3fImmutable loc) { + + if (ZoneManager.hotzone == null) + return false; + + return (Bounds.collide(loc, ZoneManager.hotzone.getBounds()) == true); + } + + public static void setSeaFloor(final Zone value) { + ZoneManager.seaFloor = value; + } + + public static Zone getSeaFloor() { + return ZoneManager.seaFloor; + } + + public static final void populateWorldZones(final Zone zone) { + + int loadNum = zone.getLoadNum(); + + // Zones are added to separate + // collections for quick access + // based upon their type. + + if (zone.isMacroZone()) { + addMacroZone(zone); + return; + } + + + if (zone.isPlayerCity()) { + addPlayerCityZone(zone); + return; + } + + if (zone.isNPCCity()) + addNPCCityZone(zone); + + } + + private static void addMacroZone(final Zone zone) { + ZoneManager.macroZones.add(zone); + } + + private static void addNPCCityZone(final Zone zone) { + zone.setNPCCity(true); + ZoneManager.npcCityZones.add(zone); + } + + public static final void addPlayerCityZone(final Zone zone) { + zone.setPlayerCity(true); + ZoneManager.playerCityZones.add(zone); + } + + public static final void generateAndSetRandomHotzone() { + + Zone hotzone; + ArrayList zoneArray = new ArrayList<>(); + + if (ZoneManager.macroZones.isEmpty()) + return; + + for (Zone zone : ZoneManager.macroZones) { + + if (validHotZone(zone)) + zoneArray.add(zone.getObjectUUID()); + + } + + int entryIndex = ThreadLocalRandom.current().nextInt(zoneArray.size()); + + hotzone = ZoneManager.getZoneByUUID(zoneArray.get(entryIndex)); + + + if (hotzone == null){ + Logger.error( "Hotzone is null"); + return; + } + + + ZoneManager.setHotZone(hotzone); + WorldServer.setLastHZChange(System.currentTimeMillis()); + + } + + public static final boolean validHotZone(Zone zone) { + + if (zone.getSafeZone() == (byte) 1) + return false; // no safe zone hotzones// if (this.hotzone == null) + + if (zone.getNodes().isEmpty()) + return false; + + if (zone.equals(ZoneManager.seaFloor)) + return false; + + // return false; //first time setting, accept it + // if (this.hotzone.getUUID() == zone.getUUID()) + // return true; //no same hotzone + + if (ZoneManager.hotzone != null) + return ZoneManager.hotzone.getObjectUUID() != zone.getObjectUUID(); + + return true; + } + + /** + * Gets a MacroZone by name. + * + * @param inputName + * MacroZone name to search for + * @return Zone of the MacroZone, or Null + */ + + public static Zone findMacroZoneByName(String inputName) { + synchronized (ZoneManager.macroZones) { + for (Zone zone : ZoneManager.macroZones) { + String zoneName = zone.getName(); + if (zoneName.equalsIgnoreCase(inputName)) + return zone; + } + } + return null; + } + + // Converts world coordinates to coordinates local to a given zone. + + public static Vector3fImmutable worldToLocal(Vector3fImmutable worldVector, + Zone serverZone) { + + Vector3fImmutable localCoords; + + localCoords = new Vector3fImmutable(worldVector.x - serverZone.absX, + worldVector.y - serverZone.absY, worldVector.z + - serverZone.absZ); + + return localCoords; + } + + public static Vector2f worldToZoneSpace(Vector3fImmutable worldVector, + Zone serverZone) { + + Vector2f localCoords; + Vector2f zoneOrigin; + + // Top left corner of zone is calculated in world space by the center and it's extents. + + zoneOrigin = new Vector2f(serverZone.getLoc().x, serverZone.getLoc().z); + zoneOrigin = zoneOrigin.subtract(new Vector2f(serverZone.getBounds().getHalfExtents().x, serverZone.getBounds().getHalfExtents().y)); + + // Local coordinate in world space translated to an offset from the calculated zone origin. + + localCoords = new Vector2f(worldVector.x, worldVector.z); + localCoords = localCoords.subtract(zoneOrigin); + + localCoords.setY((serverZone.getBounds().getHalfExtents().y * 2) - localCoords.y); + + + + + // TODO : Make sure this value does not go outside the zone's bounds. + + return localCoords; + } + + // Converts local zone coordinates to world coordinates + + public static Vector3fImmutable localToWorld(Vector3fImmutable worldVector, + Zone serverZone) { + + Vector3fImmutable worldCoords; + + worldCoords = new Vector3fImmutable(worldVector.x + serverZone.absX, + worldVector.y + serverZone.absY, worldVector.z + + serverZone.absZ); + + return worldCoords; + } + + + /** + * Converts from local (relative to this building) to world. + * + * @param localPos position in local reference (relative to this building) + * @return position relative to world + */ + + public static Vector3fImmutable convertLocalToWorld(Building building, Vector3fImmutable localPos) { + + // convert from SB rotation value to radians + + + if (building.getBounds().getQuaternion() == null) + return building.getLoc(); + Vector3fImmutable rotatedLocal = Vector3fImmutable.rotateAroundPoint(Vector3fImmutable.ZERO, localPos, building.getBounds().getQuaternion()); + // handle building rotation + // handle building translation + + return building.getLoc().add(rotatedLocal.x, rotatedLocal.y,rotatedLocal.z); + } + + + //used for regions, Building bounds not set yet. + public static Vector3f convertLocalToWorld(Building building, Vector3f localPos, Bounds bounds) { + + // convert from SB rotation value to radians + + + Vector3f rotatedLocal = Vector3f.rotateAroundPoint(Vector3f.ZERO, localPos, bounds.getQuaternion()); + // handle building rotation + // handle building translation + + return new Vector3f(building.getLoc().add(rotatedLocal.x, rotatedLocal.y,rotatedLocal.z)); + } + + public static Vector3fImmutable convertWorldToLocal(Building building, Vector3fImmutable WorldPos) { + Vector3fImmutable convertLoc = Vector3fImmutable.rotateAroundPoint(building.getLoc(),WorldPos,-building.getBounds().getQuaternion().angleY); + + + convertLoc = convertLoc.subtract(building.getLoc()); + + // convert from SB rotation value to radians + + return convertLoc; + + } + + public static Vector3fImmutable convertNPCLoc(Building building, Vector3fImmutable npcLoc) { + + return Vector3fImmutable.rotateAroundPoint(Vector3fImmutable.ZERO, npcLoc, -building.getBounds().getQuaternion().angleY); + + } + + // Method returns a city if the given location is within + // a city siege radius. + + public static City getCityAtLocation(Vector3fImmutable worldLoc) { + + Zone currentZone; + ArrayList zoneList; + City city; + + currentZone = ZoneManager.findSmallestZone(worldLoc); + + if (currentZone.isPlayerCity()) + return City.getCity(currentZone.getPlayerCityUUID()); + + // Not currently on a city grid. Test nearby cities + // to see if we are on one of their seige bounds. + + zoneList = currentZone.getNodes(); + + for (Zone zone : zoneList) { + + if (zone == currentZone) + continue; + + if (zone.isPlayerCity() == false) + continue; + + city = City.getCity(zone.getPlayerCityUUID()); + + if (worldLoc.isInsideCircle(city.getLoc(), Enum.CityBoundsType.SIEGE.extents)) + return city; + } + + return null; + } + + /* Method is called when creating a new player city to + * validate that the new zone does not overlap any other + * zone that might currently exist + */ + + public static boolean validTreePlacementLoc(Zone currentZone, float positionX, float positionZ) { + + // Member Variable declaration + + ArrayList zoneList; + boolean validLocation = true; + Bounds treeBounds; + + if (currentZone.isContininent() == false) + return false; + + + treeBounds = Bounds.borrow(); + treeBounds.setBounds(new Vector2f(positionX, positionZ), new Vector2f(Enum.CityBoundsType.SIEGE.extents, Enum.CityBoundsType.SIEGE.extents), 0.0f); + + zoneList = currentZone.getNodes(); + + + + for (Zone zone : zoneList) { + + if (zone.isContininent()) + continue; + + if (Bounds.collide(treeBounds, zone.getBounds(), 0.0f)) + validLocation = false; + } + + treeBounds.release(); + return validLocation; + } +} diff --git a/src/engine/job/AbstractJob.java b/src/engine/job/AbstractJob.java new file mode 100644 index 00000000..72ff2c58 --- /dev/null +++ b/src/engine/job/AbstractJob.java @@ -0,0 +1,151 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.job; + +import org.pmw.tinylog.Logger; + +import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; + + +/** + * Provides mandatory base implementation for all 'Job's'. + * + * @author + */ +public abstract class AbstractJob implements Runnable { + + public enum JobRunStatus { + CREATED, RUNNING, FINISHED; + }; + + public enum JobCompletionStatus { + NOTCOMPLETEDYET, SUCCESS, SUCCESSWITHERRORS, FAIL; + }; + + /** + * Keep fields private. All access through getters n setters for Thread + * Safety. + */ + private JobRunStatus runStatus; + private JobCompletionStatus completeStatus; + private UUID uuid = null; + + private final AtomicReference workerID; + private static final String DEFAULT_WORKERID = "UNPROCESSED_JOB"; + + private long submitTime = 0L; + private long startTime = 0L; + private long stopTime = 0L; + + public AbstractJob() { + //Tests against DEFAULT_WORKERID to ensure single execution + this.workerID = new AtomicReference<>(DEFAULT_WORKERID); + this.runStatus = JobRunStatus.RUNNING; + this.completeStatus = JobCompletionStatus.NOTCOMPLETEDYET; + this.uuid = UUID.randomUUID(); + } + + @Override + public void run() { + + if(workerID.get().equals(DEFAULT_WORKERID)) { + Logger.warn("FIX ME! Job ran through 'run()' directly. Use executeJob(String) instead."); + } + + this.markStartRunTime(); + + this.setRunStatus(JobRunStatus.RUNNING); + try { + this.doJob(); + } catch (Exception e) { + Logger.error(e); + } + + this.setRunStatus(JobRunStatus.FINISHED); + + this.markStopRunTime(); + } + + protected abstract void doJob(); + + public void executeJob(String threadName) { + this.workerID.set(threadName); + this.run(); + } + + protected void setRunStatus(JobRunStatus status) { + synchronized (this.runStatus) { + this.runStatus = status; + } + } + + public JobRunStatus getRunStatus() { + synchronized (this.runStatus) { + return runStatus; + } + } + + protected void setCompletionStatus(JobCompletionStatus status) { + synchronized (this.completeStatus) { + this.completeStatus = status; + } + } + + public JobCompletionStatus getCompletionStatus() { + synchronized (this.completeStatus) { + return completeStatus; + } + } + + protected void setJobId(UUID id) { + synchronized (this.uuid) { + this.uuid = id; + } + } + + public UUID getJobId() { + synchronized (this.uuid) { + return uuid; + } + } + + public String getWorkerID() { + return workerID.get(); + } + + /* + * Time markers + */ + + protected void markSubmitTime() { + this.submitTime = System.currentTimeMillis()-2; + } + + protected void markStartRunTime() { + this.startTime = System.currentTimeMillis()-1; + } + + protected void markStopRunTime() { + this.stopTime = System.currentTimeMillis(); + } + + public final long getSubmitTime() { + return submitTime; + } + + public final long getStartTime() { + return startTime; + } + + public final long getStopTime() { + return stopTime; + } +} diff --git a/src/engine/job/AbstractJobStatistics.java b/src/engine/job/AbstractJobStatistics.java new file mode 100644 index 00000000..be5a6e4a --- /dev/null +++ b/src/engine/job/AbstractJobStatistics.java @@ -0,0 +1,118 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.job; + +public abstract class AbstractJobStatistics { + + private String objectName; + private long totalServiceTime = 0L; + private long totalQueueTime = 0L; + private long executions = 0L; + private long maxServiceTime = 0L; + private long minServiceTime = 0L; + private long minQueueTime = 0L; + private long maxQueueTime = 0L; + + + public AbstractJobStatistics() { + this.objectName = "Unknown"; + } + + public AbstractJobStatistics(String objectName) { + this.objectName = objectName; + } + + public void setObjectName (String objectName) { + this.objectName = objectName; + } + + public String getObjectName () { + return this.objectName; + } + + public long getExecutions() { + return this.executions; + } + + public long getTotalServiceTime() { + return this.totalServiceTime; + } + + public long getTotalQueueTime() { + return this.totalQueueTime; + } + + public long getAvgQueueTime() { + if (this.executions > 0L && this.totalQueueTime > 0L) + return this.totalQueueTime / this.executions; + else + return 0L; + } + + public long getAvgServiceTime() { + if (this.executions > 0L && this.totalServiceTime > 0L) + return this.totalServiceTime / this.executions; + else + return 0L; + } + + public long getMinServiceTime() { + return this.minServiceTime; + } + + public long getMinQueueTime() { + return this.minQueueTime; + } + + public long getMaxServiceTime() { + return this.maxServiceTime; + } + + public long getMaxQueueTime() { + return this.maxQueueTime; + } + + public void incrExecutions() { + this.executions++; + } + + public void addServiceTime(long svcTime) { + this.totalServiceTime += svcTime; + this.incrExecutions(); + if (svcTime > this.maxServiceTime) + this.maxServiceTime = svcTime; + if (svcTime < this.minServiceTime || this.minServiceTime == 0L) + this.minServiceTime = svcTime; + + } + + public void addQueueTime(long queueTime) { + this.totalQueueTime += queueTime; + this.incrExecutions(); + if (queueTime > this.maxQueueTime) + this.maxQueueTime = queueTime; + if (queueTime < this.minQueueTime || this.minQueueTime == 0L) + this.minQueueTime = queueTime; + + } + + public String asString() { + return this.objectName + " execs=" + this.executions + " avg_svc_ms=" + this.getAvgServiceTime() + + " min_svc_ms=" + this.minServiceTime + " max_svc_ms=" + this.maxServiceTime + + " avg_q_ms=" + this.getAvgQueueTime() + " min_q_ms=" + this.minQueueTime + + " max_q_ms=" + this.maxQueueTime; + } + + public String asChatMsg() { + return this.objectName + "=>" + this.executions + ',' + this.getAvgServiceTime() + '/' + this.minServiceTime + + '/' + this.maxServiceTime + " "+ this.getAvgQueueTime() + '/' + + this.minQueueTime + '/' + this.maxQueueTime + '\n'; + } +} diff --git a/src/engine/job/AbstractScheduleJob.java b/src/engine/job/AbstractScheduleJob.java new file mode 100644 index 00000000..c33ae9ae --- /dev/null +++ b/src/engine/job/AbstractScheduleJob.java @@ -0,0 +1,28 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + package engine.job; + + +public abstract class AbstractScheduleJob extends AbstractJob { + + public AbstractScheduleJob() { + super(); + } + + @Override + protected abstract void doJob(); + + public void cancelJob() { + JobScheduler.getInstance().cancelScheduledJob(this); + _cancelJob(); + } + + protected abstract void _cancelJob(); +} diff --git a/src/engine/job/ClassJobStatistics.java b/src/engine/job/ClassJobStatistics.java new file mode 100644 index 00000000..be283ab5 --- /dev/null +++ b/src/engine/job/ClassJobStatistics.java @@ -0,0 +1,19 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.job; + +public class ClassJobStatistics extends AbstractJobStatistics { + + public ClassJobStatistics (String className) { + super(className); + } + + +} diff --git a/src/engine/job/JobContainer.java b/src/engine/job/JobContainer.java new file mode 100644 index 00000000..56048b5c --- /dev/null +++ b/src/engine/job/JobContainer.java @@ -0,0 +1,84 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.job; + + +public class JobContainer implements Comparable { + + final AbstractJob job; + final long timeOfExecution; + final boolean noTimer; + + JobContainer(AbstractJob job, long timeOfExecution) { + if (job == null) { + throw new IllegalArgumentException("No 'null' jobs allowed."); + } + this.job = job; + this.timeOfExecution = timeOfExecution; + this.noTimer = false; + } + + public JobContainer(AbstractJob job) { + if (job == null) { + throw new IllegalArgumentException("No 'null' jobs allowed."); + } + this.job = job; + this.timeOfExecution = Long.MAX_VALUE; + this.noTimer = true; + } + + public AbstractJob getJob() { + return job; + } + + public boolean noTimer() { + return noTimer; + } + + public long timeOfExection() { + return this.timeOfExecution; + } + + public int timeToExecutionLeft() { + if (JobScheduler.getInstance().isAlive()) { + int timeLeft = (int) (timeOfExecution - System.currentTimeMillis()); + if (timeLeft < 0) + timeLeft = 0; + return timeLeft; + } else + return (int) (timeOfExecution - JobScheduler.getInstance().getTimeOfKill()); + } + + @Override + public int compareTo(JobContainer compared) { + if (timeOfExecution < compared.timeOfExecution) { + return -1; + } + if (timeOfExecution > compared.timeOfExecution) { + return 1; + } + return 0; + } + + @Override + public boolean equals(Object obj) { + return job.equals(((JobContainer) obj).job); + } + + @Override + public int hashCode() { + return job.hashCode(); + } + + public void cancelJob() { + if (job != null && job instanceof AbstractScheduleJob) + ((AbstractScheduleJob)job).cancelJob(); + } +} diff --git a/src/engine/job/JobManager.java b/src/engine/job/JobManager.java new file mode 100644 index 00000000..36612f7f --- /dev/null +++ b/src/engine/job/JobManager.java @@ -0,0 +1,232 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.job; + +import engine.core.ControlledRunnable; +import engine.server.MBServerStatics; +import engine.util.ThreadUtils; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; + + +/** + * Note to DEVs. When attempting to log something in this class, use logDIRECT + * only. + */ +public class JobManager extends ControlledRunnable { + + /* + * Singleton implementation. + */ + private static volatile JobManager INSTANCE; + public static JobManager getInstance() { + if (JobManager.INSTANCE == null) { + synchronized (JobManager.class) { + if (JobManager.INSTANCE == null) { + JobManager.INSTANCE = new JobManager(); + JobManager.INSTANCE.startup(); + } + } + } + return JobManager.INSTANCE; + } + + /* + * Class implementation + */ + + private final ArrayList jobPoolList = new ArrayList<>(); + private final ConcurrentHashMap jobQueueMapping= new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_HIGH); + + private boolean shutdownNowFlag = false; + + private JobManager() { + super("JobManager"); + + // create the initial job pools with the correct sizes + // based on the number of array elements in the initial_jo_workers + // definition in Statisc + + if (MBServerStatics.INITIAL_JOBPOOL_WORKERS != null && MBServerStatics.INITIAL_JOBPOOL_WORKERS.length >0 ) { + for (int i=0; i required"; + } + + public String resetJobs() { + // moves all jobs to the P1 queue + this.jobQueueMapping.clear(); + return "All Jobs reset onto P1 queue"; + } + + public String showJobs() { + String out = ""; + Iterator jmi = this.jobQueueMapping.keySet().iterator(); + + while (jmi.hasNext()) { + String jmiKey = jmi.next(); + out += jmiKey + ' ' + this.jobQueueMapping.get(jmiKey).getJobPoolID() + '\n'; + } + return out; + } + + public ArrayList getJobPoolList() { + + return this.jobPoolList; + } + + public String modifyJobPoolWorkers(String jobPoolID, String maxWorkers) { + + try { + // parse string into an int + Integer jid = Integer.parseInt(jobPoolID); + Integer mw = Integer.parseInt(maxWorkers); + for (JobPool jp : this.jobPoolList) { + if (jp.getJobPoolID() == jid) { + // change the number of workers + return jp.setMaxWorkers(mw); + } + } + + } catch (NumberFormatException e) { + return "Invalid parameters required"; + } + + return "Invalid parameters required"; + } +} diff --git a/src/engine/job/JobPool.java b/src/engine/job/JobPool.java new file mode 100644 index 00000000..fc874407 --- /dev/null +++ b/src/engine/job/JobPool.java @@ -0,0 +1,295 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.job; + + +import engine.jobs.AttackJob; +import engine.jobs.UsePowerJob; +import engine.net.CheckNetMsgFactoryJob; +import engine.net.ConnectionMonitorJob; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Queue; +import java.util.concurrent.LinkedBlockingQueue; + +public class JobPool { + + int jobPoolID; + int maxWorkers; + int nextWorkerID; + private final LinkedBlockingQueue jobWaitQueue = new LinkedBlockingQueue<>(); + private final LinkedBlockingQueue jobWorkerQueue = new LinkedBlockingQueue<>(); + private final ArrayList jobWorkerList = new ArrayList<>(); + private final LinkedBlockingQueue jobRunList = new LinkedBlockingQueue<>(); + private boolean blockNewSubmissions = false; + + public JobPool(int id, int workers) { + this.jobPoolID = id; + + // default to 1 unless workers parameter is higher + int actualWorkers = 1; + if (workers > 1) + actualWorkers = workers; + + this.maxWorkers = actualWorkers; + for (int i=0;i q) { + HashMap ch = new HashMap<>(); + int cnt = 0; + + + // iterate through the linked queue and get every item + // putting classname and incrementing the value each time in the hashmap + Iterator wi = q.iterator(); + + while (cnt < q.size() && wi.hasNext()) { + AbstractJob aj = wi.next(); + if (ch.containsKey(aj.getClass().getSimpleName())) { + int newValue = ch.get(aj.getClass().getSimpleName()) + 1; + ch.put(aj.getClass().getSimpleName(), newValue); + } else { + ch.put(aj.getClass().getSimpleName(), 1); + } + cnt++; + } + + // iterate through the hashmap outputting the classname and number of jobs + Iterator i = ch.keySet().iterator(); + String out = ""; + while(i.hasNext()) { + Object key = i.next(); + out += "JobPoolID_" + this.jobPoolID + ' ' + key.toString() + "=>" + ch.get(key) + '\n'; + } + if (out.isEmpty()) + return "No Jobs on queue\n"; + else + return out; + } + + public void auditWorkers() { + + if(!MBServerStatics.ENABLE_AUDIT_JOB_WORKERS) { + return; + } + ArrayList problemJobs = new ArrayList<>(); + + // Checked for stalled Workers + Iterator it = jobWorkerList.iterator(); + + while (it.hasNext()) { + JobWorker jw = it.next(); + AbstractJob curJob = jw.getCurrentJob(); + + if (curJob != null) { // Has a job + + if (JobPool.isExemptJobFromAudit(curJob)) { + continue; + } + + // Determine whether the job is being executed or waiting to + // start; + + if (curJob.getStartTime() <= 0) { + // Waiting to start + long diff = System.currentTimeMillis() - curJob.getSubmitTime(); + + if (diff >= MBServerStatics.JOB_STALL_THRESHOLD_MS) { + Logger.warn("Job did not start within threshold. Stopping worker#" + jw.getWorkerId() + " JobData:" + + curJob.toString()); + jw.EmergencyStop(); + problemJobs.add(jw.getCurrentJob()); + it.remove(); + } // end if (diff >= + + } else if (curJob.getStopTime() <= 0L) { + // is executing it + long diff = System.currentTimeMillis() - curJob.getStartTime(); + + if (diff >= MBServerStatics.JOB_STALL_THRESHOLD_MS) { + Logger.warn("Job execution time exceeded threshold(" + diff + "). Stopping worker#" + jw.getWorkerId() + " JobData:" + + curJob.toString()); + jw.EmergencyStop(); + problemJobs.add(jw.getCurrentJob()); + it.remove(); + } // end if (diff >= + } // end if(curJob.getStopTime() + } // end if(curJob != null) + } // end While + + // Check Worker Count and add workers as necessary; + int workerCount = jobWorkerList.size(); + + int maxThreads = this.maxWorkers; + + + // no pool can go below a single thread + if (maxThreads < 1) + maxThreads = 1; + + while (workerCount != maxThreads) { + Logger.info("Resizing JobPool " + this.jobPoolID + " from " + workerCount + " to " + maxThreads); + + if (workerCount < maxThreads) { + this.startWorker(this.getNextWorkerID()); + + + if (jobWorkerList.size() <= workerCount) { + // Something didnt work correctly + Logger.warn("auditWorkers() failed to add a new JobWorker to JobPool " + this.jobPoolID + ". Worker count " + workerCount + " Worker pool size " + jobWorkerList.size() + " Aborting Audit."); + return; + } + + } else if (workerCount > maxThreads) { + synchronized(this.jobWorkerList) { + Logger.warn("Reducing workers in JobPool " + this.jobPoolID + " Worker Count: " + workerCount + " to Max threads: " + maxThreads); + // pick a worker off the list and shut it down + + JobWorker toRemove = null; + int loopTries = 5; + do { + //Infinite loop could be bad.. + toRemove = jobWorkerQueue.poll(); + } while (toRemove == null && --loopTries >= 0); + + //remove it from the list + toRemove.shutdown(); + jobWorkerList.remove(toRemove); + } + } + + // update value for next loop pass + workerCount = jobWorkerList.size(); + } + + } + + private static boolean isExemptJobFromAudit(AbstractJob aj) { + // If the job is any of the following classes, exempt it from auditWorkers + if (aj instanceof ConnectionMonitorJob) { + return true; + } else + return aj instanceof CheckNetMsgFactoryJob || aj instanceof AttackJob || aj instanceof UsePowerJob; + + } + + public void shutdown() { + synchronized(this.jobWorkerList) { + for (JobWorker jw : this.jobWorkerList) + jw.shutdown(); + } + } + + public void emergencyStop() { + synchronized(this.jobWorkerList) { + for (JobWorker jw : this.jobWorkerList) + jw.EmergencyStop(); + } + } + + public String getRunningQueueByClassAsString() { + return this.getQueueByClassAsString(this.jobRunList); + } + + public String getWaitQueueByClassAsString () { + return this.getQueueByClassAsString(this.jobWaitQueue); + } + + + // used by devcmds + public String setMaxWorkers (int maxWorkers) { + + if (maxWorkers > 0 && maxWorkers < 101) { + this.maxWorkers = maxWorkers; + // audit workers reduces the cap + this.auditWorkers(); + return "Max workers set to " + maxWorkers + " for JobPool_" + this.jobPoolID; + } else { + return "Max workers not set, value must be from 1-100"; + } + + } +} diff --git a/src/engine/job/JobScheduler.java b/src/engine/job/JobScheduler.java new file mode 100644 index 00000000..4ba503d6 --- /dev/null +++ b/src/engine/job/JobScheduler.java @@ -0,0 +1,169 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.job; + +import engine.server.MBServerStatics; + +import java.util.PriorityQueue; + + +public class JobScheduler { + + private static final JobScheduler INSTANCE = new JobScheduler(); + + private final PriorityQueue jobs; + private volatile boolean alive; + private long timeOfKill = -1; + + public static JobScheduler getInstance() { + return INSTANCE; + } + + private JobScheduler() { + jobs = new PriorityQueue<>(MBServerStatics.SCHEDULER_INITIAL_CAPACITY); + Runnable worker = new Runnable() { + @Override + public void run() { + execution(); + } + }; + + alive = true; + + Thread t = new Thread(worker, "JobScheduler"); + t.start(); + } + + /** + * This function schedules a job to execute in timeToExecution + * milliseconds from now. + * + * @param job + * @param timeToExecution + * @return + */ + public JobContainer scheduleJob(AbstractJob job, int timeToExecution) { + long timeOfExecution = System.currentTimeMillis() + timeToExecution; + JobContainer container = new JobContainer(job, timeOfExecution); + + synchronized (jobs) { + jobs.offer(container); + jobs.notify(); + } + + return container; + } + + /** + * This function schedules a job to execute at the absolute time of + * timeOfExecution (milliseconds). + * + * @param job + * @param timeOfExecution + * @return + */ + public JobContainer scheduleJob(AbstractJob job, long timeOfExecution) { + JobContainer container = new JobContainer(job, timeOfExecution); + + synchronized (jobs) { + jobs.offer(container); + jobs.notify(); + } + + return container; + } + + public boolean cancelScheduledJob(JobContainer container) { + return cancelScheduledJob(container.getJob()); + } + + public boolean cancelScheduledJob(AbstractJob job) { + JobContainer container = new JobContainer(job, -1); + + boolean success = false; + synchronized (jobs) { + success = jobs.remove(container); + jobs.notify(); + } + + return success; + } + + /** + * Stops the jobScheduler + */ + public void shutdown() { + if (alive) { + alive = false; + timeOfKill = System.currentTimeMillis(); + synchronized (jobs) { + jobs.notify(); + } + } + } + + public JobContainer pollNextJobContainer() { + if (alive) { + throw new IllegalStateException("Can't poll jobs from a live scheduler."); + } + + synchronized (jobs) { + return jobs.poll(); + } + } + + public long getTimeOfKill() { + return this.timeOfKill; + } + + public boolean isAlive() { + return this.alive; + } + + private void execution() { + long duration; + JobContainer container; + int compensation = MBServerStatics.SCHEDULER_EXECUTION_TIME_COMPENSATION; + + while (alive) { + synchronized (jobs) { + container = jobs.peek(); + if (container == null) { + // queue is empty, wait until notified (which happens after + // a new job is offered) + try { + jobs.wait(0); + } catch (InterruptedException ie) { + // do nothing + } + } else { + duration = container.timeOfExecution - System.currentTimeMillis(); + if (duration < compensation) { + jobs.poll(); + } else { + // enforce new loop + container = null; + + // sleep until the head job execution time + try { + jobs.wait(duration); + } catch (InterruptedException ie) { + // do nothing + } + } + } + } + + if (container != null) { + JobManager.getInstance().submitJob(container.job); + } + } + } +} diff --git a/src/engine/job/JobWorker.java b/src/engine/job/JobWorker.java new file mode 100644 index 00000000..2fc24c4d --- /dev/null +++ b/src/engine/job/JobWorker.java @@ -0,0 +1,109 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.job; + +import engine.core.ControlledRunnable; +import org.pmw.tinylog.Logger; + +import java.util.Queue; + + +public class JobWorker extends ControlledRunnable { + private final int workerId; + + private final Queue jobWaitQueue; + private final Queue jobWorkerList; + + private AbstractJob currentJob; + + public JobWorker(final int workerID, int priorityQueue, + Queue jobWaitQueue, + Queue jobWorkerList) { + super("JobWorker_" + priorityQueue + '_' + workerID); + + workerId = workerID; + this.jobWaitQueue = jobWaitQueue; + this.jobWorkerList = jobWorkerList; + } + + @Override + protected boolean _Run() { + + while (this.runCmd) { + // Access to Queue is synchronized internal to JobManager + this.currentJob = this.jobWaitQueue.poll(); + + if (this.currentJob == null) { + try { + // use self as MUTEX + synchronized (this) { + this.jobWorkerList.add(this); + this.wait(); + } + + } catch (InterruptedException e) { + Logger.error(this.getThreadName(), e.getClass() + .getSimpleName() + + ": " + e.getMessage()); + break; + + } + } else { + + // execute the new job.. + this.currentJob.executeJob(this.getThreadName()); + this.currentJob = null; + } + + } + return true; + } + + @Override + protected boolean _postRun() { + return true; + } + + @Override + protected boolean _preRun() { + return true; + } + + @Override + protected void _shutdown() { + } + + @Override + protected void _startup() { + //this.logDirectINFO(this.getThreadName(), "Starting up..."); + } + + public final int getWorkerId() { + return workerId; + } + + public final AbstractJob getCurrentJob() { + return currentJob; + } + + public final boolean hasCurrentJob() { + return (currentJob != null); + } + + protected void EmergencyStop() { + this.runCmd = false; + String out = "Stack Trace"; + for(StackTraceElement e : this.getThisThread().getStackTrace()) { + out += " -> " + e.toString(); + } + Logger.info(out); + this.getThisThread().interrupt(); + } +} diff --git a/src/engine/jobs/AbstractEffectJob.java b/src/engine/jobs/AbstractEffectJob.java new file mode 100644 index 00000000..eaafb760 --- /dev/null +++ b/src/engine/jobs/AbstractEffectJob.java @@ -0,0 +1,152 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + package engine.jobs; + +import engine.job.AbstractScheduleJob; +import engine.objects.AbstractWorldObject; +import engine.powers.ActionsBase; +import engine.powers.EffectsBase; +import engine.powers.PowersBase; + + +public abstract class AbstractEffectJob extends AbstractScheduleJob { + + protected String stackType; + protected AbstractWorldObject target; + protected AbstractWorldObject source; + protected int trains; + protected ActionsBase action; + protected PowersBase power; + protected EffectsBase eb; + protected boolean skipSendEffect=false; + protected boolean skipApplyEffect=false; + protected boolean isChant=false; + protected boolean skipCancelEffect=false; + private boolean noOverwrite; + private int effectSourceType = 0; + private int effectSourceID = 0; + + public AbstractEffectJob(AbstractWorldObject source, AbstractWorldObject target, String stackType, int trains, ActionsBase action, PowersBase power, EffectsBase eb) { + super(); + this.source = source; + this.target = target; + this.stackType = stackType; + this.trains = trains; + this.action = action; + this.power = power; + this.eb = eb; + } + + @Override + protected abstract void doJob(); + @Override + protected abstract void _cancelJob(); + + public String getStackType() { + return this.stackType; + } + + public AbstractWorldObject getTarget() { + return this.target; + } + + public AbstractWorldObject getSource() { + return this.target; + } + + public int getTrains() { + return this.trains; + } + + public ActionsBase getAction() { + return this.action; + } + + public PowersBase getPower() { + return this.power; + } + + public int getPowerToken() { + if (this.power == null) + return 0; + return this.power.getToken(); + } + + public EffectsBase getEffect() { + return this.eb; + } + + public boolean skipSendEffect() { + return this.skipSendEffect; + } + + public void setSkipSendEffect(boolean value) { + this.skipSendEffect = value; + } + + public boolean skipApplyEffect() { + return this.skipApplyEffect; + } + + public void setSkipApplyEffect(boolean value) { + this.skipApplyEffect = value; + } + + public boolean skipCancelEffect() { + return this.skipCancelEffect; + } + + public void setSkipCancelEffect(boolean value) { + this.skipCancelEffect = value; + } + + public boolean isChant() { + return this.isChant; + } + + public void setChant(boolean value) { + this.isChant = value; + } + + public void endEffect() { + if (this.eb == null) + return; + this.eb.endEffect(this.source, this.target, this.trains, this.power, this); + } + + public void endEffectNoPower() { + if (this.eb == null) + return; + this.eb.endEffectNoPower(this.trains,this); + } + public boolean isNoOverwrite() { + return noOverwrite; + } + + public void setNoOverwrite(boolean noOverwrite) { + this.noOverwrite = noOverwrite; + } + + public int getEffectSourceType() { + return effectSourceType; + } + + public void setEffectSourceType(int effectSourceType) { + this.effectSourceType = effectSourceType; + } + + public int getEffectSourceID() { + return effectSourceID; + } + + public void setEffectSourceID(int effectSourceID) { + this.effectSourceID = effectSourceID; + } +} diff --git a/src/engine/jobs/ActivateBaneJob.java b/src/engine/jobs/ActivateBaneJob.java new file mode 100644 index 00000000..ee5df670 --- /dev/null +++ b/src/engine/jobs/ActivateBaneJob.java @@ -0,0 +1,76 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.Enum; +import engine.Enum.ChatChannelType; +import engine.gameManager.DbManager; +import engine.job.AbstractScheduleJob; +import engine.net.DispatchMessage; +import engine.net.client.msg.chat.ChatSystemMsg; +import engine.objects.City; +import org.pmw.tinylog.Logger; + +public class ActivateBaneJob extends AbstractScheduleJob { + + private final int cityUUID; + + public ActivateBaneJob(int cityUUID) { + super(); + this.cityUUID = cityUUID; + + } + + @Override + protected void doJob() { + + City city; + + city = (City) DbManager.getObject(Enum.GameObjectType.City, cityUUID); + + if (city == null) + return; + + + if (city.getBane() == null) { + Logger.info( "No bane found for " + city.getCityName()); + return; + } + + if (city.getBane().isErrant()) { + Logger.info("Removed errant bane on " + city.getCityName()); + city.getBane().remove(); + return; + } + + if (city.getBane() == null) + return; + + if (city.protectionEnforced == true) + city.protectionEnforced = false; + else { + Logger.info("Bane on " + city.getCityName() + " activated for unprotected city?"); + return; + } + + Logger.info("ActivateBaneJob", "Bane on " + city.getCityName() + " is now active"); + + ChatSystemMsg msg = new ChatSystemMsg(null, "[Bane Channel] The Banecircle placed by " + city.getBane().getOwner().getGuild().getName() + " is now active! Buildings are now vulnerable to damage!"); + msg.setMessageType(4); // Error message + msg.setChannel(ChatChannelType.SYSTEM.getChannelID()); + + DispatchMessage.dispatchMsgToAll(msg); + } + + @Override + protected void _cancelJob() { + } + +} diff --git a/src/engine/jobs/AttackJob.java b/src/engine/jobs/AttackJob.java new file mode 100644 index 00000000..3e3ba607 --- /dev/null +++ b/src/engine/jobs/AttackJob.java @@ -0,0 +1,39 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + package engine.jobs; + +import engine.gameManager.CombatManager; +import engine.job.AbstractJob; +import engine.objects.AbstractCharacter; + +public class AttackJob extends AbstractJob { + + private final AbstractCharacter source; + private final int slot; + private final boolean success; + + public AttackJob(AbstractCharacter source, int slot, boolean success) { + super(); + this.source = source; + this.slot = slot; + this.success = success; + } + + @Override + protected void doJob() { + CombatManager.doCombat(this.source, slot); + } + + public boolean success() { + return this.success; + } + protected void _cancelJob() { + } +} \ No newline at end of file diff --git a/src/engine/jobs/BaneDefaultTimeJob.java b/src/engine/jobs/BaneDefaultTimeJob.java new file mode 100644 index 00000000..54f8e162 --- /dev/null +++ b/src/engine/jobs/BaneDefaultTimeJob.java @@ -0,0 +1,47 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.job.AbstractScheduleJob; +import engine.objects.Bane; +import org.joda.time.DateTime; + +public class BaneDefaultTimeJob extends AbstractScheduleJob { + + private final Bane bane; + + public BaneDefaultTimeJob(Bane bane) { + super(); + this.bane = bane; + + } + + @Override + protected void doJob() { + + //bane already set. + if (this.bane.getLiveDate() != null) { + return; + } + + DateTime defaultTime = new DateTime(this.bane.getPlacementDate()); + defaultTime = defaultTime.plusDays(2); + defaultTime = defaultTime.hourOfDay().setCopy(22); + defaultTime = defaultTime.minuteOfHour().setCopy(0); + defaultTime = defaultTime.secondOfMinute().setCopy(0); + this.bane.setLiveDate(defaultTime); + + } + + @Override + protected void _cancelJob() { + } + +} diff --git a/src/engine/jobs/BasicScheduledJob.java b/src/engine/jobs/BasicScheduledJob.java new file mode 100644 index 00000000..e4bb8fb2 --- /dev/null +++ b/src/engine/jobs/BasicScheduledJob.java @@ -0,0 +1,126 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.job.AbstractJob; +import engine.job.JobScheduler; +import org.pmw.tinylog.Logger; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + + +/** + * A generic execution job which is an extension of {@link AbstractJob}. + *

+ * Intended to be used with the {@link JobScheduler}, a BasicScheduledJob will + * hold a reference to an execution method. + * + * @author Burfo + **/ + +public class BasicScheduledJob extends AbstractJob { + + private Method execution; + private Object referenceObject; + + /** + * Generates a new BasicScheduledJob that executes a static method that has + * no parameters. + * + * @param methodName + * Name of the static method to execute, such as "myMethod" + * + * @param methodClass + * The class in which {@code methodName} exists + */ + public BasicScheduledJob(String methodName, Class methodClass) { + this(methodName, methodClass, null); + } + + /** + * Generates a new BasicScheduledJob that executes an instance method that + * has no parameters. + * + * @param methodName + * Name of the instance method to execute, such as "myMethod" + * + * @param methodClass + * The class in which {@code methodName} exists + * + * @param referenceObject + * Instance of {@code methodClass} against which {@code + * methodName} should be executed + */ + @SuppressWarnings("unchecked") + public BasicScheduledJob(String methodName, Class methodClass, Object referenceObject) { + super(); + Method method = null; + try { + method = methodClass.getMethod(methodName); + } catch (SecurityException e) { + Logger.error( e); + } catch (NoSuchMethodException e) { + Logger.error( e); + } + setData(method, null); + } + + /** + * Generates a new BasicScheduledJob that executes a static method that has + * no parameters. + * + * @param executionMethod + * Reference to the static method to execute + */ + public BasicScheduledJob(Method executionMethod) { + this(executionMethod, null); + } + + /** + * Generates a new BasicScheduledJob that executes an instance method that + * has no parameters. + * + * @param executionMethod + * Reference to the static method to execute + * + * @param referenceObject + * Instanciated object against which {@code executionMethod} + * should be executed + */ + public BasicScheduledJob(Method executionMethod, Object referenceObject) { + super(); + setData(executionMethod, referenceObject); + } + + private void setData(Method executionMethod, Object referenceObject) { + this.execution = executionMethod; + this.referenceObject = referenceObject; + if (execution == null) { + Logger.error("BasicScheduledJob instanciated with no execution method."); + } + } + + @Override + protected void doJob() { + if (execution == null) { + Logger.error( "BasicScheduledJob executed with nothing to execute."); + return; + } + + try { + execution.invoke(referenceObject); + } catch (IllegalArgumentException | IllegalAccessException e) { + Logger.error( "BasicScheduledJob execution failed. Method: " + execution.toString(), e); + } catch (InvocationTargetException e) { + Logger.error( "BasicScheduledJob execution failed. " + "Method: " + execution.toString(), e); + } + } +} diff --git a/src/engine/jobs/BonusCalcJob.java b/src/engine/jobs/BonusCalcJob.java new file mode 100644 index 00000000..2fdaf060 --- /dev/null +++ b/src/engine/jobs/BonusCalcJob.java @@ -0,0 +1,31 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.job.AbstractJob; +import engine.objects.AbstractCharacter; + +public class BonusCalcJob extends AbstractJob { + + private final AbstractCharacter ac; + + public BonusCalcJob(AbstractCharacter ac) { + super(); + this.ac = ac; + } + + @Override + protected void doJob() { + if (this.ac != null) { + this.ac.applyBonuses(); + + } + } +} diff --git a/src/engine/jobs/CSessionCleanupJob.java b/src/engine/jobs/CSessionCleanupJob.java new file mode 100644 index 00000000..df591aab --- /dev/null +++ b/src/engine/jobs/CSessionCleanupJob.java @@ -0,0 +1,28 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + package engine.jobs; + +import engine.gameManager.SessionManager; +import engine.job.AbstractJob; + +public class CSessionCleanupJob extends AbstractJob { + + private final String secKey; + + public CSessionCleanupJob(String key) { + super(); + this.secKey = key; + } + + @Override + protected void doJob() { + SessionManager.cSessionCleanup(secKey); + } +} diff --git a/src/engine/jobs/ChangeAltitudeJob.java b/src/engine/jobs/ChangeAltitudeJob.java new file mode 100644 index 00000000..72a1cd8c --- /dev/null +++ b/src/engine/jobs/ChangeAltitudeJob.java @@ -0,0 +1,42 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.gameManager.MovementManager; +import engine.job.AbstractScheduleJob; +import engine.objects.AbstractCharacter; + +public class ChangeAltitudeJob extends AbstractScheduleJob { + + private final AbstractCharacter ac; + private final float targetAlt; + private final float startAlt; + + public ChangeAltitudeJob(AbstractCharacter ac, float startAlt, float targetAlt) { + super(); + this.ac = ac; + this.startAlt = startAlt; + this.targetAlt = targetAlt; + } + + @Override + protected void doJob() { + if (this.ac != null) + MovementManager.finishChangeAltitude(ac, targetAlt); + } + + @Override + protected void _cancelJob() { + } + + public float getStartAlt() { + return startAlt; + } +} diff --git a/src/engine/jobs/ChantJob.java b/src/engine/jobs/ChantJob.java new file mode 100644 index 00000000..2c3de86e --- /dev/null +++ b/src/engine/jobs/ChantJob.java @@ -0,0 +1,103 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.Enum.GameObjectType; +import engine.Enum.ModType; +import engine.Enum.SourceType; +import engine.gameManager.PowersManager; +import engine.gameManager.SessionManager; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.PlayerBonuses; +import engine.objects.PlayerCharacter; +import engine.powers.ActionsBase; +import engine.powers.EffectsBase; +import engine.powers.PowersBase; + +import java.util.HashSet; + + +public class ChantJob extends AbstractEffectJob { + + private final AbstractEffectJob aej; + private int iteration = 0; + + public ChantJob(AbstractWorldObject source, AbstractWorldObject target, String stackType, int trains, ActionsBase action, PowersBase power, EffectsBase eb, AbstractEffectJob aej) { + super(source, target, stackType, trains, action, power, eb); + this.aej = aej; + } + + @Override + protected void doJob() { + if (this.aej == null || this.source == null || this.target == null || this.action == null || this.power == null || this.source == null || this.eb == null) + return; + PlayerBonuses bonuses = null; + + //if player isnt in game, do not run chant. + if (this.source.getObjectType().equals(GameObjectType.PlayerCharacter)){ + if (SessionManager.getPlayerCharacterByID(this.source.getObjectUUID()) == null) + return; + } + if (AbstractWorldObject.IsAbstractCharacter(source)) + bonuses = ((AbstractCharacter)source).getBonuses(); + if (!this.source.isAlive()) { + PowersManager.finishEffectTime(this.source, this.target, this.action, this.trains); + if (AbstractWorldObject.IsAbstractCharacter(source)) + ((AbstractCharacter)source).cancelLastChant(); + } else if (bonuses != null && bonuses.getBool(ModType.Silenced, SourceType.None)) { + PowersManager.finishEffectTime(this.source, this.target, this.action, this.trains); + if (AbstractWorldObject.IsAbstractCharacter(source)) + ((AbstractCharacter)source).cancelLastChant(); + } + else if (AbstractWorldObject.IsAbstractCharacter(source) && ((AbstractCharacter)source).isSit()){ + return; + }else if (this.iteration < this.power.getChantIterations() && AbstractWorldObject.IsAbstractCharacter(source)) { + this.skipSendEffect = true; + this.iteration++; + + // *** Refactor holy wtf batman + + String stackType = action.getStackType(); + stackType = (stackType.equals("IgnoreStack")) ? Integer.toString(action.getUUID()) : stackType; + + HashSet awolist = null; + if (this.source instanceof PlayerCharacter) + awolist = PowersManager.getAllTargets(this.source, this.source.getLoc(), (PlayerCharacter)this.source, this.power); + else + awolist = new HashSet<>(); + for (AbstractWorldObject awo : awolist) { + + if (awo == null) + continue; + + PowersManager.finishApplyPowerA((AbstractCharacter) this.source, awo, awo.getLoc(), this.power, this.trains, true); + + } + + if (AbstractWorldObject.IsAbstractCharacter(source)) + //handle invul + if(power.getUUID() != 334) + ((AbstractCharacter)this.source).setLastChant((int)(this.power.getChantDuration()) * 1000, this); + else + ((AbstractCharacter)this.source).setLastChant((int)(this.power.getChantDuration()) * 1000, this); + } else { + PowersManager.finishEffectTime(this.source, this.target, this.action, this.trains); + if (AbstractWorldObject.IsAbstractCharacter(source)) { + ((AbstractCharacter)source).cancelLastChant(); + } + } + } + + @Override + protected void _cancelJob() { + } + +} diff --git a/src/engine/jobs/CloseGateJob.java b/src/engine/jobs/CloseGateJob.java new file mode 100644 index 00000000..591a4d0a --- /dev/null +++ b/src/engine/jobs/CloseGateJob.java @@ -0,0 +1,44 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + package engine.jobs; + +import engine.Enum.RunegateType; +import engine.job.AbstractScheduleJob; +import engine.objects.Building; +import engine.objects.Runegate; +import org.pmw.tinylog.Logger; + +public class CloseGateJob extends AbstractScheduleJob { + + private final Building building; + private final RunegateType portalType; + + public CloseGateJob(Building building, RunegateType portalType) { + super(); + this.building = building; + this.portalType = portalType; + } + + @Override + protected void doJob() { + + if (building == null) { + Logger.error("Rungate building was null"); + return; + } + + Runegate.getRunegates()[RunegateType.getGateTypeFromUUID(building.getObjectUUID()).ordinal()].deactivatePortal(portalType); + } + + @Override + protected void _cancelJob() { + } +} + diff --git a/src/engine/jobs/DamageOverTimeJob.java b/src/engine/jobs/DamageOverTimeJob.java new file mode 100644 index 00000000..3470842a --- /dev/null +++ b/src/engine/jobs/DamageOverTimeJob.java @@ -0,0 +1,92 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.Enum.GameObjectType; +import engine.gameManager.PowersManager; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Building; +import engine.powers.ActionsBase; +import engine.powers.EffectsBase; +import engine.powers.PowersBase; +import engine.powers.poweractions.DamageOverTimePowerAction; + + +public class DamageOverTimeJob extends AbstractEffectJob { + + private final DamageOverTimePowerAction dot; + private int iteration = 0; + private int liveCounter = 0; + + public DamageOverTimeJob(AbstractWorldObject source, AbstractWorldObject target, String stackType, int trains, ActionsBase action, PowersBase power, EffectsBase eb, DamageOverTimePowerAction dot) { + super(source, target, stackType, trains, action, power, eb); + + this.dot = dot; + if (this.target != null && AbstractWorldObject.IsAbstractCharacter(target)) + this.liveCounter = ((AbstractCharacter)target).getLiveCounter(); + + this.iteration = action.getDurationInSeconds(trains) / this.dot.getNumIterations(); + } + + @Override + protected void doJob() { + if (this.target.getObjectType().equals(GameObjectType.Building) + && ((Building)this.target).isVulnerable() == false) { + _cancelJob(); + return; + } + + + if (this.dot == null || this.target == null || this.action == null || this.source == null || this.eb == null) + return; + if (AbstractWorldObject.IsAbstractCharacter(target) && ((AbstractCharacter)this.target).getLiveCounter() != liveCounter){ + PowersManager.finishEffectTime(this.source, this.target, this.action, this.trains); + return; + } + if (!this.target.isAlive()){ + PowersManager.finishEffectTime(this.source, this.target, this.action, this.trains); + return; + } + + this.iteration--; + + if (this.iteration < 0){ + PowersManager.finishEffectTime(this.source, this.target, this.action, this.trains); + return; + } + this.skipSendEffect = true; + String stackType = action.getStackType(); + if (stackType.equals("IgnoreStack")) + this.target.addEffect(Integer.toString(action.getUUID()), getTickLength(), this, this.eb, this.trains); + else + this.target.addEffect(stackType, getTickLength(), this, this.eb, this.trains); + if (AbstractWorldObject.IsAbstractCharacter(source)) + eb.startEffect((AbstractCharacter)this.source, this.target, this.trains, this); + } + + @Override + protected void _cancelJob() { + PowersManager.cancelEffectTime(this.source, this.target, this.power, this.eb, this.action, this.trains, this); + } + + public int getIteration() { + return this.iteration; + } + + public int getTickLength() { + return this.dot.getNumIterations() * 1000; + } + + public int inc() { + this.iteration++; + return this.iteration; + } +} diff --git a/src/engine/jobs/DatabaseUpdateJob.java b/src/engine/jobs/DatabaseUpdateJob.java new file mode 100644 index 00000000..18e3dd61 --- /dev/null +++ b/src/engine/jobs/DatabaseUpdateJob.java @@ -0,0 +1,65 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + package engine.jobs; + +import engine.Enum.GameObjectType; +import engine.gameManager.DbManager; +import engine.job.AbstractScheduleJob; +import engine.objects.AbstractGameObject; +import engine.objects.Building; +import engine.objects.PlayerCharacter; + +public class DatabaseUpdateJob extends AbstractScheduleJob { + + private final AbstractGameObject ago; + private final String type; + + public DatabaseUpdateJob(AbstractGameObject ago, String type) { + super(); + this.ago = ago; + this.type = type; + } + + @Override + protected void doJob() { + if (this.ago == null) + return; + ago.removeDatabaseJob(this.type, false); + + if (ago.getObjectType().equals(GameObjectType.PlayerCharacter)) { + + PlayerCharacter pc = (PlayerCharacter) ago; + + switch (this.type) { + case "Skills": + pc.updateSkillsAndPowersToDatabase(); + break; + case "Stats": + DbManager.PlayerCharacterQueries.UPDATE_CHARACTER_STATS(pc); + break; + case "EXP": + DbManager.PlayerCharacterQueries.UPDATE_CHARACTER_EXPERIENCE(pc); + break; + } + + } + else if (ago instanceof Building) { + Building b = (Building) ago; + if (this.type.equals("health")) + DbManager.BuildingQueries.UPDATE_BUILDING_HEALTH(b.getObjectUUID(), (int)(b.getHealth())); + } + + } + + @Override + protected void _cancelJob() { + } +} + diff --git a/src/engine/jobs/DebugTimerJob.java b/src/engine/jobs/DebugTimerJob.java new file mode 100644 index 00000000..17f7ec04 --- /dev/null +++ b/src/engine/jobs/DebugTimerJob.java @@ -0,0 +1,61 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.gameManager.ChatManager; +import engine.job.AbstractScheduleJob; +import engine.objects.PlayerCharacter; + +public class DebugTimerJob extends AbstractScheduleJob { + + private final PlayerCharacter pc; + private final String command; + private final int commandNum; + private final int duration; + + public DebugTimerJob(PlayerCharacter pc, String command, int commandNum, int duration) { + super(); + this.pc = pc; + this.command = command; + this.commandNum = commandNum; + this.duration = duration; + } + + @Override + protected void doJob() { + if (this.pc == null) { + return; + } + + String text; + switch (this.commandNum) { + case 1: //health + text = "Health: " + pc.getHealth(); + ChatManager.chatSystemInfo(pc, text); + break; + case 2: //mana + text = "Mana: " + pc.getMana(); + ChatManager.chatSystemInfo(pc, text); + break; + case 3: //stamina + text = "Stamina: " + pc.getStamina(); + ChatManager.chatSystemInfo(pc, text); + break; + default: + } + + //re-up the timer for this + this.pc.renewTimer(command, this, duration); + } + + @Override + protected void _cancelJob() { + } +} diff --git a/src/engine/jobs/DeferredPowerJob.java b/src/engine/jobs/DeferredPowerJob.java new file mode 100644 index 00000000..17ffefa6 --- /dev/null +++ b/src/engine/jobs/DeferredPowerJob.java @@ -0,0 +1,109 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.gameManager.CombatManager; +import engine.gameManager.PowersManager; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Mob; +import engine.objects.PlayerCharacter; +import engine.powers.ActionsBase; +import engine.powers.EffectsBase; +import engine.powers.PowersBase; +import engine.powers.poweractions.ApplyEffectPowerAction; +import engine.powers.poweractions.DeferredPowerPowerAction; + +public class DeferredPowerJob extends AbstractEffectJob { + + private final DeferredPowerPowerAction def; + + public DeferredPowerJob(AbstractWorldObject source, AbstractWorldObject target, String stackType, int trains, ActionsBase action, PowersBase power, EffectsBase eb, DeferredPowerPowerAction def) { + super(source, target, stackType, trains, action, power, eb); + this.def = def; + } + + public DeferredPowerJob(AbstractWorldObject source, AbstractWorldObject target, String stackType, int trains, ActionsBase action, PowersBase power, EffectsBase eb, ApplyEffectPowerAction def) { + super(source, target, stackType, trains, action, power, eb); + this.def = null; + } + + @Override + protected void doJob() { + //Power ended with no attack, cancel weapon power boost + if (this.source != null && this.source instanceof PlayerCharacter) { + ((PlayerCharacter) this.source).setWeaponPower(null); + } + PowersManager.finishEffectTime(this.source, this.target, this.action, this.trains); + } + + @Override + protected void _cancelJob() { + //Attack happened. + PowersManager.cancelEffectTime(this.source, this.target, this.power, this.eb, this.action, this.trains, this); + } + + public void attack(AbstractWorldObject tar, float attackRange) { + + if (this.source == null) + return; + + if (!AbstractWorldObject.IsAbstractCharacter(tar)) + return; + + if (this.power == null) + return; + + + switch(this.source.getObjectType()){ + + case PlayerCharacter: + + if (def == null) { + //No powers applied, just reset weapon power. + ((PlayerCharacter) this.source).setWeaponPower(null); + return; + } + float powerRange = this.power.getWeaponRange(); + + // Wtf? Method returns TRUE if rage test fails? Seriously? + + //DO valid range check ONLY for weapon powers with range less than attack range. + if (attackRange > powerRange) + if (CombatManager.NotInRange((AbstractCharacter)this.source, tar, powerRange)) + return; + + //Range check passed, apply power and clear weapon power. + ((PlayerCharacter) this.source).setWeaponPower(null); + + + //weapon powers with no deferedpoweraction have null Def, but still have bonuses applied already and will finish here. + + + + + PowersManager.applyPower((AbstractCharacter) this.source, tar, Vector3fImmutable.ZERO, def.getDeferredPowerID(), this.trains, false); + PowersManager.finishEffectTime(this.source, this.target, this.action, this.trains); + break; + case Mob: + ((Mob) this.source).setWeaponPower(null); + if (def == null) { + return; + } + + PowersManager.applyPower((AbstractCharacter) this.source, tar, Vector3fImmutable.ZERO, def.getDeferredPowerID(), this.trains, false); + PowersManager.finishEffectTime(this.source, this.target, this.action, this.trains); + break; + + } + + } +} diff --git a/src/engine/jobs/DisconnectJob.java b/src/engine/jobs/DisconnectJob.java new file mode 100644 index 00000000..f78448cc --- /dev/null +++ b/src/engine/jobs/DisconnectJob.java @@ -0,0 +1,34 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.job.AbstractScheduleJob; +import engine.net.client.ClientConnection; + +public class DisconnectJob extends AbstractScheduleJob { + + private final ClientConnection origin; + + public DisconnectJob(ClientConnection origin) { + super(); + this.origin = origin; + } + + @Override + protected void doJob() { + if (this.origin != null) { + this.origin.disconnect(); + } + } + + @Override + protected void _cancelJob() { + } +} diff --git a/src/engine/jobs/DoorCloseJob.java b/src/engine/jobs/DoorCloseJob.java new file mode 100644 index 00000000..1614fa96 --- /dev/null +++ b/src/engine/jobs/DoorCloseJob.java @@ -0,0 +1,63 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.jobs; + +import engine.Enum.DoorState; +import engine.job.AbstractScheduleJob; +import engine.net.DispatchMessage; +import engine.net.client.msg.DoorTryOpenMsg; +import engine.objects.Blueprint; +import engine.objects.Building; + +public class DoorCloseJob extends AbstractScheduleJob { + + Building building; + int door; + + public DoorCloseJob(Building building, int door) { + super(); + this.building = building; + this.door = door; + } + + @Override + protected void doJob() { + + int doorNumber; + + if (this.building == null) + return; + + doorNumber = Blueprint.getDoorNumberbyMesh(this.door); + + this.building.setDoorState(doorNumber, DoorState.CLOSED); + + DoorTryOpenMsg msg = new DoorTryOpenMsg(door, this.building.getObjectUUID(), 0, (byte) 0); + DispatchMessage.sendToAllInRange(building, msg); + + } + + @Override + protected void _cancelJob() { + } +} diff --git a/src/engine/jobs/EndFearJob.java b/src/engine/jobs/EndFearJob.java new file mode 100644 index 00000000..981ebd31 --- /dev/null +++ b/src/engine/jobs/EndFearJob.java @@ -0,0 +1,45 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.objects.AbstractWorldObject; +import engine.objects.Mob; +import engine.powers.ActionsBase; +import engine.powers.EffectsBase; +import engine.powers.PowersBase; + +public class EndFearJob extends AbstractEffectJob { + + public EndFearJob(AbstractWorldObject source, AbstractWorldObject target, String stackType, int trains, ActionsBase action, PowersBase power, EffectsBase eb) { + super(source, target, stackType, trains, action, power, eb); + } + + @Override + protected void doJob() { + + //cancel fear for mob. + + if (this.target == null || (!(this.target instanceof Mob))) + return; + + ((Mob) this.target).setFearedObject(null); + } + + @Override + protected void _cancelJob() { + + //cancel fear for mob. + + if (this.target == null || (!(this.target instanceof Mob))) + return; + + ((Mob) this.target).setFearedObject(null); + } +} diff --git a/src/engine/jobs/FinishCooldownTimeJob.java b/src/engine/jobs/FinishCooldownTimeJob.java new file mode 100644 index 00000000..6759cf87 --- /dev/null +++ b/src/engine/jobs/FinishCooldownTimeJob.java @@ -0,0 +1,32 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.gameManager.PowersManager; +import engine.job.AbstractJob; +import engine.net.client.msg.PerformActionMsg; +import engine.objects.PlayerCharacter; + +public class FinishCooldownTimeJob extends AbstractJob { + + PlayerCharacter pc; + PerformActionMsg msg; + + public FinishCooldownTimeJob(PlayerCharacter pc, PerformActionMsg msg) { + super(); + this.pc = pc; + this.msg = msg; + } + + @Override + protected void doJob() { + PowersManager.finishCooldownTime(this.msg, this.pc); + } +} diff --git a/src/engine/jobs/FinishEffectTimeJob.java b/src/engine/jobs/FinishEffectTimeJob.java new file mode 100644 index 00000000..60786f68 --- /dev/null +++ b/src/engine/jobs/FinishEffectTimeJob.java @@ -0,0 +1,33 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.gameManager.PowersManager; +import engine.objects.AbstractWorldObject; +import engine.powers.ActionsBase; +import engine.powers.EffectsBase; +import engine.powers.PowersBase; + +public class FinishEffectTimeJob extends AbstractEffectJob { + + public FinishEffectTimeJob(AbstractWorldObject source, AbstractWorldObject target, String stackType, int trains, ActionsBase action, PowersBase power, EffectsBase eb) { + super(source, target, stackType, trains, action, power, eb); + } + + @Override + protected void doJob() { + PowersManager.finishEffectTime(this.source, this.target, this.action, this.trains); + } + + @Override + protected void _cancelJob() { + PowersManager.cancelEffectTime(this.source, this.target, this.power, this.eb, this.action, this.trains, this); + } +} diff --git a/src/engine/jobs/FinishRecycleTimeJob.java b/src/engine/jobs/FinishRecycleTimeJob.java new file mode 100644 index 00000000..47c4126a --- /dev/null +++ b/src/engine/jobs/FinishRecycleTimeJob.java @@ -0,0 +1,36 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.gameManager.PowersManager; +import engine.job.AbstractScheduleJob; +import engine.net.client.msg.PerformActionMsg; +import engine.objects.PlayerCharacter; + +public class FinishRecycleTimeJob extends AbstractScheduleJob { + + PlayerCharacter pc; + PerformActionMsg msg; + + public FinishRecycleTimeJob(PlayerCharacter pc, PerformActionMsg msg) { + super(); + this.pc = pc; + this.msg = msg; + } + + @Override + protected void doJob() { + PowersManager.finishRecycleTime(this.msg, this.pc, false); + } + + @Override + protected void _cancelJob() { + } +} diff --git a/src/engine/jobs/FinishSpireEffectJob.java b/src/engine/jobs/FinishSpireEffectJob.java new file mode 100644 index 00000000..09e6b147 --- /dev/null +++ b/src/engine/jobs/FinishSpireEffectJob.java @@ -0,0 +1,37 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.objects.AbstractWorldObject; +import engine.objects.PlayerCharacter; +import engine.powers.EffectsBase; + +public class FinishSpireEffectJob extends AbstractEffectJob { + + public FinishSpireEffectJob(AbstractWorldObject target, String stackType, EffectsBase eb, int trains) { + super(null, target, stackType, trains, null, null, eb); + } + + @Override + protected void doJob() { + + PlayerCharacter pc = (PlayerCharacter) target; + + if (pc == null) + return; + + pc.endEffectNoPower(Integer.toString(eb.getUUID())); + + } + + @Override + protected void _cancelJob() { + } +} diff --git a/src/engine/jobs/FinishSummonsJob.java b/src/engine/jobs/FinishSummonsJob.java new file mode 100644 index 00000000..afe4f6d2 --- /dev/null +++ b/src/engine/jobs/FinishSummonsJob.java @@ -0,0 +1,81 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.Enum.ModType; +import engine.Enum.SourceType; +import engine.gameManager.PowersManager; +import engine.job.AbstractScheduleJob; +import engine.job.JobContainer; +import engine.net.client.msg.ErrorPopupMsg; +import engine.objects.PlayerCharacter; + +import java.util.concurrent.ConcurrentHashMap; + +public class FinishSummonsJob extends AbstractScheduleJob { + + PlayerCharacter source; + PlayerCharacter target; + + public FinishSummonsJob(PlayerCharacter source, PlayerCharacter target) { + super(); + this.source = source; + this.target = target; + } + + @Override + protected void doJob() { + + if (this.target == null) + return; + + //clear summon timer + + ConcurrentHashMap timers = this.target.getTimers(); + + if (timers != null && timers.containsKey("Summon")) + timers.remove("Summon"); + + if (this.source == null || !this.source.isAlive() || !this.target.isAlive()) + return; + + // cannot summon a player in combat + if (this.target.isCombat()) { + + ErrorPopupMsg.sendErrorMsg(this.source, "Cannot summon player in combat."); + + PowersManager.finishRecycleTime(428523680, this.source, false); + return; + } + + if (this.target.getBonuses() != null && this.target.getBonuses().getBool(ModType.BlockedPowerType, SourceType.SUMMON)){ + ErrorPopupMsg.sendErrorMsg(this.target, "You have been blocked from receiving summons!"); + ErrorPopupMsg.sendErrorMsg(this.source, "Target is blocked from receiving summons!"); + return; + } + + if (this.source.getRegion() != null) + this.target.setRegion(this.source.getRegion()); + //teleport target to source + target.teleport(source.getLoc()); + } + + @Override + protected void _cancelJob() { + } + + public PlayerCharacter getSource() { + return this.source; + } + + public PlayerCharacter getTarget() { + return this.target; + } +} diff --git a/src/engine/jobs/FlightJob.java b/src/engine/jobs/FlightJob.java new file mode 100644 index 00000000..1bd9366c --- /dev/null +++ b/src/engine/jobs/FlightJob.java @@ -0,0 +1,40 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.gameManager.MovementManager; +import engine.job.AbstractScheduleJob; +import engine.net.client.msg.ChangeAltitudeMsg; +import engine.objects.PlayerCharacter; + +public class FlightJob extends AbstractScheduleJob { + + private final PlayerCharacter pc; + private final ChangeAltitudeMsg msg; + private final int duration; + + public FlightJob(PlayerCharacter pc, ChangeAltitudeMsg msg, int duration) { + super(); + this.msg = msg; + this.duration = duration; + this.pc = pc; + } + + @Override + protected void doJob() { + if (this.pc != null && this.msg != null) + MovementManager.updateFlight(pc, msg, duration); + } + + @Override + protected void _cancelJob() { + } + +} diff --git a/src/engine/jobs/LoadEffectsJob.java b/src/engine/jobs/LoadEffectsJob.java new file mode 100644 index 00000000..f21ad973 --- /dev/null +++ b/src/engine/jobs/LoadEffectsJob.java @@ -0,0 +1,48 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.job.AbstractJob; +import engine.net.client.ClientConnection; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; + +import java.util.ArrayList; + +public class LoadEffectsJob extends AbstractJob { + + ArrayList acsToLoad; + ClientConnection originToSend; + + public LoadEffectsJob(ArrayList acsToLoad, ClientConnection origin) { + this.acsToLoad = acsToLoad; + this.originToSend = origin; + + } + + @Override + protected void doJob() { + if (this.originToSend == null) { + return; + } + + for (AbstractWorldObject awo : this.acsToLoad) { + + if (AbstractWorldObject.IsAbstractCharacter(awo)) { + AbstractCharacter acToLoad = (AbstractCharacter) awo; + acToLoad.sendAllEffects(this.originToSend); + + } + + } + + } + +} diff --git a/src/engine/jobs/LogoutCharacterJob.java b/src/engine/jobs/LogoutCharacterJob.java new file mode 100644 index 00000000..cfdf60d2 --- /dev/null +++ b/src/engine/jobs/LogoutCharacterJob.java @@ -0,0 +1,37 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.job.AbstractScheduleJob; +import engine.objects.PlayerCharacter; +import engine.server.world.WorldServer; + +public class LogoutCharacterJob extends AbstractScheduleJob { + + private final PlayerCharacter pc; + private final WorldServer server; + + public LogoutCharacterJob(PlayerCharacter pc, WorldServer server) { + super(); + this.pc = pc; + this.server = server; + } + + @Override + protected void doJob() { + server.logoutCharacter(this.pc); + } + + @Override + protected void _cancelJob() { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/jobs/MineActiveJob.java b/src/engine/jobs/MineActiveJob.java new file mode 100644 index 00000000..b6ce1453 --- /dev/null +++ b/src/engine/jobs/MineActiveJob.java @@ -0,0 +1,71 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.job.AbstractJob; +import engine.objects.Mine; +import org.pmw.tinylog.Logger; + +import java.time.LocalDateTime; +import java.util.ArrayList; + +public class MineActiveJob extends AbstractJob { + + public MineActiveJob() { + super(); + } + + @Override + protected void doJob() { + ArrayList mines = Mine.getMines(); + LocalDateTime now = LocalDateTime.now(); + + for (Mine mine : mines) { + try { + + if (mine.getOwningGuild() == null){ + mine.handleStartMineWindow(); + Mine.setLastChange(System.currentTimeMillis()); + continue; + } + + //handle claimed mines + LocalDateTime mineWindow = mine.openDate.withMinute(0).withSecond(0).withNano(0); + if (mineWindow != null && now.plusMinutes(1).isAfter(mineWindow)) + if (!mine.getIsActive()) { + Logger.info("activating mine. " + mineWindow.getHour() + " , " + now.getHour()); + mine.handleStartMineWindow(); + Mine.setLastChange(System.currentTimeMillis()); + + }else{ + if (mine.handleEndMineWindow()){ + Logger.info("Deactivating mine. " + mineWindow.getHour() + " , " + now.getHour()); + Mine.setLastChange(System.currentTimeMillis()); + } + + } + }catch (Exception e) { + Logger.error( "mineID: " + mine.getObjectUUID() + e); + } + } + } +} diff --git a/src/engine/jobs/NoTimeJob.java b/src/engine/jobs/NoTimeJob.java new file mode 100644 index 00000000..309bec03 --- /dev/null +++ b/src/engine/jobs/NoTimeJob.java @@ -0,0 +1,28 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.objects.AbstractWorldObject; +import engine.powers.EffectsBase; + +public class NoTimeJob extends AbstractEffectJob { + + public NoTimeJob(AbstractWorldObject target, String stackType, EffectsBase eb, int trains) { + super(null, target, stackType, trains, null, null, eb); + } + + @Override + protected void doJob() { + } + + @Override + protected void _cancelJob() { + } +} diff --git a/src/engine/jobs/PersistentAoeJob.java b/src/engine/jobs/PersistentAoeJob.java new file mode 100644 index 00000000..33e7827c --- /dev/null +++ b/src/engine/jobs/PersistentAoeJob.java @@ -0,0 +1,110 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.Enum.GameObjectType; +import engine.gameManager.PowersManager; +import engine.math.Vector3fImmutable; +import engine.net.client.msg.PerformActionMsg; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.PlayerCharacter; +import engine.powers.ActionsBase; +import engine.powers.EffectsBase; +import engine.powers.PowersBase; + +import java.util.HashSet; + +public class PersistentAoeJob extends AbstractEffectJob { + + private final AbstractEffectJob aej; + private int iteration = 0; + private Vector3fImmutable targetLoc; + private Vector3fImmutable lastLoc; + + public PersistentAoeJob(AbstractWorldObject source, AbstractWorldObject target, String stackType, int trains, ActionsBase action, PowersBase power, EffectsBase eb, AbstractEffectJob aej, Vector3fImmutable targetLoc) { + super(source, target, stackType, trains, action, power, eb); + this.aej = aej; + if (target != null && this.target.getObjectType() == GameObjectType.PlayerCharacter) + this.targetLoc = this.target.getLoc(); + else + this.targetLoc = targetLoc; + this.lastLoc = targetLoc; + } + + @Override + protected void doJob() { + + if (this.aej == null || this.source == null || this.action == null || this.power == null || this.source == null || this.eb == null) + return; + + if (!this.source.isAlive()) + PowersManager.finishEffectTime(this.source, this.target, this.action, this.trains); + else if (this.iteration < this.power.getChantIterations()) { + + + this.skipSendEffect = true; + this.iteration++; + + + if (this.target != null){ + this.lastLoc = this.target.getLoc(); + this.targetLoc = this.target.getLoc(); + } + + String stackType = action.getStackType(); + stackType = (stackType.equals("IgnoreStack")) ? Integer.toString(action.getUUID()) : stackType; + HashSet awolist = null; + + + if (this.source instanceof PlayerCharacter) + awolist = PowersManager.getAllTargets(null, this.targetLoc, (PlayerCharacter) this.source, this.power); + else + awolist = new HashSet<>(); + PerformActionMsg msg = new PerformActionMsg(power.getToken(), 9999, source + .getObjectType().ordinal(), source.getObjectUUID(), source.getObjectType().ordinal(), + source.getObjectUUID(), 0, 0, 0, 2, 0); + + + for (AbstractWorldObject awo : awolist) { + + //judge the defense of the target + + + + if (awo == null + || PowersManager.testAttack((PlayerCharacter) this.source, awo, this.power, msg)) + continue; + + PowersManager.finishApplyPowerA((AbstractCharacter) this.source, awo, this.targetLoc, this.power, this.trains, true); + if (this.target != null && !this.target.isAlive()){ + this.target = null; + } + + } + if (AbstractWorldObject.IsAbstractCharacter(source)) + ((AbstractCharacter) this.source).addPersistantAoe(stackType, (int) (this.power.getChantDuration() * 1000), this, this.eb, this.trains); + } else + PowersManager.finishEffectTime(this.source, this.target, this.action, this.trains); + } + + @Override + protected void _cancelJob() { + } + + public int getIteration() { + return this.iteration; + } + + public int inc() { + this.iteration++; + return this.iteration; + } +} diff --git a/src/engine/jobs/RefreshGroupJob.java b/src/engine/jobs/RefreshGroupJob.java new file mode 100644 index 00000000..9d27abff --- /dev/null +++ b/src/engine/jobs/RefreshGroupJob.java @@ -0,0 +1,82 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.gameManager.GroupManager; +import engine.job.AbstractJob; +import engine.net.client.ClientConnection; +import engine.objects.Group; +import engine.objects.PlayerCharacter; + +public class RefreshGroupJob extends AbstractJob { + + private final PlayerCharacter pc; + private ClientConnection origin; + private Group grp; + private PlayerCharacter pcToRefresh; + private final boolean wholeGroup; + + public RefreshGroupJob(PlayerCharacter pc, PlayerCharacter pcToRefresh) { + super(); + this.pc = pc; + if (pc != null) { + this.origin = pc.getClientConnection(); + this.grp = GroupManager.getGroup(pc); + } + this.pcToRefresh = pcToRefresh; + this.wholeGroup = false; + } + + public RefreshGroupJob(PlayerCharacter pc) { + super(); + this.pc = pc; + if (pc != null) { + this.origin = pc.getClientConnection(); + this.grp = GroupManager.getGroup(pc); + } + this.wholeGroup = true; + } + + @Override + protected void doJob() { + + if (this.pc == null || this.origin == null || grp == null) { + return; + } + + if (wholeGroup) { + + // refresh everyone in the group including me + // check that we are in the same group as when we submitted the job + + if (GroupManager.getGroup(pc) != null && GroupManager.getGroup(pc) == grp) { + + // refresh pc's group list for just the one player that needed refreshing + + GroupManager.RefreshMyGroupList(pc, origin); + GroupManager.RefreshOthersGroupList(pc); + } + + return; + } + + // only refresh the single player + if (this.pcToRefresh == null) + return; + + // check that we are in the same group as when we submitted the job + if (GroupManager.getGroup(pc) != null && GroupManager.getGroup(pc) == grp) { + // refresh pc's group list for just the one player that needed refreshing + GroupManager.RefreshMyGroupListSinglePlayer(pc, origin, pcToRefresh); + } + + } + +} diff --git a/src/engine/jobs/RemoveCorpseJob.java b/src/engine/jobs/RemoveCorpseJob.java new file mode 100644 index 00000000..ff4d1fbf --- /dev/null +++ b/src/engine/jobs/RemoveCorpseJob.java @@ -0,0 +1,35 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.job.AbstractScheduleJob; +import engine.objects.Corpse; + +public class RemoveCorpseJob extends AbstractScheduleJob { + + private final Corpse corpse; + + public RemoveCorpseJob(Corpse corpse) { + super(); + this.corpse = corpse; + } + + @Override + protected void doJob() { + + if (this.corpse != null) + Corpse.removeCorpse(corpse, true); + + } + + @Override + protected void _cancelJob() { + } +} diff --git a/src/engine/jobs/SiegeSpireWithdrawlJob.java b/src/engine/jobs/SiegeSpireWithdrawlJob.java new file mode 100644 index 00000000..cd2bbf79 --- /dev/null +++ b/src/engine/jobs/SiegeSpireWithdrawlJob.java @@ -0,0 +1,78 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.jobs; + +import engine.job.AbstractScheduleJob; +import engine.job.JobContainer; +import engine.job.JobScheduler; +import engine.objects.Building; +import engine.objects.City; +import org.pmw.tinylog.Logger; + +public class SiegeSpireWithdrawlJob extends AbstractScheduleJob { + + private Building spire = null; + + public SiegeSpireWithdrawlJob(Building spire) { + super(); + this.spire = spire; + } + + @Override + protected void doJob() { + + if (spire == null) + return; + + // Early exit if someone disabled the spire + + if (!spire.isSpireIsActive()) + return; + + City buildingCity = spire.getCity(); + + if (buildingCity == null) + return; + + + + buildingCity.transactionLock.writeLock().lock(); + try{ + + // If the spire runs out of money, disable it. + //*** Refactor: 5000 every 30 seconds? Wtf? + + if (!spire.hasFunds(5000)){ + spire.disableSpire(true); + return; + } + if (spire.getStrongboxValue() < 5000) { + spire.disableSpire(true); + return; + } + + // Deduct the activation cost from the strongbox and resubmit the job + + if (!spire.transferGold(-5000,false)) + return; + JobContainer jc = JobScheduler.getInstance().scheduleJob(new SiegeSpireWithdrawlJob(spire), 300000); + spire.getTimers().put("SpireWithdrawl", jc); + + }catch(Exception e){ + Logger.error(e); + }finally{ + buildingCity.transactionLock.writeLock().unlock(); + } + + } + + @Override + protected void _cancelJob() { + } +} diff --git a/src/engine/jobs/StuckJob.java b/src/engine/jobs/StuckJob.java new file mode 100644 index 00000000..c2a2d3ad --- /dev/null +++ b/src/engine/jobs/StuckJob.java @@ -0,0 +1,119 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + package engine.jobs; + +import engine.InterestManagement.WorldGrid; +import engine.job.AbstractScheduleJob; +import engine.math.Bounds; +import engine.math.Vector3fImmutable; +import engine.net.client.msg.ErrorPopupMsg; +import engine.objects.AbstractWorldObject; +import engine.objects.Building; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; + +import java.util.HashSet; + +public class StuckJob extends AbstractScheduleJob { + + private final PlayerCharacter player; + + public StuckJob(PlayerCharacter player) { + super(); + this.player = player; + } + + @Override + protected void doJob() { + + Vector3fImmutable stuckLoc; + Building stuckBuilding = null; + + if (player == null) + return; + + if (player.getClientConnection() == null) + return; + + HashSetawoList = WorldGrid.getObjectsInRangePartial(player, 150, MBServerStatics.MASK_BUILDING); + + for (AbstractWorldObject awo:awoList){ + + Building toStuckOutOf = (Building)awo; + + if (toStuckOutOf.getStuckLocation() == null) + continue; + + if (Bounds.collide(player.getLoc(), toStuckOutOf)){ + stuckBuilding = toStuckOutOf; + break; + + } + } + + //Could not find closest building get stuck location of nearest building. + + if (stuckBuilding == null){ + ErrorPopupMsg.sendErrorMsg(player, "Unable to find desired location"); + return; + } else + stuckLoc = stuckBuilding.getStuckLocation(); + + if (stuckLoc == null){ + ErrorPopupMsg.sendErrorMsg(player, "Unable to find desired location"); + return; + } + + + player.teleport(stuckLoc); + + + + + // Needs to be re-written with stuck locations + // Disabled for now. + + + /* + + // Cannot have a null zone or player + + if (this.player == null) + return; + + if (ZoneManager.findSmallestZone(player.getLoc()) == null) + return; + + // If player is on a citygrid make sure the stuck direction + // is facing away from the tree + + if ((ZoneManager.findSmallestZone(player.getLoc()).isNPCCity()) || + (ZoneManager.findSmallestZone(player.getLoc()).isPlayerCity())) { + + zoneVector = player.getLoc().subtract(ZoneManager.findSmallestZone(player.getLoc()).getLoc()); + zoneVector = zoneVector.normalize(); + + if (zoneVector.dot(player.getFaceDir()) > 0) + return; + + } + + player.teleport(player.getLoc().add(player.getFaceDir().mult(34))); +*/ + } + + @Override + protected void _cancelJob() { + } + + private void sendTrackArrow(float rotation) { + } + +} + diff --git a/src/engine/jobs/SummonSendJob.java b/src/engine/jobs/SummonSendJob.java new file mode 100644 index 00000000..ff237410 --- /dev/null +++ b/src/engine/jobs/SummonSendJob.java @@ -0,0 +1,54 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.job.AbstractScheduleJob; +import engine.job.JobContainer; +import engine.objects.PlayerCharacter; + +import java.util.concurrent.ConcurrentHashMap; + +public class SummonSendJob extends AbstractScheduleJob { + + PlayerCharacter source; + PlayerCharacter target; + + public SummonSendJob(PlayerCharacter source, PlayerCharacter target) { + super(); + this.source = source; + this.target = target; + } + + @Override + protected void doJob() { + + if (this.source == null) + return; + + //clear summon send timer + ConcurrentHashMap timers = this.source.getTimers(); + + if (timers != null && timers.containsKey("SummonSend")) + timers.remove("SummonSend"); + + } + + @Override + protected void _cancelJob() { + } + + public PlayerCharacter getSource() { + return this.source; + } + + public PlayerCharacter getTarget() { + return this.target; + } +} diff --git a/src/engine/jobs/TeleportJob.java b/src/engine/jobs/TeleportJob.java new file mode 100644 index 00000000..7f1d43aa --- /dev/null +++ b/src/engine/jobs/TeleportJob.java @@ -0,0 +1,65 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.job.AbstractScheduleJob; +import engine.math.Vector3fImmutable; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ErrorPopupMsg; +import engine.objects.NPC; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; + +public class TeleportJob extends AbstractScheduleJob { + + private final ClientConnection origin; + private final NPC npc; + private final PlayerCharacter pc; + private final Vector3fImmutable loc; + private int oldLiveCounter; + private final boolean setSafeMode; + + public TeleportJob(PlayerCharacter pc, NPC npc, Vector3fImmutable loc, ClientConnection origin, boolean setSafeMode) { + super(); + this.pc = pc; + this.npc = npc; + this.loc = loc; + this.origin = origin; + this.setSafeMode = setSafeMode; + if (pc != null) { + this.oldLiveCounter = pc.getLiveCounter(); + } + } + + @Override + protected void doJob() { + + if (this.pc == null || this.npc == null || this.origin == null) + return; + + if (!pc.isAlive() || this.oldLiveCounter != pc.getLiveCounter()) + return; + + if (pc.getLoc().distanceSquared2D(npc.getLoc()) > MBServerStatics.NPC_TALK_RANGE * MBServerStatics.NPC_TALK_RANGE) { + ErrorPopupMsg.sendErrorPopup(pc, 114); + return; + } + + pc.teleport(loc); + + if (this.setSafeMode) + pc.setSafeMode(); + + } + + @Override + protected void _cancelJob() { + } +} diff --git a/src/engine/jobs/TrackJob.java b/src/engine/jobs/TrackJob.java new file mode 100644 index 00000000..f46921b2 --- /dev/null +++ b/src/engine/jobs/TrackJob.java @@ -0,0 +1,92 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.Enum; +import engine.gameManager.PowersManager; +import engine.math.Vector3fImmutable; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.msg.TrackArrowMsg; +import engine.objects.AbstractWorldObject; +import engine.objects.PlayerCharacter; +import engine.powers.ActionsBase; +import engine.powers.EffectsBase; +import engine.powers.PowersBase; +import engine.powers.poweractions.TrackPowerAction; +import engine.server.MBServerStatics; + +import static engine.math.FastMath.sqr; + +public class TrackJob extends AbstractEffectJob { + + private final TrackPowerAction tpa; + + public TrackJob(AbstractWorldObject source, AbstractWorldObject target, String stackType, int trains, ActionsBase action, PowersBase power, EffectsBase eb, TrackPowerAction tpa) { + super(source, target, stackType, trains, action, power, eb); + this.tpa = tpa; + } + + @Override + protected void doJob() { + + if (this.tpa == null || this.target == null || this.action == null || this.source == null || this.eb == null || !(this.source instanceof PlayerCharacter)) + return; + + if (this.target.isAlive() == false) { + sendTrackArrow(Float.intBitsToFloat(0x7E967699)); + PowersManager.finishEffectTime(this.source, this.target, this.action, this.trains); + return; + } + + String stackType = action.getStackType(); + + float distanceSquared = this.target.getLoc().distanceSquared2D(this.source.getLoc()); + + int speed; + + if (distanceSquared < sqr(MBServerStatics.TRACK_ARROW_FAST_RANGE)) + speed = MBServerStatics.TRACK_ARROW_SENSITIVITY_FAST; + else + speed = MBServerStatics.TRACK_ARROW_SENSITIVITY; + + this.source.addEffect(stackType, speed, this, this.eb, this.trains); + + Vector3fImmutable dir = this.target.getLoc().subtract2D(this.source.getLoc()); + dir = dir.normalize(); + + sendTrackArrow(dir.getRotation()); + + } + + @Override + protected void _cancelJob() { + sendTrackArrow(Float.intBitsToFloat(0x7E967699)); + PowersManager.cancelEffectTime(this.source, this.target, this.power, this.eb, this.action, this.trains, this); + } + + private void sendTrackArrow(float rotation) { + + if (this.source != null && this.source instanceof PlayerCharacter) { + PlayerCharacter pc = (PlayerCharacter) this.source; + + if (pc == null) + return; + + // We send track arrows over primary channel + + TrackArrowMsg tam = new TrackArrowMsg(rotation); + Dispatch dispatch = Dispatch.borrow(pc, tam); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); + + } +} + +} diff --git a/src/engine/jobs/TransferStatOTJob.java b/src/engine/jobs/TransferStatOTJob.java new file mode 100644 index 00000000..62d54a6f --- /dev/null +++ b/src/engine/jobs/TransferStatOTJob.java @@ -0,0 +1,74 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.gameManager.PowersManager; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.powers.ActionsBase; +import engine.powers.EffectsBase; +import engine.powers.PowersBase; +import engine.powers.poweractions.TransferStatOTPowerAction; + +public class TransferStatOTJob extends AbstractEffectJob { + + private final TransferStatOTPowerAction dot; + private int iteration = 0; + + public TransferStatOTJob(AbstractWorldObject source, AbstractWorldObject target, String stackType, int trains, ActionsBase action, PowersBase power, EffectsBase eb, TransferStatOTPowerAction dot) { + super(source, target, stackType, trains, action, power, eb); + this.dot = dot; + this.iteration = action.getDurationInSeconds(trains) / this.dot.getNumIterations(); + } + + @Override + protected void doJob() { + if (this.dot == null || this.target == null || this.action == null || this.source == null || this.eb == null || this.action == null || this.power == null) + return; + if (!this.target.isAlive()){ + PowersManager.finishEffectTime(this.source, this.target, this.action, this.trains); + return; + } + + iteration--; + + if (iteration < 0){ + PowersManager.finishEffectTime(this.source, this.target, this.action, this.trains); + return; + } + + this.skipSendEffect = true; + + String stackType = action.getStackType(); + stackType = (stackType.equals("IgnoreStack")) ? Integer.toString(action.getUUID()) : stackType; + this.target.addEffect(stackType, getTickLength(), this, this.eb, this.trains); + if (AbstractWorldObject.IsAbstractCharacter(source)) + this.dot.runAction((AbstractCharacter)this.source, this.target, this.trains, this.action, this.power); + + } + + @Override + protected void _cancelJob() { + PowersManager.cancelEffectTime(this.source, this.target, this.power, this.eb, this.action, this.trains, this); + } + + public int getIteration() { + return this.iteration; + } + + public int getTickLength() { + return this.dot.getNumIterations() * 1000; + } + + public int inc() { + this.iteration++; + return this.iteration; + } +} diff --git a/src/engine/jobs/UpdateGroupJob.java b/src/engine/jobs/UpdateGroupJob.java new file mode 100644 index 00000000..db391898 --- /dev/null +++ b/src/engine/jobs/UpdateGroupJob.java @@ -0,0 +1,51 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.jobs; + +import engine.gameManager.GroupManager; +import engine.job.AbstractScheduleJob; +import engine.job.JobScheduler; +import engine.objects.Group; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +public class UpdateGroupJob extends AbstractScheduleJob { + + private final Group group; + + public UpdateGroupJob(Group group) { + super(); + this.group = group; + } + + @Override + protected void doJob() { + + if (this.group == null) + return; + + PlayerCharacter lead = group.getGroupLead(); + + if (lead == null) + return; + + try { + GroupManager.RefreshWholeGroupList(lead, lead.getClientConnection(), this.group); + } catch (Exception e) { + Logger.error( e); + } + + JobScheduler.getInstance().scheduleJob(this, MBServerStatics.UPDATE_GROUP_RATE); + } + + @Override + protected void _cancelJob() { + } +} diff --git a/src/engine/jobs/UpgradeBuildingJob.java b/src/engine/jobs/UpgradeBuildingJob.java new file mode 100644 index 00000000..510308e6 --- /dev/null +++ b/src/engine/jobs/UpgradeBuildingJob.java @@ -0,0 +1,55 @@ +package engine.jobs; + +import engine.job.AbstractScheduleJob; +import engine.objects.Building; +import org.pmw.tinylog.Logger; + +/* + * This class handles upgrading of buildings, swapping the + * appropriate mesh according to the building's blueprint. + * @Author + */ +public class UpgradeBuildingJob extends AbstractScheduleJob { + + private final Building rankingBuilding; + + public UpgradeBuildingJob(Building building) { + super(); + this.rankingBuilding = building; + + } + + @Override + protected void doJob() { + + + + // Must have a building to rank! + + if (rankingBuilding == null) { + Logger.error("Attempting to rank null building"); + return; + } + + // Make sure the building is currently set to upgrade + // (Duplicate job sanity check) + + if (rankingBuilding.isRanking() == false) + return; + + // SetCurrentRank also changes the mesh and maxhp + // accordingly for buildings with blueprints + + rankingBuilding.setRank(rankingBuilding.getRank() + 1); + + // Reload the object + + + + } + + @Override + protected void _cancelJob() { + } + +} diff --git a/src/engine/jobs/UpgradeNPCJob.java b/src/engine/jobs/UpgradeNPCJob.java new file mode 100644 index 00000000..23d0c5fb --- /dev/null +++ b/src/engine/jobs/UpgradeNPCJob.java @@ -0,0 +1,64 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.Enum.GameObjectType; +import engine.InterestManagement.WorldGrid; +import engine.job.AbstractScheduleJob; +import engine.objects.AbstractCharacter; +import engine.objects.Mob; +import engine.objects.NPC; + +public class UpgradeNPCJob extends AbstractScheduleJob { + + private final AbstractCharacter rankingAC; + boolean success; + + public UpgradeNPCJob(AbstractCharacter ac) { + super(); + this.rankingAC = ac; + } + + @Override + protected void doJob() { + + int newRank; + + if (this.rankingAC.getObjectType() == GameObjectType.NPC){ + + + if (this.rankingAC == null) //NPC could've been removed... + return; + newRank = (this.rankingAC.getRank() * 10) + 10; + + + + ((NPC)this.rankingAC).setRank(newRank); + ((NPC)this.rankingAC).setUpgradeDateTime(null); + }else if (this.rankingAC.getObjectType() == GameObjectType.Mob){ + if (this.rankingAC == null) //NPC could've been removed... + return; + newRank = (this.rankingAC.getRank() * 10) + 10; + + + + ((Mob)this.rankingAC).setRank(newRank); + Mob.setUpgradeDateTime((Mob)this.rankingAC, null); + WorldGrid.updateObject(this.rankingAC); + + } + + } + + @Override + protected void _cancelJob() { + } + +} diff --git a/src/engine/jobs/UseItemJob.java b/src/engine/jobs/UseItemJob.java new file mode 100644 index 00000000..5f2be44b --- /dev/null +++ b/src/engine/jobs/UseItemJob.java @@ -0,0 +1,57 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.gameManager.PowersManager; +import engine.job.AbstractScheduleJob; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.powers.PowersBase; + +public class UseItemJob extends AbstractScheduleJob { + + private final AbstractCharacter ac; + private final AbstractWorldObject target; + private final PowersBase pb; + private final int trains; + private final int liveCounter; + + public UseItemJob(AbstractCharacter ac, AbstractWorldObject target, PowersBase pb, int trains, int liveCounter) { + super(); + this.ac = ac; + this.target = target; + this.pb = pb; + this.trains = trains; + this.liveCounter = liveCounter; + } + + @Override + protected void doJob() { + PowersManager.finishApplyPower(ac, target, Vector3fImmutable.ZERO, pb, trains, liveCounter); + } + + @Override + protected void _cancelJob() { + this.ac.setItemCasting(false); + } + + public PowersBase getPowersBase() { + return this.pb; + } + + public int getTrains() { + return this.trains; + } + + public AbstractWorldObject getTarget() { + return this.target; + } +} diff --git a/src/engine/jobs/UseMobPowerJob.java b/src/engine/jobs/UseMobPowerJob.java new file mode 100644 index 00000000..3b827a34 --- /dev/null +++ b/src/engine/jobs/UseMobPowerJob.java @@ -0,0 +1,57 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.gameManager.PowersManager; +import engine.job.AbstractScheduleJob; +import engine.net.client.msg.PerformActionMsg; +import engine.objects.Mob; +import engine.powers.PowersBase; + +public class UseMobPowerJob extends AbstractScheduleJob { + + private final Mob caster; + private final PerformActionMsg msg; + private final int token; + private final PowersBase pb; + private final int casterLiveCounter; + private final int targetLiveCounter; + + public UseMobPowerJob(Mob caster, PerformActionMsg msg, int token, PowersBase pb, int casterLiveCounter, int targetLiveCounter) { + super(); + this.caster = caster; + this.msg = msg; + this.token = token; + this.pb = pb; + this.casterLiveCounter = casterLiveCounter; + this.targetLiveCounter = targetLiveCounter; + } + + @Override + protected void doJob() { + PowersManager.finishUseMobPower(this.msg, this.caster, casterLiveCounter, targetLiveCounter); + } + + @Override + protected void _cancelJob() { + } + + public PowersBase getPowersBase() { + return this.pb; + } + + public int getToken() { + return this.token; + } + + public PerformActionMsg getMsg() { + return this.msg; + } +} diff --git a/src/engine/jobs/UsePowerJob.java b/src/engine/jobs/UsePowerJob.java new file mode 100644 index 00000000..c9d4c18f --- /dev/null +++ b/src/engine/jobs/UsePowerJob.java @@ -0,0 +1,59 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.jobs; + +import engine.gameManager.PowersManager; +import engine.job.AbstractScheduleJob; +import engine.net.client.msg.PerformActionMsg; +import engine.objects.PlayerCharacter; +import engine.powers.PowersBase; + +public class UsePowerJob extends AbstractScheduleJob { + + private final PlayerCharacter pc; + private final PerformActionMsg msg; + private final int token; + private final PowersBase pb; + private final int casterLiveCounter; + private final int targetLiveCounter; + + public UsePowerJob(PlayerCharacter pc, PerformActionMsg msg, int token, PowersBase pb, int casterLiveCounter, int targetLiveCounter) { + super(); + this.pc = pc; + this.msg = msg; + this.token = token; + this.pb = pb; + this.casterLiveCounter = casterLiveCounter; + this.targetLiveCounter = targetLiveCounter; + } + + @Override + protected void doJob() { + PowersManager.finishUsePower(this.msg, this.pc, casterLiveCounter, targetLiveCounter); + } + + @Override + protected void _cancelJob() { + //cast stopped early, reset recycle timer + PowersManager.finishRecycleTime(this.msg, this.pc, true); + } + + public PowersBase getPowersBase() { + return this.pb; + } + + public int getToken() { + return this.token; + } + + public PerformActionMsg getMsg() { + return this.msg; + } +} diff --git a/src/engine/loot/LootGroup.java b/src/engine/loot/LootGroup.java new file mode 100644 index 00000000..8d9ab7bf --- /dev/null +++ b/src/engine/loot/LootGroup.java @@ -0,0 +1,114 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.loot; + +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * Data storage object for Loot System. + * Holds row in the lootGroup database table + */ +public class LootGroup { + + private final int groupID; + private final String groupName; + private final int minRoll; + private final int maxRoll; + private final int lootTableID; + private final String lootTableName; + private final int pMod; + private final int pModTableID; + private final int sMod; + private final int sModTableID; + + public LootGroup(ResultSet rs) throws SQLException { + + this.groupID = rs.getInt("groupID"); + this.groupName = rs.getString("groupName"); + this.minRoll = rs.getInt("minRoll"); + this.maxRoll = rs.getInt("maxRoll"); + this.lootTableID = rs.getInt("lootTableID"); + this.lootTableName = rs.getString("lootTableName"); + this.pMod = rs.getInt("pMod"); + this.pModTableID = rs.getInt("pModTableID"); + this.sMod = rs.getInt("sMod"); + this.sModTableID = rs.getInt("sModTableID"); + } + /** + * @return the groupID + */ + public int getGroupID() { + return groupID; + } + + /** + * @return the groupName + */ + public String getGroupName() { + return groupName; + } + + /** + * @return the minRoll + */ + public int getMinRoll() { + return minRoll; + } + + /** + * @return the maxRoll + */ + public int getMaxRoll() { + return maxRoll; + } + + /** + * @return the lootTableID + */ + public int getLootTableID() { + return lootTableID; + } + + /** + * @return the lootTableName + */ + public String getLootTableName() { + return lootTableName; + } + + /** + * @return the pMod + */ + public int getpMod() { + return pMod; + } + + /** + * @return the pModTableID + */ + public int getpModTableID() { + return pModTableID; + } + + /** + * @return the sMod + */ + public int getsMod() { + return sMod; + } + + /** + * @return the sModTableID + */ + public int getsModTableID() { + return sModTableID; + } + +} diff --git a/src/engine/loot/LootManager.java b/src/engine/loot/LootManager.java new file mode 100644 index 00000000..798dc55c --- /dev/null +++ b/src/engine/loot/LootManager.java @@ -0,0 +1,231 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.loot; + +import engine.gameManager.DbManager; +import engine.objects.Item; + +import java.util.HashMap; +import java.util.TreeMap; +import java.util.concurrent.ThreadLocalRandom; + +/** + * Class contains static methods for data from Magicbane's loot tables + */ +public class LootManager { + + private static final HashMap> _lootGroups = new HashMap<>(); + private static final HashMap> _lootTables = new HashMap<>(); + private static final HashMap> _modGroups = new HashMap<>(); + private static final HashMap _modTables = new HashMap<>(); + + private LootManager() { + + } + +// Method adds a lootGroup to the class's internal collection +// and configures the treemap accordingly + + public static void addLootGroup(LootGroup lootGroup) { + + // If entry for this lootGroup does not currently exist + // we need to create one. + + if (_lootGroups.containsKey(lootGroup.getGroupID()) == false) + _lootGroups.put(lootGroup.getGroupID(), new TreeMap<>()); + + // Add this lootgroup to the appropriate treemap + + _lootGroups.get(lootGroup.getGroupID()).put(lootGroup.getMaxRoll(), lootGroup); + + } + + public static void addLootTable(engine.loot.LootTable lootTable) { + + // If entry for this lootTabe does not currently exist + // we need to create one. + + if (_lootTables.containsKey(lootTable.getLootTable()) == false) + _lootTables.put(lootTable.getLootTable(), + new TreeMap<>()); + + // Add this lootTable to the appropriate treemap + + _lootTables.get(lootTable.getLootTable()).put(lootTable.getMaxRoll(), lootTable); + + } + + public static void addModifierGroup(ModifierGroup modGroup) { + + // If entry for this lootTabe does not currently exist + // we need to create one. + + if (_modGroups.containsKey(modGroup.getModGroup()) == false) + _modGroups.put(modGroup.getModGroup(), + new TreeMap<>()); + + // Add this lootTable to the appropriate treemap + + _modGroups.get(modGroup.getModGroup()).put(modGroup.getMaxRoll(), modGroup); + + } + + public static void addModifierTable(ModifierTable modTable) { + + // If entry for this lootTabe does not currently exist + // we need to create one. + + if (_modTables.containsKey(modTable.getModTable()) == false) + _modTables.put(modTable.getModTable(), + new TreeMap()); + + // Add this lootTable to the appropriate treemap + + _modTables.get(modTable.getModTable()).put(modTable.getMaxRoll(), modTable); + + } + + /* Mainline interfaces for this class. Methods below retrieve + * entries from the loottable by random number and range. + */ + + public static LootGroup getRandomLootGroup(int lootGroupID, int randomRoll) { + + if ((randomRoll < 1) || (randomRoll > 100)) + return null; + + // Get random lootGroup for this roll + + return _lootGroups.get(lootGroupID).floorEntry(randomRoll).getValue(); + + } + + public static engine.loot.LootTable getRandomLootTable(int lootTableID, int randomRoll) { + + if ((randomRoll < 1) || (randomRoll > 100)) + return null; + + // Get random lootTable for this roll + + return _lootTables.get(lootTableID).floorEntry(randomRoll).getValue(); + + } + + public static ModifierGroup getRandomModifierGroup(int modGroupID, int randomRoll) { + + if ((randomRoll < 1) || (randomRoll > 100)) + return null; + + // Get random modGroup for this roll + + return _modGroups.get(modGroupID).floorEntry(randomRoll).getValue(); + + } + + public static ModifierTable getRandomModifierTable(int modTableID, float randomRoll) { + + if ((randomRoll < 1.0f)) + return null; + + // Roll is outside of range + + if (randomRoll > getMaxRangeForModifierTable(modTableID)) + return null; + + // Get random lootGroup for this roll + + return (ModifierTable) _modTables.get(modTableID).floorEntry(randomRoll).getValue(); + + } + + // Returns minmum rolling range for a particular modifier table entry + + public static float getMinRangeForModifierTable(int modTableID) { + + ModifierTable outTable; + + outTable = (ModifierTable) _modTables.get(modTableID).firstEntry(); + + return outTable.getMinRoll(); + + } + + // Returns maximum rolling range for a particular modifier table entry + + public static float getMaxRangeForModifierTable(int modTableID) { + + ModifierTable outTable; + + outTable = (ModifierTable) _modTables.get(modTableID).lastEntry(); + + return outTable.getMaxRoll(); + } + + public static Item getRandomItemFromLootGroup(int lootGroupID, int randomRoll) { + + Item outItem = null; + LootGroup lootGroup; + LootTable lootTable; + ModifierGroup modGroup; + ModifierTable prefixTable; + ModifierTable suffixTable; + + // Retrieve a random loot group + + lootGroup = getRandomLootGroup(lootGroupID, randomRoll); + + if (lootGroup == null) + return null; + + // Retrieve a random loot table + + lootTable = getRandomLootTable(lootGroup.getLootTableID(), ThreadLocalRandom.current().nextInt(100)); + + if (lootTable == null) + return null; + + // Retrieve a random prefix + + modGroup = getRandomModifierGroup(lootGroup.getpModTableID(), ThreadLocalRandom.current().nextInt(100)); + + if (modGroup == null) + return null; + + prefixTable = getRandomModifierTable(modGroup.getSubTableID(), ThreadLocalRandom.current().nextFloat() * getMaxRangeForModifierTable(lootGroup.getpModTableID())); + + if (prefixTable == null) + return null; + + // Retrieve a random suffix + + modGroup = getRandomModifierGroup(lootGroup.getsModTableID(), ThreadLocalRandom.current().nextInt(100)); + + if (modGroup == null) + return null; + + suffixTable = getRandomModifierTable(modGroup.getSubTableID(), ThreadLocalRandom.current().nextFloat() * getMaxRangeForModifierTable(lootGroup.getsModTableID())); + + if (suffixTable == null) + return null; + + // Create the item! + + return outItem; + } + + // Bootstrap routine to load loot data from database + + public static void loadLootData() { + DbManager.LootQueries.LOAD_ALL_LOOTGROUPS(); + DbManager.LootQueries.LOAD_ALL_LOOTTABLES(); + DbManager.LootQueries.LOAD_ALL_MODGROUPS(); + DbManager.LootQueries.LOAD_ALL_MODTABLES(); + } + +} diff --git a/src/engine/loot/LootTable.java b/src/engine/loot/LootTable.java new file mode 100644 index 00000000..b2bd29d1 --- /dev/null +++ b/src/engine/loot/LootTable.java @@ -0,0 +1,98 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.loot; + +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * Data storage object for Loot System. + * Holds row in the lootTable database table + */ +public class LootTable { + + private final int lootTale; + private final String tableName; + private final String itemName; + private final int minRoll; + private final int maxRoll; + private final int itemBaseUUID; + private final int minSpawn; + private final int maxSpawn; + + public LootTable(ResultSet rs) throws SQLException { + + this.lootTale = rs.getInt("lootTable"); + this.tableName = rs.getString("tableName"); + this.itemName = rs.getString("itemName"); + this.minRoll = rs.getInt("minRoll"); + this.maxRoll = rs.getInt("maxRoll"); + this.itemBaseUUID = rs.getInt("itemBaseUUID"); + this.minSpawn = rs.getInt("minSpawn"); + this.maxSpawn = rs.getInt("maxSpawn"); + + } + + /** + * @return the lootTale + */ + public int getLootTable() { + return lootTale; + } + + /** + * @return the tableName + */ + public String getTableName() { + return tableName; + } + + /** + * @return the itemName + */ + public String getItemName() { + return itemName; + } + + /** + * @return the minRoll + */ + public int getMinRoll() { + return minRoll; + } + + /** + * @return the maxRoll + */ + public int getMaxRoll() { + return maxRoll; + } + + /** + * @return the itemBaseUUID + */ + public int getItemBaseUUID() { + return itemBaseUUID; + } + + /** + * @return the minSpawn + */ + public int getMinSpawn() { + return minSpawn; + } + + /** + * @return the maxSpawn + */ + public int getMaxSpawn() { + return maxSpawn; + } + +} diff --git a/src/engine/loot/ModifierGroup.java b/src/engine/loot/ModifierGroup.java new file mode 100644 index 00000000..ad854bfe --- /dev/null +++ b/src/engine/loot/ModifierGroup.java @@ -0,0 +1,78 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.loot; + +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * Data storage object for Loot System. + * Holds row in the modGroup database table + */ +public class ModifierGroup { + + private final int modGroup; + private final String groupName; + private final int minRoll; + private final int maxRoll; + private final int subTableID; + private final String subTableName; + + public ModifierGroup(ResultSet rs) throws SQLException { + + this.modGroup = rs.getInt("modGroup"); + this.groupName = rs.getString("groupName"); + this.minRoll = rs.getInt("minRoll"); + this.maxRoll = rs.getInt("maxRoll"); + this.subTableID = rs.getInt("subTableID"); + this.subTableName = rs.getString("subTableName"); + } + + /** + * @return the modGroup + */ + public int getModGroup() { + return modGroup; + } + + /** + * @return the groupName + */ + public String getGroupName() { + return groupName; + } + + /** + * @return the minRoll + */ + public int getMinRoll() { + return minRoll; + } + + /** + * @return the maxRoll + */ + public int getMaxRoll() { + return maxRoll; + } + + /** + * @return the subTableID + */ + public int getSubTableID() { + return subTableID; + } + + /** + * @return the subTableName + */ + public String getSubTableName() { + return subTableName; + } +} \ No newline at end of file diff --git a/src/engine/loot/ModifierTable.java b/src/engine/loot/ModifierTable.java new file mode 100644 index 00000000..5ca83431 --- /dev/null +++ b/src/engine/loot/ModifierTable.java @@ -0,0 +1,88 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.loot; + +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * Data storage object for Loot System. + * Holds row in the modTables database table + */ +public class ModifierTable{ + + private final int modTable; + private final String tableName; + private final float minRoll; + private final float maxRoll; + private final String action; + private final int level; + private final int value; + + public ModifierTable(ResultSet rs) throws SQLException { + + this.modTable = rs.getInt("modTable"); + this.tableName = rs.getString("tableName"); + this.minRoll = rs.getInt("minRoll"); + this.maxRoll = rs.getInt("maxRoll"); + this.action = rs.getString("action"); + this.level = rs.getInt("level"); + this.value = rs.getInt("value"); + } + + /** + * @return the modTable + */ + public int getModTable() { + return modTable; + } + + /** + * @return the tableName + */ + public String getTableName() { + return tableName; + } + + /** + * @return the minRoll + */ + public float getMinRoll() { + return minRoll; + } + + /** + * @return the maxRoll + */ + public float getMaxRoll() { + return maxRoll; + } + + /** + * @return the action + */ + public String getAction() { + return action; + } + + /** + * @return the level + */ + public int getLevel() { + return level; + } + + /** + * @return the value + */ + public int getValue() { + return value; + } + +} diff --git a/src/engine/math/AtomicFloat.java b/src/engine/math/AtomicFloat.java new file mode 100644 index 00000000..b5a1a808 --- /dev/null +++ b/src/engine/math/AtomicFloat.java @@ -0,0 +1,101 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.math; + +import java.util.concurrent.atomic.AtomicInteger; + +public class AtomicFloat { + + private final AtomicInteger fl; + + public AtomicFloat() { + fl = new AtomicInteger(Float.floatToIntBits(0f)); + } + + public AtomicFloat(float value) { + fl = new AtomicInteger(Float.floatToIntBits(value)); + } + + public float addAndGet(float delta) { + int oldValue = fl.get(); + while (!fl.compareAndSet(oldValue, Float.floatToIntBits(Float.intBitsToFloat(oldValue) + delta))) { + oldValue = fl.get(); + } + return fl.get(); + } + + public boolean compareAndSet(float oldVal, float newVal) { + return fl.compareAndSet(Float.floatToIntBits(oldVal), Float.floatToIntBits(newVal)); + } + + public float decrementAndGet() { + int oldValue = fl.get(); + while (!fl.compareAndSet(oldValue, Float.floatToIntBits(Float.intBitsToFloat(oldValue) - 1f))) { + oldValue = fl.get(); + } + return fl.get(); + } + + public float get() { + return Float.intBitsToFloat(fl.get()); + } + + public float getAndAdd(float delta) { + int oldValue = fl.get(); + while (!fl.compareAndSet(oldValue, Float.floatToIntBits(Float.intBitsToFloat(oldValue) + delta))) { + oldValue = fl.get(); + } + return oldValue; + } + + public float getAndIncrement() { + int oldValue = fl.get(); + while (!fl.compareAndSet(oldValue, Float.floatToIntBits(Float.intBitsToFloat(oldValue) + 1f))) { + oldValue = fl.get(); + } + return oldValue; + } + + public float getAndDecrement(float delta) { + int oldValue = fl.get(); + while (!fl.compareAndSet(oldValue, Float.floatToIntBits(Float.intBitsToFloat(oldValue) - 1f))) { + oldValue = fl.get(); + } + return oldValue; + } + + public float getAndSet(float value) { + return Float.intBitsToFloat(fl.getAndSet(Float.floatToIntBits(value))); + } + + public float incrementAndGet() { + int oldValue = fl.get(); + while (!fl.compareAndSet(oldValue, Float.floatToIntBits(Float.intBitsToFloat(oldValue) + 1f))) { + oldValue = fl.get(); + } + return fl.get(); + } + + public void lazySet(float value) { + fl.lazySet(Float.floatToIntBits(value)); + } + + public void set(float value) { + fl.set(Float.floatToIntBits(value)); + } + + @Override + public String toString() { + return fl.toString(); + } + + public boolean weakCompareAndSet(float oldVal, float newVal) { + return fl.weakCompareAndSet(Float.floatToIntBits(oldVal), Float.floatToIntBits(newVal)); + } +} diff --git a/src/engine/math/Bounds.java b/src/engine/math/Bounds.java new file mode 100644 index 00000000..f295f1dc --- /dev/null +++ b/src/engine/math/Bounds.java @@ -0,0 +1,583 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.math; + +import engine.InterestManagement.WorldGrid; +import engine.gameManager.ZoneManager; +import engine.net.client.msg.PlaceAssetMsg.PlacementInfo; +import engine.objects.*; +import engine.server.MBServerStatics; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.concurrent.LinkedBlockingQueue; + +/** + * This class contains all methods of storing bounds + * information within MagicBane and performing collision + * detection against them. + *

+ * These objects are essentially an AABB, given rotations + * in MagicBane for placed objects come in a quantum of 90. + */ + +public class Bounds { + + private static final LinkedBlockingQueue boundsPool = new LinkedBlockingQueue<>(); + public static HashMap meshBoundsCache = new HashMap<>(); + + private Vector2f origin = new Vector2f(); + private Vector2f halfExtents = new Vector2f(); + private float rotation; + private float rotationDegrees = 0; + private Quaternion quaternion; + private boolean flipExtents; + + private ArrayList regions = new ArrayList<>(); + private ArrayList colliders = new ArrayList<>(); + + // Default constructor + + public Bounds() { + + origin.zero(); + halfExtents.zero(); + rotation = 0.0f; + flipExtents = false; + } + + public static Bounds borrow() { + Bounds outBounds; + + outBounds = boundsPool.poll(); + + if (outBounds == null) + outBounds = new Bounds(); + + return outBounds; + } + + public void release() { + Bounds.zero(this); + boundsPool.add(this); + + } + + public void setBounds(Vector2f origin, Vector2f extents, float rotation) { + + this.origin.set(origin); + this.halfExtents.set(extents); + this.rotation = rotation; + + this.flipExtents = Bounds.calculateFlipExtents(this); + + } + + public void setBounds(PlacementInfo sourceInfo) { + + Blueprint sourceBlueprint; + + sourceBlueprint = Blueprint.getBlueprint(sourceInfo.getBlueprintUUID()); + this.origin.set(sourceInfo.getLoc().x, sourceInfo.getLoc().z); + this.halfExtents.set(sourceBlueprint.getExtents()); + + this.quaternion = new Quaternion(sourceInfo.getRot().x, sourceInfo.getRot().y,sourceInfo.getRot().z,sourceInfo.getW()); + this.rotation = sourceInfo.getRot().y; + this.flipExtents = Bounds.calculateFlipExtents(this); + + } + + public void setBounds(Bounds sourceBounds) { + + origin.set(sourceBounds.origin); + halfExtents.set(sourceBounds.halfExtents); + this.rotation = sourceBounds.rotation; + this.flipExtents = sourceBounds.flipExtents; + + } + + public void setBounds(AbstractCharacter sourcePlayer) { + + this.origin.set(sourcePlayer.getLoc().x, sourcePlayer.getLoc().z); + this.halfExtents.set(.5f, .5f); + this.rotation = 0; + this.flipExtents = false; + + } + + public void setBounds(Vector3fImmutable sourceLocation) { + + this.origin.set(sourceLocation.x, sourceLocation.z); + this.halfExtents.set(.5f, .5f); + this.rotation = 0; + this.flipExtents = false; + + } + + public void setBounds(Vector3fImmutable sourceLocation, float halfExtent) { + + this.origin.set(sourceLocation.x, sourceLocation.z); + this.halfExtents.set(halfExtent, halfExtent); + this.rotation = 0; + this.flipExtents = false; + + } + + public void setBounds(Building building) { + + Blueprint blueprint; + MeshBounds meshBounds; + int halfExtentX; + int halfExtentY; + // Need a blueprint for proper bounds + + blueprint = building.getBlueprint(); + + this.quaternion = new Quaternion(building.getRot().x, building.getRot().y,building.getRot().z,building.getw()); + + // Calculate Bounds for non-blueprint objects + + if (blueprint == null) { + + // If a mesh is a non-blueprint structure then we calculate + // it's bounding box based upon defaults from original source + // lookup. + + meshBounds = meshBoundsCache.get(building.getMeshUUID()); + this.origin.set(building.getLoc().x, building.getLoc().z); + + + // Magicbane uses half halfExtents + + if (meshBounds == null){ + halfExtentX = 1; + halfExtentY = 1; + }else{ + + float halfExtent = Math.max((meshBounds.maxX - meshBounds.minX)/2, (meshBounds.maxZ - meshBounds.minZ) /2); + halfExtentX = Math.round(halfExtent); + halfExtentY = Math.round(halfExtent); + } + + + // The rotation is reset after the new aabb is calculated. + + this.rotation = building.getRot().y; + // Caclculate and set the new half halfExtents for the rotated bounding box + // and reset the rotation to 0 for this bounds. + this.halfExtents.set(halfExtentX, (halfExtentY)); + this.rotation = 0; + + this.setRegions(building); + this.setColliders(building); + return; + } + + this.origin.set(building.getLoc().x, building.getLoc().z); + this.rotation = building.getRot().y; + this.halfExtents.set(blueprint.getExtents()); + this.flipExtents = Bounds.calculateFlipExtents(this); + + + this.setRegions(building); + this.setColliders(building); + + } + + + + + + + // Identity Bounds at location + public static void zero(Bounds bounds) { + bounds.origin.zero(); + bounds.halfExtents.zero(); + bounds.rotation = 0.0f; + bounds.flipExtents = false; + } + + public static boolean collide(Vector3fImmutable location, Bounds targetBounds) { + + if (targetBounds == null) + return false; + + boolean collisionState = false; + Bounds identityBounds = Bounds.borrow(); + identityBounds.setBounds(location); + + collisionState = collide(targetBounds, identityBounds, 0.0f); + identityBounds.release(); + return collisionState; + } + + + public static boolean collide(Vector3fImmutable location, Building targetBuilding) { + + boolean collisionState = false; + Bounds targetBounds = targetBuilding.getBounds(); + + if (targetBounds == null) + return false; + Bounds identityBounds = Bounds.borrow(); + identityBounds.setBounds(location); + + collisionState = collide(targetBounds, identityBounds, 0.1f); + identityBounds.release(); + return collisionState; + } + + public static boolean collide(Bounds sourceBounds, Bounds targetBounds, float threshold) { + + float deltaX; + float deltaY; + float extentX; + float extentY; + float sourceExtentX; + float sourceExtentY; + float targetExtentX; + float targetExtentY; + + deltaX = Math.abs(sourceBounds.origin.x - targetBounds.origin.x); + deltaY = Math.abs(sourceBounds.origin.y - targetBounds.origin.y); + + if (sourceBounds.flipExtents) { + sourceExtentX = sourceBounds.halfExtents.y; + sourceExtentY = sourceBounds.halfExtents.x; + } + else { + sourceExtentX = sourceBounds.halfExtents.x; + sourceExtentY = sourceBounds.halfExtents.y; + } + if (targetBounds.flipExtents) { + targetExtentX = targetBounds.halfExtents.y; + targetExtentY = targetBounds.halfExtents.x; + } + else { + targetExtentX = targetBounds.halfExtents.x; + targetExtentY = targetBounds.halfExtents.y; + } + + extentX = sourceExtentX + targetExtentX; + extentY = sourceExtentY + targetExtentY; + + // Return false on overlapping edge cases + if ((Math.abs(deltaX + threshold) < extentX)) + if ((Math.abs(deltaY + threshold) < extentY)) + return true; + + return false; + + } + + // Method detects overlap of two given Bounds objects. + // Just your generic AABB collision algorythm. + + public static boolean collide(PlacementInfo sourceInfo, Building targetBuilding) { + + Bounds sourceBounds; + Bounds targetBounds; + + boolean collisionState = false; + + // Early exit sanity check. Can't quite collide against nothing + + if ((sourceInfo == null) || (targetBuilding == null)) + return false; + + sourceBounds = Bounds.borrow(); + sourceBounds.setBounds(sourceInfo); + + // WARNING: DO NOT EVER RELEASE THESE WORLDOBJECT BOUNDS + // THEY ARE NOT IMMUTABLE + + targetBounds = targetBuilding.getBounds(); + + // If target building has no bounds, we certainly cannot collide. + // Note: We remove and release bounds objects to the pool when + // buildings are destroyed. + + if (targetBounds == null) + return false; + + collisionState = collide(sourceBounds, targetBounds,.1f); + + // Release bounds and return collision state + + sourceBounds.release(); + return collisionState; + } + + public static boolean collide(Bounds bounds, Vector3fImmutable start, Vector3fImmutable end) { + boolean collide = false; + for (Colliders collider: bounds.colliders) { + + collide = linesTouching(collider.startX, collider.startY, collider.endX,collider.endY, start.x, start.z, end.x,end.z); + + if (collide) + break; + + + } + + return collide; + } + + //used for wall collision with players. + public static Vector3fImmutable PlayerBuildingCollisionPoint(PlayerCharacter player, Vector3fImmutable start, Vector3fImmutable end) { + Vector3fImmutable collidePoint = null; + + //player can fly over walls when at max altitude. skip collision checks. + if (player.getAltitude() >= 60) + return null; + + + float distance = player.getLoc().distance2D(end); + // Players should not be able to move more than 2000 units at a time, stop them dead in their tracks if they do. (hacks) + if (distance > 2000) + return player.getLoc(); + + + + HashSet awoList = WorldGrid.getObjectsInRangePartial(player, distance + 1000, MBServerStatics.MASK_BUILDING); + float collideDistance = 0; + float lastDistance = -1; + + + for (AbstractWorldObject awo : awoList) { + + Building building = (Building)awo; + + + + //player is inside building region, skip collision check. we only do collision from the outside. + if (player.getRegion() != null && player.getRegion().parentBuildingID == building.getObjectUUID()) + continue; + if (building.getBounds().colliders == null) + continue; + + for (Colliders collider: building.getBounds().colliders) { + + //links are what link together buildings, allow players to run through them only if they are in a building already. + if (collider.isLink() && player.getRegion() != null) + continue; + if (collider.getDoorID() != 0 && building.isDoorOpen(collider.getDoorID())) + continue; + + Vector3fImmutable tempCollidePoint = lineIntersection(collider.startX, collider.startY, collider.endX,collider.endY, start.x, start.z, end.x,end.z); + + //didnt collide, skip distance checks. + if (tempCollidePoint == null) + continue; + + //first collision detection, inititialize all variables. + if (lastDistance == -1) { + collideDistance = start.distance2D(tempCollidePoint); + lastDistance = collideDistance; + collidePoint = tempCollidePoint; + }else + //get closest collide point. + collideDistance = start.distance2D(tempCollidePoint); + + if (collideDistance < lastDistance) { + lastDistance = collideDistance; + collidePoint = tempCollidePoint; + } + } + } + + + + // + if (collidePoint != null) { + + if(collideDistance >= 2) + collidePoint = player.getFaceDir().scaleAdd(-2f, new Vector3fImmutable((float) collidePoint.getX(), end.y, (float) collidePoint.getZ())); + else + collidePoint = player.getLoc(); + } + + + return collidePoint; + } + + public static boolean linesTouching(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) { + float denominator = ((x2 - x1) * (y4 - y3)) - ((y2 - y1) * (x4 - x3)); + float numerator1 = ((y1 - y3) * (x4 - x3)) - ((x1 - x3) * (y4 - y3)); + float numerator2 = ((y1 - y3) * (x2 - x1)) - ((x1 - x3) * (y2 - y1)); + + // Detect coincident lines (has a problem, read below) + if (denominator == 0) return numerator1 == 0 && numerator2 == 0; + + float r = numerator1 / denominator; + float s = numerator2 / denominator; + + return (r >= 0 && r <= 1) && (s >= 0 && s <= 1); + } + + public static Vector3fImmutable lineIntersection(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) { + + // calculate the distance to intersection point + float uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1)); + float uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1)); + + // if uA and uB are between 0-1, lines are colliding + if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) { + return new Vector3fImmutable(x1 + (uA * (x2-x1)),0, y1 + (uA * (y2-y1))); + } + return null; + } + + + + private static boolean calculateFlipExtents(Bounds bounds) { + + int degrees; + double radian =0; + if (bounds.quaternion != null){ + radian = bounds.quaternion.angleY; + } + + degrees = (int) Math.toDegrees(radian); + bounds.rotationDegrees = degrees; + if (degrees < 0) + degrees += 360; + return (degrees >= 85 && degrees <= 95) || + (degrees >= 265 && degrees <= 275); + + } + + public void modify(float x, float y, float extents) { + this.origin.x = x; + this.origin.y = y; + this.halfExtents.x = extents; + this.halfExtents.y = extents; + } + + + /** + * @return the origin + */ + public Vector2f getOrigin() { + return origin; + } + + /** + * @return the halfExtents + */ + public Vector2f getHalfExtents() { + return halfExtents; + } + + /** + * @return the rotation + */ + public float getRotation() { + return rotation; + } + + /** + * @param rotation the rotation to set + */ + public void setRotation(float rotation) { + this.rotation = rotation; + } + + + + + public void setRegions(Building building ){ + //Collidables are for player movement collision + ArrayList tempList = BuildingRegions._staticRegions.get(building.getMeshUUID()); + + ArrayList tempRegions = new ArrayList<>(); + if (tempList != null){ + + for (BuildingRegions buildingRegion:tempList){ + + ArrayList regionPoints = new ArrayList<>(); + + Vector3f centerPoint = ZoneManager.convertLocalToWorld(building, buildingRegion.center, this); + for (Vector3f point: buildingRegion.getRegionPoints()){ + Vector3f rotatedPoint = ZoneManager.convertLocalToWorld(building, point,this); + regionPoints.add(rotatedPoint); + } + tempRegions.add(new Regions(regionPoints, buildingRegion.getLevel(),buildingRegion.getRoom(),buildingRegion.isOutside(),buildingRegion.isExitRegion(), buildingRegion.isStairs(), centerPoint,building.getObjectUUID())); + } + + } + + + this.regions = tempRegions; + } + + public void setColliders(Building building ){ + //Collidables are for player movement collision + ArrayList tempList = StaticColliders._staticColliders.get(building.getMeshUUID()); + + ArrayList tempColliders = new ArrayList<>(); + if (tempList != null){ + + for (StaticColliders staticCollider :tempList){ + + ArrayList regionPoints = new ArrayList<>(); + + Vector3f colliderStart = new Vector3f(staticCollider.getStartX(), 0, staticCollider.getStartY()); + Vector3f colliderEnd = new Vector3f(staticCollider.getEndX(), 0, staticCollider.getEndY()); + Vector3f worldStart = ZoneManager.convertLocalToWorld(building, colliderStart, this); + Vector3f worldEnd = ZoneManager.convertLocalToWorld(building, colliderEnd, this); + tempColliders.add(new Colliders(worldStart.x, worldStart.z, worldEnd.x, worldEnd.z, staticCollider.getDoorID(), staticCollider.isLink())); + } + + } + + + this.colliders = tempColliders; + } + + public ArrayList getRegions() { + return regions; + } + + public void setRegions(ArrayList regions) { + this.regions = regions; + } + + public static Vector3f getRotatedPoint(Vector3f point, float centerX, float centerZ, float angle){ + + //TRANSLATE TO ORIGIN + float x1 = point.getX() - centerX; + float y1 = point.getZ() - centerZ; + + //APPLY ROTATION + float temp_x1 = (float) (x1 * Math.cos(angle) - y1 * Math.sin(angle)); + float temp_z1 = (float) (x1 * Math.sin(angle) + y1 * Math.cos(angle)); + + temp_x1 += centerX; + temp_z1 += centerZ; + + return new Vector3f(temp_x1,point.y,temp_z1); + + } + + + public float getRotationDegrees() { + return rotationDegrees; + } + + public boolean isFlipExtents() { + return flipExtents; + } + + public Quaternion getQuaternion() { + return quaternion; + } + +} diff --git a/src/engine/math/FastMath.java b/src/engine/math/FastMath.java new file mode 100644 index 00000000..067ef617 --- /dev/null +++ b/src/engine/math/FastMath.java @@ -0,0 +1,668 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + package engine.math; + +import java.util.Calendar; +import java.util.concurrent.ThreadLocalRandom; + +/** + * FastMath provides 'fast' math approximations and float + * equivalents of Math functions. These are all used as static values and + * functions. + * + * @author Various + */ + +final public class FastMath { + + private FastMath() { + } + + /** A "close to zero" double epsilon value for use */ + public static final double DBL_EPSILON = 2.220446049250313E-16d; + + /** A "close to zero" float epsilon value for use */ + public static final float FLT_EPSILON = 1.1920928955078125E-7f; + + /** A "close to zero" float epsilon value for use */ + public static final float ZERO_TOLERANCE = 0.0001f; + + public static final float ONE_THIRD = 1f / 3f; + + /** The value PI as a float. (180 degrees) */ + public static final float PI = (float) Math.PI; + + /** The value 2PI as a float. (360 degrees) */ + public static final float TWO_PI = 2.0f * PI; + + /** The value PI/2 as a float. (90 degrees) */ + public static final float HALF_PI = 0.5f * PI; + + /** The value PI/4 as a float. (45 degrees) */ + public static final float QUARTER_PI = 0.25f * PI; + + /** The value 1/PI as a float. */ + public static final float INV_PI = 1.0f / PI; + + /** The value 1/(2PI) as a float. */ + public static final float INV_TWO_PI = 1.0f / TWO_PI; + + /** A value to multiply a degree value by, to convert it to radians. */ + public static final float DEG_TO_RAD = PI / 180.0f; + + /** A value to multiply a radian value by, to convert it to degrees. */ + public static final float RAD_TO_DEG = 180.0f / PI; + + /** + * Returns true if the number is a power of 2 (2,4,8,16...) + * + * A good implementation found on the Java boards. note: a number is a power + * of two if and only if it is the smallest number with that number of + * significant bits. Therefore, if you subtract 1, you know that the new + * number will have fewer bits, so ANDing the original number with anything + * less than it will give 0. + * + * @param number + * The number to test. + * @return True if it is a power of two. + */ + public static boolean isPowerOfTwo(int number) { + return (number > 0) && (number & (number - 1)) == 0; + } + + public static int nearestPowerOfTwo(int number) { + return (int) Math.pow(2, Math.ceil(Math.log(number) / Math.log(2))); + } + + public static boolean between(float i, float minValueInclusive, float maxValueInclusive) { + return (i >= minValueInclusive && i <= maxValueInclusive); + } + + /** + * Linear interpolation from startValue to endValue by the given percent. + * Basically: ((1 - percent) * startValue) + (percent * endValue) + * + * @param percent + * Percent value to use. + * @param startValue + * Begining value. 0% of f + * @param endValue + * ending value. 100% of f + * @return The interpolated value between startValue and endValue. + */ + public static float LERP(float percent, float startValue, float endValue) { + if (startValue == endValue) + return startValue; + return ((1 - percent) * startValue) + (percent * endValue); + } + + /** + * Returns the arc cosine of an angle given in radians.
+ * Special cases: + *

    + *
  • If fValue is smaller than -1, then the result is PI. + *
  • If the argument is greater than 1, then the result is 0. + *
+ * + * @param fValue + * The angle, in radians. + * @return fValue's acos + * @see java.lang.Math#acos(double) + */ + public static float acos(float fValue) { + if (-1.0f < fValue) { + if (fValue < 1.0f) + return (float) Math.acos(fValue); + + return 0.0f; + } + + return PI; + } + + /** + * Returns the arc sine of an angle given in radians.
+ * Special cases: + *
    + *
  • If fValue is smaller than -1, then the result is -HALF_PI. + *
  • If the argument is greater than 1, then the result is HALF_PI. + *
+ * + * @param fValue + * The angle, in radians. + * @return fValue's asin + * @see java.lang.Math#asin(double) + */ + public static float asin(float fValue) { + if (-1.0f < fValue) { + if (fValue < 1.0f) + return (float) Math.asin(fValue); + + return HALF_PI; + } + + return -HALF_PI; + } + + /** + * Returns the arc tangent of an angle given in radians.
+ * + * @param fValue + * The angle, in radians. + * @return fValue's asin + * @see java.lang.Math#atan(double) + */ + public static float atan(float fValue) { + return (float) Math.atan(fValue); + } + + /** + * A direct call to Math.atan2. + * + * @param fY + * @param fX + * @return Math.atan2(fY,fX) + * @see java.lang.Math#atan2(double, double) + */ + public static float atan2(float fY, float fX) { + return (float) Math.atan2(fY, fX); + } + + /** + * Rounds a fValue up. A call to Math.ceil + * + * @param fValue + * The value. + * @return The fValue rounded up + * @see java.lang.Math#ceil(double) + */ + public static float ceil(float fValue) { + return (float) Math.ceil(fValue); + } + + /** + * Fast Trig functions for x86. This forces the trig functiosn to stay + * within the safe area on the x86 processor (-45 degrees to +45 degrees) + * The results may be very slightly off from what the Math and StrictMath + * trig functions give due to rounding in the angle reduction but it will be + * very very close. + * + */ + public static float reduceSinAngle(float radians) { + radians %= TWO_PI; // put us in -2PI to +2PI space + if (Math.abs(radians) > PI) { // put us in -PI to +PI space + radians -= (TWO_PI); + } + if (Math.abs(radians) > HALF_PI) {// put us in -PI/2 to +PI/2 space + radians = PI - radians; + } + + return radians; + } + + /** + * Returns sine of a value. + * + * @param fValue + * The value to sine, in radians. + * @return The sine of fValue. + * @see java.lang.Math#sin(double) + */ + public static float sin(float fValue) { + fValue = reduceSinAngle(fValue); // limits angle to between -PI/2 and + // +PI/2 + if (Math.abs(fValue) <= Math.PI / 4) { + return (float) Math.sin(fValue); + } + + return (float) Math.cos(Math.PI / 2 - fValue); + } + + /** + * Returns cos of a value. + * + * @param fValue + * The value to cosine, in radians. + * @return The cosine of fValue. + * @see java.lang.Math#cos(double) + */ + public static float cos(float fValue) { + return sin(fValue + HALF_PI); + } + + /** + * Returns E^fValue + * + * @param fValue + * Value to raise to a power. + * @return The value E^fValue + * @see java.lang.Math#exp(double) + */ + public static float exp(float fValue) { + return (float) Math.exp(fValue); + } + + /** + * Returns Absolute value of a float. + * + * @param fValue + * The value to abs. + * @return The abs of the value. + * @see java.lang.Math#abs(float) + */ + public static float abs(float fValue) { + if (fValue < 0) + return -fValue; + return fValue; + } + + /** + * Returns a number rounded down. + * + * @param fValue + * The value to round + * @return The given number rounded down + * @see java.lang.Math#floor(double) + */ + public static float floor(float fValue) { + return (float) Math.floor(fValue); + } + + /** + * Returns 1/sqrt(fValue) + * + * @param fValue + * The value to process. + * @return 1/sqrt(fValue) + * @see java.lang.Math#sqrt(double) + */ + public static float invSqrt(float fValue) { + return (float) (1.0f / Math.sqrt(fValue)); + } + + /** + * Returns the log base E of a value. + * + * @param fValue + * The value to Logger.getInstance(). + * @return The log of fValue base E + * @see java.lang.Math#log(double) + */ + public static float log(float fValue) { + return (float) Math.log(fValue); + } + + /** + * Returns the logarithm of value with given base, calculated as + * log(value)/log(base), so that pow(base, return)==value (contributed by + * vear) + * + * @param value + * The value to Logger.getInstance(). + * @param base + * Base of logarithm. + * @return The logarithm of value with given base + */ + public static float log(float value, float base) { + return (float) (Math.log(value) / Math.log(base)); + } + + /** + * Returns a number raised to an exponent power. fBase^fExponent + * + * @param fBase + * The base value (IE 2) + * @param fExponent + * The exponent value (IE 3) + * @return base raised to exponent (IE 8) + * @see java.lang.Math#pow(double, double) + */ + public static float pow(float fBase, float fExponent) { + return (float) Math.pow(fBase, fExponent); + } + + /** + * Returns the value squared. fValue ^ 2 + * + * @param fValue + * The vaule to square. + * @return The square of the given value. + */ + public static float sqr(float fValue) { + return fValue * fValue; + } + + public static double sqr(double fValue) { + return fValue * fValue; + } + + /** + * Returns the square root of a given value. + * + * @param fValue + * The value to sqrt. + * @return The square root of the given value. + * @see java.lang.Math#sqrt(double) + */ + public static float sqrt(float fValue) { + return (float) Math.sqrt(fValue); + } + + /** + * Returns the tangent of a value. If USE_FAST_TRIG is enabled, an + * approximate value is returned. Otherwise, a direct value is used. + * + * @param fValue + * The value to tangent, in radians. + * @return The tangent of fValue. + * @see java.lang.Math#tan(double) + */ + public static float tan(float fValue) { + return (float) Math.tan(fValue); + } + + /** + * Returns 1 if the number is positive, -1 if the number is negative, and 0 + * otherwise + * + * @param iValue + * The integer to examine. + * @return The integer's sign. + */ + public static int sign(int iValue) { + if (iValue > 0) + return 1; + + if (iValue < 0) + return -1; + + return 0; + } + + /** + * Returns 1 if the number is positive, -1 if the number is negative, and 0 + * otherwise + * + * @param fValue + * The float to examine. + * @return The float's sign. + */ + public static float sign(float fValue) { + return Math.signum(fValue); + } + + /** + * Given 3 points in a 2d plane, this function computes if the points going + * from A-B-C are moving counter clock wise. + * + * @param p0 + * Point 0. + * @param p1 + * Point 1. + * @param p2 + * Point 2. + * @return 1 If they are CCW, -1 if they are not CCW, 0 if p2 is between p0 + * and p1. + */ + public static int counterClockwise(Vector2f p0, Vector2f p1, Vector2f p2) { + float dx1, dx2, dy1, dy2; + dx1 = p1.x - p0.x; + dy1 = p1.y - p0.y; + dx2 = p2.x - p0.x; + dy2 = p2.y - p0.y; + if (dx1 * dy2 > dy1 * dx2) + return 1; + if (dx1 * dy2 < dy1 * dx2) + return -1; + if ((dx1 * dx2 < 0) || (dy1 * dy2 < 0)) + return -1; + if ((dx1 * dx1 + dy1 * dy1) < (dx2 * dx2 + dy2 * dy2)) + return 1; + return 0; + } + + /** + * Test if a point is inside a triangle. 1 if the point is on the ccw side, + * -1 if the point is on the cw side, and 0 if it is on neither. + * + * @param t0 + * First point of the triangle. + * @param t1 + * Second point of the triangle. + * @param t2 + * Third point of the triangle. + * @param p + * The point to test. + * @return Value 1 or -1 if inside triangle, 0 otherwise. + */ + public static int pointInsideTriangle(Vector2f t0, Vector2f t1, + Vector2f t2, Vector2f p) { + int val1 = counterClockwise(t0, t1, p); + if (val1 == 0) + return 1; + int val2 = counterClockwise(t1, t2, p); + if (val2 == 0) + return 1; + if (val2 != val1) + return 0; + int val3 = counterClockwise(t2, t0, p); + if (val3 == 0) + return 1; + if (val3 != val1) + return 0; + return val3; + } + + /** + * Returns the determinant of a 4x4 matrix. + */ + public static float determinant(double m00, double m01, double m02, + double m03, double m10, double m11, double m12, double m13, + double m20, double m21, double m22, double m23, double m30, + double m31, double m32, double m33) { + + double det01 = m20 * m31 - m21 * m30; + double det02 = m20 * m32 - m22 * m30; + double det03 = m20 * m33 - m23 * m30; + double det12 = m21 * m32 - m22 * m31; + double det13 = m21 * m33 - m23 * m31; + double det23 = m22 * m33 - m23 * m32; + return (float) (m00 * (m11 * det23 - m12 * det13 + m13 * det12) - m01 + * (m10 * det23 - m12 * det03 + m13 * det02) + m02 + * (m10 * det13 - m11 * det03 + m13 * det01) - m03 + * (m10 * det12 - m11 * det02 + m12 * det01)); + } + + /** + * Converts a point from Spherical coordinates to Cartesian (using positive + * Y as up) and stores the results in the store var. + */ + public static Vector3f sphericalToCartesian(Vector3f sphereCoords, + Vector3f store) { + store.y = sphereCoords.x * FastMath.sin(sphereCoords.z); + float a = sphereCoords.x * FastMath.cos(sphereCoords.z); + store.x = a * FastMath.cos(sphereCoords.y); + store.z = a * FastMath.sin(sphereCoords.y); + + return store; + } + + /** + * Converts a point from Cartesian coordinates (using positive Y as up) to + * Spherical and stores the results in the store var. (Radius, Azimuth, + * Polar) + */ + public static Vector3f cartesianToSpherical(Vector3f cartCoords, + Vector3f store) { + if (cartCoords.x == 0) + cartCoords.x = FastMath.FLT_EPSILON; + store.x = FastMath + .sqrt((cartCoords.x * cartCoords.x) + + (cartCoords.y * cartCoords.y) + + (cartCoords.z * cartCoords.z)); + store.y = FastMath.atan(cartCoords.z / cartCoords.x); + if (cartCoords.x < 0) + store.y += FastMath.PI; + store.z = FastMath.asin(cartCoords.y / store.x); + return store; + } + + /** + * Converts a point from Spherical coordinates to Cartesian (using positive + * Z as up) and stores the results in the store var. + */ + public static Vector3f sphericalToCartesianZ(Vector3f sphereCoords, + Vector3f store) { + store.z = sphereCoords.x * FastMath.sin(sphereCoords.z); + float a = sphereCoords.x * FastMath.cos(sphereCoords.z); + store.x = a * FastMath.cos(sphereCoords.y); + store.y = a * FastMath.sin(sphereCoords.y); + + return store; + } + + /** + * Converts a point from Cartesian coordinates (using positive Z as up) to + * Spherical and stores the results in the store var. (Radius, Azimuth, + * Polar) + */ + public static Vector3f cartesianZToSpherical(Vector3f cartCoords, + Vector3f store) { + if (cartCoords.x == 0) + cartCoords.x = FastMath.FLT_EPSILON; + store.x = FastMath + .sqrt((cartCoords.x * cartCoords.x) + + (cartCoords.y * cartCoords.y) + + (cartCoords.z * cartCoords.z)); + store.z = FastMath.atan(cartCoords.z / cartCoords.x); + if (cartCoords.x < 0) + store.z += FastMath.PI; + store.y = FastMath.asin(cartCoords.y / store.x); + return store; + } + + /** + * Takes an value and expresses it in terms of min to max. + * + * @param val + * - the angle to normalize (in radians) + * @return the normalized angle (also in radians) + */ + public static float normalize(float val, float min, float max) { + if (Float.isInfinite(val) || Float.isNaN(val)) + return 0f; + float range = max - min; + while (val > max) + val -= range; + while (val < min) + val += range; + return val; + } + + /** + * @param x + * the value whose sign is to be adjusted. + * @param y + * the value whose sign is to be used. + * @return x with its sign changed to match the sign of y. + */ + public static float copysign(float x, float y) { + if (y >= 0 && x <= -0) + return -x; + else if (y < 0 && x >= 0) + return -x; + else + return x; + } + + /** + * Take a float input and clamp it between min and max. + * + * @param input + * @param min + * @param max + * @return clamped input + */ + public static float clamp(float input, float min, float max) { + return (input < min) ? min : (input > max) ? max : input; + } + + + /** + * Return a random 2D vector + * this needs checked + */ + public static Vector3fImmutable randomVector2D() { + float x = ((ThreadLocalRandom.current().nextFloat() * 2) - 1); + float z = ((ThreadLocalRandom.current().nextFloat() * 2) - 1); + Vector3fImmutable ret = new Vector3fImmutable(x, 0, z); + return ret.normalize(); + } + + /** + * Turns a String into an Integer hash that the client will recognize. + * + * @param s + * The String to hash + * @return the Integer hash + */ + public static int hash(String s) { + int out = 0, pad = 0; + for(char c : s.toCharArray()) { + if(c == ' ' || c == '\\') { + pad += 5; + continue; + } + if(c > 0x40 && c < 0x60) + out ^= _rotl((byte) 0x60, pad); + out ^= _rotl((byte) (c & 0xDF), pad); + pad += 5; + } + return out; + } + + /** + * Utility function for making hashes + */ + private static int _rotl(int value, int shift) { + if ((shift &= 31) == 0) + return value; + return (value << shift) | (value >> (32 - shift)); + } + + /** + * Gets number of seconds until next hours + */ + + public static int secondsUntilNextHour() { + Calendar cal = Calendar.getInstance(); + int minute = cal.get(Calendar.MINUTE); + int second = cal.get(Calendar.SECOND); + return 3600 - (minute * 60) - second; + } + + public static float area(float x1, float y1, float x2, float y2, float x3, float y3){ + return (float) Math.abs((x1*(y2-y3) + x2*(y3-y1)+ x3*(y1-y2))/2.0); //Change to *.5? + } + + public static float GetDegrees(float yRot){ + double radian = Math.asin(yRot); + float degrees = (float) Math.toDegrees(radian); + + degrees *=2; + return degrees; + } + + public static float degreesToW(float degrees){ + return (float) Math.abs(Math.cos(Math.toRadians(degrees)/2)); + } + public static float degreesToYRotation(float degrees){ + return (float)Math.sin(Math.toRadians(degrees)/2); + } +} \ No newline at end of file diff --git a/src/engine/math/Matrix3f.java b/src/engine/math/Matrix3f.java new file mode 100644 index 00000000..f0e1efe8 --- /dev/null +++ b/src/engine/math/Matrix3f.java @@ -0,0 +1,1220 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + package engine.math; + +import org.pmw.tinylog.Logger; + +import java.nio.FloatBuffer; + + +/** + * Matrix3f defines a 3x3 matrix. Matrix data is maintained + * internally and is accessible via the get and set methods. Convenience methods + * are used for matrix operations as well as generating a matrix from a given + * set of values. + * + */ +public class Matrix3f { + public float m00, m01, m02; + public float m10, m11, m12; + public float m20, m21, m22; + + /** + * Constructor instantiates a new Matrix3f object. The initial + * values for the matrix is that of the identity matrix. + * + */ + public Matrix3f() { + loadIdentity(); + } + + /** + * constructs a matrix with the given values. + * + * @param m00 + * 0x0 in the matrix. + * @param m01 + * 0x1 in the matrix. + * @param m02 + * 0x2 in the matrix. + * @param m10 + * 1x0 in the matrix. + * @param m11 + * 1x1 in the matrix. + * @param m12 + * 1x2 in the matrix. + * @param m20 + * 2x0 in the matrix. + * @param m21 + * 2x1 in the matrix. + * @param m22 + * 2x2 in the matrix. + */ + public Matrix3f(float m00, float m01, float m02, float m10, float m11, + float m12, float m20, float m21, float m22) { + + this.m00 = m00; + this.m01 = m01; + this.m02 = m02; + this.m10 = m10; + this.m11 = m11; + this.m12 = m12; + this.m20 = m20; + this.m21 = m21; + this.m22 = m22; + } + + /** + * Copy constructor that creates a new Matrix3f object that is + * the same as the provided matrix. + * + * @param mat + * the matrix to copy. + */ + public Matrix3f(Matrix3f mat) { + copy(mat); + } + + /** + * copy transfers the contents of a given matrix to this + * matrix. If a null matrix is supplied, this matrix is set to the identity + * matrix. + * + * @param matrix + * the matrix to copy. + */ + public void copy(Matrix3f matrix) { + if (null == matrix) { + loadIdentity(); + } else { + m00 = matrix.m00; + m01 = matrix.m01; + m02 = matrix.m02; + m10 = matrix.m10; + m11 = matrix.m11; + m12 = matrix.m12; + m20 = matrix.m20; + m21 = matrix.m21; + m22 = matrix.m22; + } + } + + /** + * get retrieves a value from the matrix at the given position. + * If the position is invalid a Exception is thrown. + * + * @param i + * the row index. + * @param j + * the column index. + * @return the value at (i, j). + * @throws Exception + */ + public float get(int i, int j) throws Exception { + switch (i) { + case 0: + switch (j) { + case 0: + return m00; + case 1: + return m01; + case 2: + return m02; + } + case 1: + switch (j) { + case 0: + return m10; + case 1: + return m11; + case 2: + return m12; + } + case 2: + switch (j) { + case 0: + return m20; + case 1: + return m21; + case 2: + return m22; + } + } + throw new Exception("Invalid indices into matrix."); + } + + /** + * get(float[]) returns the matrix in row-major or column-major + * order. + * + * @param data + * The array to return the data into. This array can be 9 or 16 + * floats in size. Only the upper 3x3 are assigned to in the case + * of a 16 element array. + * @param rowMajor + * True for row major storage in the array (translation in + * elements 3, 7, 11 for a 4x4), false for column major + * (translation in elements 12, 13, 14 for a 4x4). + * @throws Exception + */ + public void get(float[] data, boolean rowMajor) throws Exception { + if (data.length == 9) { + if (rowMajor) { + data[0] = m00; + data[1] = m01; + data[2] = m02; + data[3] = m10; + data[4] = m11; + data[5] = m12; + data[6] = m20; + data[7] = m21; + data[8] = m22; + } else { + data[0] = m00; + data[1] = m10; + data[2] = m20; + data[3] = m01; + data[4] = m11; + data[5] = m21; + data[6] = m02; + data[7] = m12; + data[8] = m22; + } + } else if (data.length == 16) { + if (rowMajor) { + data[0] = m00; + data[1] = m01; + data[2] = m02; + data[4] = m10; + data[5] = m11; + data[6] = m12; + data[8] = m20; + data[9] = m21; + data[10] = m22; + } else { + data[0] = m00; + data[1] = m10; + data[2] = m20; + data[4] = m01; + data[5] = m11; + data[6] = m21; + data[8] = m02; + data[9] = m12; + data[10] = m22; + } + } else { + throw new Exception("Array size must be 9 or 16 in Matrix3f.get()."); + } + } + + /** + * getColumn returns one of three columns specified by the + * parameter. This column is returned as a Vector3f object. + * + * @param i + * the column to retrieve. Must be between 0 and 2. + * @return the column specified by the index. + * @throws Exception + */ + public Vector3f getColumn(int i) throws Exception { + return getColumn(i, null); + } + + /** + * getColumn returns one of three columns specified by the + * parameter. This column is returned as a Vector3f object. + * + * @param i + * the column to retrieve. Must be between 0 and 2. + * @param store + * the vector object to store the result in. if null, a new one + * is created. + * @return the column specified by the index. + * @throws Exception + */ + public Vector3f getColumn(int i, Vector3f store) throws Exception { + if (store == null) + store = new Vector3f(); + switch (i) { + case 0: + store.x = m00; + store.y = m10; + store.z = m20; + break; + case 1: + store.x = m01; + store.y = m11; + store.z = m21; + break; + case 2: + store.x = m02; + store.y = m12; + store.z = m22; + break; + default: + throw new Exception("Invalid column index. " + i); + } + return store; + } + + /** + * getColumn returns one of three rows as specified by the + * parameter. This row is returned as a Vector3f object. + * + * @param i + * the row to retrieve. Must be between 0 and 2. + * @return the row specified by the index. + * @throws Exception + */ + public Vector3f getRow(int i) throws Exception { + return getRow(i, null); + } + + /** + * getRow returns one of three rows as specified by the + * parameter. This row is returned as a Vector3f object. + * + * @param i + * the row to retrieve. Must be between 0 and 2. + * @param store + * the vector object to store the result in. if null, a new one + * is created. + * @return the row specified by the index. + * @throws Exception + */ + public Vector3f getRow(int i, Vector3f store) throws Exception { + if (store == null) + store = new Vector3f(); + switch (i) { + case 0: + store.x = m00; + store.y = m01; + store.z = m02; + break; + case 1: + store.x = m10; + store.y = m11; + store.z = m12; + break; + case 2: + store.x = m20; + store.y = m21; + store.z = m22; + break; + default: + throw new Exception("Invalid row index. " + i); + } + return store; + } + + /** + * fillFloatBuffer fills a FloatBuffer object with the matrix + * data. + * + * @param fb + * the buffer to fill, starting at current position. Must have + * room for 9 more floats. + * @return matrix data as a FloatBuffer. (position is advanced by 9 and any + * limit set is not changed). + */ + public FloatBuffer fillFloatBuffer(FloatBuffer fb) { + fb.put(m00).put(m01).put(m02); + fb.put(m10).put(m11).put(m12); + fb.put(m20).put(m21).put(m22); + return fb; + } + + /** + * + * setColumn sets a particular column of this matrix to that + * represented by the provided vector. + * + * @param i + * the column to set. + * @param column + * the data to set. + * @throws Exception + */ + public void setColumn(int i, Vector3f column) throws Exception { + + if (column == null) { + return; + } + switch (i) { + case 0: + m00 = column.x; + m10 = column.y; + m20 = column.z; + break; + case 1: + m01 = column.x; + m11 = column.y; + m21 = column.z; + break; + case 2: + m02 = column.x; + m12 = column.y; + m22 = column.z; + break; + default: + throw new Exception("Invalid column index. " + i); + } + } + + /** + * + * setRow sets a particular row of this matrix to that + * represented by the provided vector. + * + * @param i + * the row to set. + * @param row + * the data to set. + * @throws Exception + */ + public void setRow(int i, Vector3f row) throws Exception { + + if (row == null) { + return; + } + switch (i) { + case 0: + m00 = row.x; + m01 = row.y; + m02 = row.z; + break; + case 1: + m10 = row.x; + m11 = row.y; + m12 = row.z; + break; + case 2: + m20 = row.x; + m21 = row.y; + m22 = row.z; + break; + default: + throw new Exception("Invalid row index. " + i); + } + } + + /** + * set places a given value into the matrix at the given + * position. If the position is invalid a Exception is thrown. + * + * @param i + * the row index. + * @param j + * the column index. + * @param value + * the value for (i, j). + * @throws Exception + */ + public void set(int i, int j, float value) throws Exception { + switch (i) { + case 0: + switch (j) { + case 0: + m00 = value; + return; + case 1: + m01 = value; + return; + case 2: + m02 = value; + return; + } + case 1: + switch (j) { + case 0: + m10 = value; + return; + case 1: + m11 = value; + return; + case 2: + m12 = value; + return; + } + case 2: + switch (j) { + case 0: + m20 = value; + return; + case 1: + m21 = value; + return; + case 2: + m22 = value; + return; + } + } + throw new Exception("Invalid indices into matrix."); + } + + /** + * + * set sets the values of the matrix to those supplied by the + * 3x3 two dimenion array. + * + * @param matrix + * the new values of the matrix. + * @throws Exception + * if the array is not of size 9. + */ + public void set(float[][] matrix) throws Exception { + if (matrix.length != 3 || matrix[0].length != 3) { + throw new Exception("Array must be of size 9."); + } + + m00 = matrix[0][0]; + m01 = matrix[0][1]; + m02 = matrix[0][2]; + m10 = matrix[1][0]; + m11 = matrix[1][1]; + m12 = matrix[1][2]; + m20 = matrix[2][0]; + m21 = matrix[2][1]; + m22 = matrix[2][2]; + } + + /** + * Recreate Matrix using the provided axis. + * + * @param uAxis + * Vector3f + * @param vAxis + * Vector3f + * @param wAxis + * Vector3f + */ + public void fromAxes(Vector3f uAxis, Vector3f vAxis, Vector3f wAxis) { + m00 = uAxis.x; + m10 = uAxis.y; + m20 = uAxis.z; + + m01 = vAxis.x; + m11 = vAxis.y; + m21 = vAxis.z; + + m02 = wAxis.x; + m12 = wAxis.y; + m22 = wAxis.z; + } + + /** + * set sets the values of this matrix from an array of values + * assuming that the data is rowMajor order; + * + * @param matrix + * the matrix to set the value to. + * @throws Exception + */ + public void set(float[] matrix) throws Exception { + set(matrix, true); + } + + /** + * set sets the values of this matrix from an array of values; + * + * @param matrix + * the matrix to set the value to. + * @param rowMajor + * whether the incoming data is in row or column major order. + */ + public void set(float[] matrix, boolean rowMajor) throws Exception { + if (matrix.length != 9) + throw new Exception("Array must be of size 9."); + + if (rowMajor) { + m00 = matrix[0]; + m01 = matrix[1]; + m02 = matrix[2]; + m10 = matrix[3]; + m11 = matrix[4]; + m12 = matrix[5]; + m20 = matrix[6]; + m21 = matrix[7]; + m22 = matrix[8]; + } else { + m00 = matrix[0]; + m01 = matrix[3]; + m02 = matrix[6]; + m10 = matrix[1]; + m11 = matrix[4]; + m12 = matrix[7]; + m20 = matrix[2]; + m21 = matrix[5]; + m22 = matrix[8]; + } + } + + /** + * + * set defines the values of the matrix based on a supplied + * Quaternion. It should be noted that all previous values will + * be overridden. + * + * @param quaternion + * the quaternion to create a rotational matrix from. + */ + public void set(Quaternion quaternion) { + quaternion.toRotationMatrix(this); + } + + /** + * loadIdentity sets this matrix to the identity matrix. Where + * all values are zero except those along the diagonal which are one. + * + */ + public void loadIdentity() { + m01 = m02 = m10 = m12 = m20 = m21 = 0; + m00 = m11 = m22 = 1; + } + + /** + * @return true if this matrix is identity + */ + public boolean isIdentity() { + return (m00 == 1 && m01 == 0 && m02 == 0) + && (m10 == 0 && m11 == 1 && m12 == 0) + && (m20 == 0 && m21 == 0 && m22 == 1); + } + + /** + * fromAngleAxis sets this matrix4f to the values specified by + * an angle and an axis of rotation. This method creates an object, so use + * fromAngleNormalAxis if your axis is already normalized. + * + * @param angle + * the angle to rotate (in radians). + * @param axis + * the axis of rotation. + */ + public void fromAngleAxis(float angle, Vector3f axis) { + Vector3f normAxis = axis.normalize(); + fromAngleNormalAxis(angle, normAxis); + } + + /** + * fromAngleNormalAxis sets this matrix4f to the values + * specified by an angle and a normalized axis of rotation. + * + * @param angle + * the angle to rotate (in radians). + * @param axis + * the axis of rotation (already normalized). + */ + public void fromAngleNormalAxis(float angle, Vector3f axis) { + float fCos = FastMath.cos(angle); + float fSin = FastMath.sin(angle); + float fOneMinusCos = ((float) 1.0) - fCos; + float fX2 = axis.x * axis.x; + float fY2 = axis.y * axis.y; + float fZ2 = axis.z * axis.z; + float fXYM = axis.x * axis.y * fOneMinusCos; + float fXZM = axis.x * axis.z * fOneMinusCos; + float fYZM = axis.y * axis.z * fOneMinusCos; + float fXSin = axis.x * fSin; + float fYSin = axis.y * fSin; + float fZSin = axis.z * fSin; + + m00 = fX2 * fOneMinusCos + fCos; + m01 = fXYM - fZSin; + m02 = fXZM + fYSin; + m10 = fXYM + fZSin; + m11 = fY2 * fOneMinusCos + fCos; + m12 = fYZM - fXSin; + m20 = fXZM - fYSin; + m21 = fYZM + fXSin; + m22 = fZ2 * fOneMinusCos + fCos; + } + + /** + * mult multiplies this matrix by a given matrix. The result + * matrix is returned as a new object. If the given matrix is null, a null + * matrix is returned. + * + * @param mat + * the matrix to multiply this matrix by. + * @return the result matrix. + */ + public Matrix3f mult(Matrix3f mat) { + return mult(mat, null); + } + + /** + * mult multiplies this matrix by a given matrix. The result + * matrix is returned as a new object. + * + * @param mat + * the matrix to multiply this matrix by. + * @param product + * the matrix to store the result in. if null, a new matrix3f is + * created. It is safe for mat and product to be the same object. + * @return a matrix3f object containing the result of this operation + */ + public Matrix3f mult(Matrix3f mat, Matrix3f product) { + + float temp00, temp01, temp02; + float temp10, temp11, temp12; + float temp20, temp21, temp22; + + if (product == null) + product = new Matrix3f(); + temp00 = m00 * mat.m00 + m01 * mat.m10 + m02 * mat.m20; + temp01 = m00 * mat.m01 + m01 * mat.m11 + m02 * mat.m21; + temp02 = m00 * mat.m02 + m01 * mat.m12 + m02 * mat.m22; + temp10 = m10 * mat.m00 + m11 * mat.m10 + m12 * mat.m20; + temp11 = m10 * mat.m01 + m11 * mat.m11 + m12 * mat.m21; + temp12 = m10 * mat.m02 + m11 * mat.m12 + m12 * mat.m22; + temp20 = m20 * mat.m00 + m21 * mat.m10 + m22 * mat.m20; + temp21 = m20 * mat.m01 + m21 * mat.m11 + m22 * mat.m21; + temp22 = m20 * mat.m02 + m21 * mat.m12 + m22 * mat.m22; + + product.m00 = temp00; + product.m01 = temp01; + product.m02 = temp02; + product.m10 = temp10; + product.m11 = temp11; + product.m12 = temp12; + product.m20 = temp20; + product.m21 = temp21; + product.m22 = temp22; + + return product; + } + + /** + * mult multiplies this matrix by a given Vector3f + * object. The result vector is returned. If the given vector is null, null + * will be returned. + * + * @param vec + * the vector to multiply this matrix by. + * @return the result vector. + */ + public Vector3f mult(Vector3f vec) { + return mult(vec, null); + } + + /** + * Multiplies this 3x3 matrix by the 1x3 Vector vec and stores the result in + * product. + * + * @param vec + * The Vector3f to multiply. + * @param product + * The Vector3f to store the result, it is safe for this to be + * the same as vec. + * @return The given product vector. + */ + public Vector3f mult(Vector3f vec, Vector3f product) { + + if (null == product) { + product = new Vector3f(); + } + + float x = vec.x; + float y = vec.y; + float z = vec.z; + + product.x = m00 * x + m01 * y + m02 * z; + product.y = m10 * x + m11 * y + m12 * z; + product.z = m20 * x + m21 * y + m22 * z; + return product; + } + + /** + * multLocal multiplies this matrix internally by a given float + * scale factor. + * + * @param scale + * the value to scale by. + * @return this Matrix3f + */ + public Matrix3f multLocal(float scale) { + m00 *= scale; + m01 *= scale; + m02 *= scale; + m10 *= scale; + m11 *= scale; + m12 *= scale; + m20 *= scale; + m21 *= scale; + m22 *= scale; + return this; + } + + /** + * multLocal multiplies this matrix by a given + * Vector3f object. The result vector is stored inside the + * passed vector, then returned . If the given vector is null, null will be + * returned. + * + * @param vec + * the vector to multiply this matrix by. + * @return The passed vector after multiplication + */ + public Vector3f multLocal(Vector3f vec) { + if (vec == null) + return null; + float x = vec.x; + float y = vec.y; + vec.x = m00 * x + m01 * y + m02 * vec.z; + vec.y = m10 * x + m11 * y + m12 * vec.z; + vec.z = m20 * x + m21 * y + m22 * vec.z; + return vec; + } + + /** + * mult multiplies this matrix by a given matrix. The result + * matrix is saved in the current matrix. If the given matrix is null, + * nothing happens. The current matrix is returned. This is equivalent to + * this*=mat + * + * @param mat + * the matrix to multiply this matrix by. + * @return This matrix, after the multiplication + */ + public Matrix3f multLocal(Matrix3f mat) { + + return mult(mat, this); + } + + /** + * Transposes this matrix in place. Returns this matrix for chaining + * + * @return This matrix after transpose + * @throws Exception + */ + public Matrix3f transposeLocal() throws Exception { + float[] tmp = new float[9]; + get(tmp, false); + set(tmp, true); + return this; + } + + /** + * Inverts this matrix as a new Matrix3f. + * + * @return The new inverse matrix + */ + public Matrix3f invert() { + return invert(null); + } + + /** + * Inverts this matrix and stores it in the given store. + * + * @return The store + */ + public Matrix3f invert(Matrix3f store) { + if (store == null) + store = new Matrix3f(); + + float det = determinant(); + if (FastMath.abs(det) <= 0) + return store.zero(); + + store.m00 = m11 * m22 - m12 * m21; + store.m01 = m02 * m21 - m01 * m22; + store.m02 = m01 * m12 - m02 * m11; + store.m10 = m12 * m20 - m10 * m22; + store.m11 = m00 * m22 - m02 * m20; + store.m12 = m02 * m10 - m00 * m12; + store.m20 = m10 * m21 - m11 * m20; + store.m21 = m01 * m20 - m00 * m21; + store.m22 = m00 * m11 - m01 * m10; + + store.multLocal(1f / det); + return store; + } + + /** + * Inverts this matrix locally. + * + * @return this + */ + public Matrix3f invertLocal() { + float det = determinant(); + if (FastMath.abs(det) <= FastMath.FLT_EPSILON) + return zero(); + + float f00 = m11 * m22 - m12 * m21; + float f01 = m02 * m21 - m01 * m22; + float f02 = m01 * m12 - m02 * m11; + float f10 = m12 * m20 - m10 * m22; + float f11 = m00 * m22 - m02 * m20; + float f12 = m02 * m10 - m00 * m12; + float f20 = m10 * m21 - m11 * m20; + float f21 = m01 * m20 - m00 * m21; + float f22 = m00 * m11 - m01 * m10; + + m00 = f00; + m01 = f01; + m02 = f02; + m10 = f10; + m11 = f11; + m12 = f12; + m20 = f20; + m21 = f21; + m22 = f22; + + multLocal(1f / det); + return this; + } + + /** + * Returns a new matrix representing the adjoint of this matrix. + * + * @return The adjoint matrix + */ + public Matrix3f adjoint() { + return adjoint(null); + } + + /** + * Places the adjoint of this matrix in store (creates store if null.) + * + * @param store + * The matrix to store the result in. If null, a new matrix is + * created. + * @return store + */ + public Matrix3f adjoint(Matrix3f store) { + if (store == null) + store = new Matrix3f(); + + store.m00 = m11 * m22 - m12 * m21; + store.m01 = m02 * m21 - m01 * m22; + store.m02 = m01 * m12 - m02 * m11; + store.m10 = m12 * m20 - m10 * m22; + store.m11 = m00 * m22 - m02 * m20; + store.m12 = m02 * m10 - m00 * m12; + store.m20 = m10 * m21 - m11 * m20; + store.m21 = m01 * m20 - m00 * m21; + store.m22 = m00 * m11 - m01 * m10; + + return store; + } + + /** + * determinant generates the determinate of this matrix. + * + * @return the determinate + */ + public float determinant() { + float fCo00 = m11 * m22 - m12 * m21; + float fCo10 = m12 * m20 - m10 * m22; + float fCo20 = m10 * m21 - m11 * m20; + return m00 * fCo00 + m01 * fCo10 + m02 * fCo20; + } + + /** + * Sets all of the values in this matrix to zero. + * + * @return this matrix + */ + public Matrix3f zero() { + m00 = m01 = m02 = m10 = m11 = m12 = m20 = m21 = m22 = 0.0f; + return this; + } + + /** + * add adds the values of a parameter matrix to this matrix. + * + * @param mat + * the matrix to add to this. + */ + public void add(Matrix3f mat) { + m00 += mat.m00; + m01 += mat.m01; + m02 += mat.m02; + m10 += mat.m10; + m11 += mat.m11; + m12 += mat.m12; + m20 += mat.m20; + m21 += mat.m21; + m22 += mat.m22; + } + + /** + * transpose locally transposes this Matrix. This is + * inconsistent with general value vs local semantics, but is preserved for + * backwards compatibility. Use transposeNew() to transpose to a new object + * (value). + * + * @return this object for chaining. + * @throws Exception + */ + public Matrix3f transpose() throws Exception { + return transposeLocal(); + } + + /** + * transposeNew returns a transposed version of this matrix. + * + * @return The new Matrix3f object. + */ + public Matrix3f transposeNew() { + return new Matrix3f(m00, m10, m20, m01, m11, m21, m02, m12, m22); + } + + /** + * toString returns the string representation of this object. + * It is in a format of a 3x3 matrix. For example, an identity matrix would + * be represented by the following string. com.jme.math.Matrix3f
+ * [
+ * 1.0 0.0 0.0
+ * 0.0 1.0 0.0
+ * 0.0 0.0 1.0
+ * ]
+ * + * @return the string representation of this object. + */ + @Override + public String toString() { + StringBuffer result = new StringBuffer("com.jme.math.Matrix3f\n[\n"); + result.append(' '); + result.append(m00); + result.append(" "); + result.append(m01); + result.append(" "); + result.append(m02); + result.append(" \n"); + result.append(' '); + result.append(m10); + result.append(" "); + result.append(m11); + result.append(" "); + result.append(m12); + result.append(" \n"); + result.append(' '); + result.append(m20); + result.append(" "); + result.append(m21); + result.append(" "); + result.append(m22); + result.append(" \n]"); + return result.toString(); + } + + /** + * + * hashCode returns the hash code value as an integer and is + * supported for the benefit of hashing based collection classes such as + * Hashtable, HashMap, HashSet etc. + * + * @return the hashcode for this instance of Matrix4f. + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + int hash = 37; + hash = 37 * hash + Float.floatToIntBits(m00); + hash = 37 * hash + Float.floatToIntBits(m01); + hash = 37 * hash + Float.floatToIntBits(m02); + + hash = 37 * hash + Float.floatToIntBits(m10); + hash = 37 * hash + Float.floatToIntBits(m11); + hash = 37 * hash + Float.floatToIntBits(m12); + + hash = 37 * hash + Float.floatToIntBits(m20); + hash = 37 * hash + Float.floatToIntBits(m21); + hash = 37 * hash + Float.floatToIntBits(m22); + + return hash; + } + + /** + * are these two matrices the same? they are is they both have the same mXX + * values. + * + * @param o + * the object to compare for equality + * @return true if they are equal + */ + @Override + public boolean equals(Object o) { + if (!(o instanceof Matrix3f) || o == null) { + return false; + } + + if (this == o) { + return true; + } + + Matrix3f comp = (Matrix3f) o; + if (Float.compare(m00, comp.m00) != 0) + return false; + if (Float.compare(m01, comp.m01) != 0) + return false; + if (Float.compare(m02, comp.m02) != 0) + return false; + + if (Float.compare(m10, comp.m10) != 0) + return false; + if (Float.compare(m11, comp.m11) != 0) + return false; + if (Float.compare(m12, comp.m12) != 0) + return false; + + if (Float.compare(m20, comp.m20) != 0) + return false; + if (Float.compare(m21, comp.m21) != 0) + return false; + return Float.compare(m22, comp.m22) == 0; + } + + /** + * A function for creating a rotation matrix that rotates a vector called + * "start" into another vector called "end". + * + * @param start + * normalized non-zero starting vector + * @param end + * normalized non-zero ending vector + * @throws Exception + * @see "Tomas Miller, John Hughes \"Efficiently Building a Matrix to Rotate + * \ One Vector to Another\" Journal of Graphics Tools, 4(4):1-4, 1999" + */ + public void fromStartEndVectors(Vector3f start, Vector3f end) + throws Exception { + Vector3f v = new Vector3f(); + float e, h, f; + + start.cross(end, v); + e = start.dot(end); + f = (e < 0) ? -e : e; + + // if "from" and "to" vectors are nearly parallel + if (f > 1.0f - FastMath.ZERO_TOLERANCE) { + Vector3f u = new Vector3f(); + Vector3f x = new Vector3f(); + float c1, c2, c3; /* coefficients for later use */ + int i, j; + + x.x = (start.x > 0.0) ? start.x : -start.x; + x.y = (start.y > 0.0) ? start.y : -start.y; + x.z = (start.z > 0.0) ? start.z : -start.z; + + if (x.x < x.y) { + if (x.x < x.z) { + x.x = 1.0f; + x.y = x.z = 0.0f; + } else { + x.z = 1.0f; + x.x = x.y = 0.0f; + } + } else { + if (x.y < x.z) { + x.y = 1.0f; + x.x = x.z = 0.0f; + } else { + x.z = 1.0f; + x.x = x.y = 0.0f; + } + } + + u.x = x.x - start.x; + u.y = x.y - start.y; + u.z = x.z - start.z; + v.x = x.x - end.x; + v.y = x.y - end.y; + v.z = x.z - end.z; + + c1 = 2.0f / u.dot(u); + c2 = 2.0f / v.dot(v); + c3 = c1 * c2 * u.dot(v); + + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + float val = -c1 * u.get(i) * u.get(j) - c2 * v.get(i) + * v.get(j) + c3 * v.get(i) * u.get(j); + set(i, j, val); + } + float val = get(i, i); + set(i, i, val + 1.0f); + } + } else { + // the most common case, unless "start"="end", or "start"=-"end" + float hvx, hvz, hvxy, hvxz, hvyz; + h = 1.0f / (1.0f + e); + hvx = h * v.x; + hvz = h * v.z; + hvxy = hvx * v.y; + hvxz = hvx * v.z; + hvyz = hvz * v.y; + set(0, 0, e + hvx * v.x); + set(0, 1, hvxy - v.z); + set(0, 2, hvxz + v.y); + + set(1, 0, hvxy + v.z); + set(1, 1, e + h * v.y * v.y); + set(1, 2, hvyz - v.x); + + set(2, 0, hvxz - v.y); + set(2, 1, hvyz + v.x); + set(2, 2, e + hvz * v.z); + } + } + + /** + * scale scales the operation performed by this matrix on a + * per-component basis. + * + * @param scale + * The scale applied to each of the X, Y and Z output values. + */ + public void scale(Vector3f scale) { + m00 *= scale.x; + m10 *= scale.x; + m20 *= scale.x; + m01 *= scale.y; + m11 *= scale.y; + m21 *= scale.y; + m02 *= scale.z; + m12 *= scale.z; + m22 *= scale.z; + } + + static boolean equalIdentity(Matrix3f mat) { + if (Math.abs(mat.m00 - 1) > 1e-4) + return false; + if (Math.abs(mat.m11 - 1) > 1e-4) + return false; + if (Math.abs(mat.m22 - 1) > 1e-4) + return false; + + if (Math.abs(mat.m01) > 1e-4) + return false; + if (Math.abs(mat.m02) > 1e-4) + return false; + + if (Math.abs(mat.m10) > 1e-4) + return false; + if (Math.abs(mat.m12) > 1e-4) + return false; + + if (Math.abs(mat.m20) > 1e-4) + return false; + return !(Math.abs(mat.m21) > 1e-4); + } + + @Override + public Matrix3f clone() { + try { + return (Matrix3f) super.clone(); + } catch (CloneNotSupportedException e) { + Logger.error( e); + throw new AssertionError(); // can not happen + } + } +} \ No newline at end of file diff --git a/src/engine/math/Matrix4f.java b/src/engine/math/Matrix4f.java new file mode 100644 index 00000000..e0b18b00 --- /dev/null +++ b/src/engine/math/Matrix4f.java @@ -0,0 +1,1791 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.math; + +import org.pmw.tinylog.Logger; + +import java.nio.FloatBuffer; + + +/** + * Matrix4f defines and maintains a 4x4 matrix in row major order. + * This matrix is intended for use in a translation and rotational capacity. It + * provides convenience methods for creating the matrix from a multitude of + * sources. + * + * Matrices are stored assuming column vectors on the right, with the + * translation in the rightmost column. Element numbering is row,column, so m03 + * is the zeroth row, third column, which is the "x" translation part. This + * means that the implicit storage order is column major. However, the get() and + * set() functions on float arrays default to row major order! + * + */ +public class Matrix4f{ + + public float m00, m01, m02, m03; + + public float m10, m11, m12, m13; + + public float m20, m21, m22, m23; + + public float m30, m31, m32, m33; + + /** + * Constructor instantiates a new Matrix that is set to the + * identity matrix. + * + */ + public Matrix4f() { + loadIdentity(); + } + + /** + * constructs a matrix with the given values. + */ + public Matrix4f(float m00, float m01, float m02, float m03, float m10, float m11, float m12, float m13, float m20, float m21, + float m22, float m23, float m30, float m31, float m32, float m33) { + + this.m00 = m00; + this.m01 = m01; + this.m02 = m02; + this.m03 = m03; + this.m10 = m10; + this.m11 = m11; + this.m12 = m12; + this.m13 = m13; + this.m20 = m20; + this.m21 = m21; + this.m22 = m22; + this.m23 = m23; + this.m30 = m30; + this.m31 = m31; + this.m32 = m32; + this.m33 = m33; + } + + /** + * Create a new Matrix4f, given data in column-major format. + * + * @param array + * An array of 16 floats in column-major format (translation in + * elements 12, 13 and 14). + * @throws Exception + */ + public Matrix4f(float[] array) throws Exception { + set(array, false); + } + + /** + * Constructor instantiates a new Matrix that is set to the + * provided matrix. This constructor copies a given Matrix. If the provided + * matrix is null, the constructor sets the matrix to the identity. + * + * @param mat + * the matrix to copy. + */ + public Matrix4f(Matrix4f mat) { + copy(mat); + } + + /** + * copy transfers the contents of a given matrix to this + * matrix. If a null matrix is supplied, this matrix is set to the identity + * matrix. + * + * @param matrix + * the matrix to copy. + */ + public void copy(Matrix4f matrix) { + if (null == matrix) { + loadIdentity(); + } else { + m00 = matrix.m00; + m01 = matrix.m01; + m02 = matrix.m02; + m03 = matrix.m03; + m10 = matrix.m10; + m11 = matrix.m11; + m12 = matrix.m12; + m13 = matrix.m13; + m20 = matrix.m20; + m21 = matrix.m21; + m22 = matrix.m22; + m23 = matrix.m23; + m30 = matrix.m30; + m31 = matrix.m31; + m32 = matrix.m32; + m33 = matrix.m33; + } + } + + /** + * get retrieves the values of this object into a float array + * in row-major order. + * + * @param matrix + * the matrix to set the values into. + * @throws Exception + */ + public void get(float[] matrix) throws Exception { + get(matrix, true); + } + + /** + * set retrieves the values of this object into a float array. + * + * @param matrix + * the matrix to set the values into. + * @param rowMajor + * whether the outgoing data is in row or column major order. + * @throws Exception + */ + public void get(float[] matrix, boolean rowMajor) throws Exception { + if (matrix.length != 16) + throw new Exception("Array must be of size 16."); + + if (rowMajor) { + matrix[0] = m00; + matrix[1] = m01; + matrix[2] = m02; + matrix[3] = m03; + matrix[4] = m10; + matrix[5] = m11; + matrix[6] = m12; + matrix[7] = m13; + matrix[8] = m20; + matrix[9] = m21; + matrix[10] = m22; + matrix[11] = m23; + matrix[12] = m30; + matrix[13] = m31; + matrix[14] = m32; + matrix[15] = m33; + } else { + matrix[0] = m00; + matrix[4] = m01; + matrix[8] = m02; + matrix[12] = m03; + matrix[1] = m10; + matrix[5] = m11; + matrix[9] = m12; + matrix[13] = m13; + matrix[2] = m20; + matrix[6] = m21; + matrix[10] = m22; + matrix[14] = m23; + matrix[3] = m30; + matrix[7] = m31; + matrix[11] = m32; + matrix[15] = m33; + } + } + + /** + * get retrieves a value from the matrix at the given position. + * If the position is invalid a Exception is thrown. + * + * @param i + * the row index. + * @param j + * the column index. + * @return the value at (i, j). + * @throws Exception + */ + public float get(int i, int j) throws Exception { + switch (i) { + case 0: + switch (j) { + case 0: + return m00; + case 1: + return m01; + case 2: + return m02; + case 3: + return m03; + } + case 1: + switch (j) { + case 0: + return m10; + case 1: + return m11; + case 2: + return m12; + case 3: + return m13; + } + case 2: + switch (j) { + case 0: + return m20; + case 1: + return m21; + case 2: + return m22; + case 3: + return m23; + } + case 3: + switch (j) { + case 0: + return m30; + case 1: + return m31; + case 2: + return m32; + case 3: + return m33; + } + } + + throw new Exception("Invalid indices into matrix."); + } + + /** + * getColumn returns one of three columns specified by the + * parameter. This column is returned as a float array of length 4. + * + * @param i + * the column to retrieve. Must be between 0 and 3. + * @return the column specified by the index. + * @throws Exception + */ + public float[] getColumn(int i) throws Exception { + return getColumn(i, null); + } + + /** + * getColumn returns one of three columns specified by the + * parameter. This column is returned as a float[4]. + * + * @param i + * the column to retrieve. Must be between 0 and 3. + * @param store + * the float array to store the result in. if null, a new one is + * created. + * @return the column specified by the index. + */ + public float[] getColumn(int i, float[] store) throws Exception { + if (store == null) + store = new float[4]; + switch (i) { + case 0: + store[0] = m00; + store[1] = m10; + store[2] = m20; + store[3] = m30; + break; + case 1: + store[0] = m01; + store[1] = m11; + store[2] = m21; + store[3] = m31; + break; + case 2: + store[0] = m02; + store[1] = m12; + store[2] = m22; + store[3] = m32; + break; + case 3: + store[0] = m03; + store[1] = m13; + store[2] = m23; + store[3] = m33; + break; + default: + throw new Exception("Invalid column index. " + i); + } + return store; + } + + /** + * + * setColumn sets a particular column of this matrix to that + * represented by the provided vector. + * + * @param i + * the column to set. + * @param column + * the data to set. + */ + public void setColumn(int i, float[] column) throws Exception { + + if (column == null) { + return; + } + switch (i) { + case 0: + m00 = column[0]; + m10 = column[1]; + m20 = column[2]; + m30 = column[3]; + break; + case 1: + m01 = column[0]; + m11 = column[1]; + m21 = column[2]; + m31 = column[3]; + break; + case 2: + m02 = column[0]; + m12 = column[1]; + m22 = column[2]; + m32 = column[3]; + break; + case 3: + m03 = column[0]; + m13 = column[1]; + m23 = column[2]; + m33 = column[3]; + break; + default: + throw new Exception("Invalid column index. " + i); + } + } + + /** + * set places a given value into the matrix at the given + * position. If the position is invalid a Exception is thrown. + * + * @param i + * the row index. + * @param j + * the column index. + * @param value + * the value for (i, j). + */ + public void set(int i, int j, float value) throws Exception { + switch (i) { + case 0: + switch (j) { + case 0: + m00 = value; + return; + case 1: + m01 = value; + return; + case 2: + m02 = value; + return; + case 3: + m03 = value; + return; + } + case 1: + switch (j) { + case 0: + m10 = value; + return; + case 1: + m11 = value; + return; + case 2: + m12 = value; + return; + case 3: + m13 = value; + return; + } + case 2: + switch (j) { + case 0: + m20 = value; + return; + case 1: + m21 = value; + return; + case 2: + m22 = value; + return; + case 3: + m23 = value; + return; + } + case 3: + switch (j) { + case 0: + m30 = value; + return; + case 1: + m31 = value; + return; + case 2: + m32 = value; + return; + case 3: + m33 = value; + return; + } + } + throw new Exception("Invalid indices into matrix."); + } + + /** + * set sets the values of this matrix from an array of values. + * + * @param matrix + * the matrix to set the value to. + * @throws Exception + * if the array is not of size 16. + */ + public void set(float[][] matrix) throws Exception { + if (matrix.length != 4 || matrix[0].length != 4) { + throw new Exception("Array must be of size 16."); + } + + m00 = matrix[0][0]; + m01 = matrix[0][1]; + m02 = matrix[0][2]; + m03 = matrix[0][3]; + m10 = matrix[1][0]; + m11 = matrix[1][1]; + m12 = matrix[1][2]; + m13 = matrix[1][3]; + m20 = matrix[2][0]; + m21 = matrix[2][1]; + m22 = matrix[2][2]; + m23 = matrix[2][3]; + m30 = matrix[3][0]; + m31 = matrix[3][1]; + m32 = matrix[3][2]; + m33 = matrix[3][3]; + } + + /** + * set sets the values of this matrix from another matrix. + * + * @param matrix + * the matrix to read the value from. + */ + public Matrix4f set(Matrix4f matrix) { + m00 = matrix.m00; + m01 = matrix.m01; + m02 = matrix.m02; + m03 = matrix.m03; + m10 = matrix.m10; + m11 = matrix.m11; + m12 = matrix.m12; + m13 = matrix.m13; + m20 = matrix.m20; + m21 = matrix.m21; + m22 = matrix.m22; + m23 = matrix.m23; + m30 = matrix.m30; + m31 = matrix.m31; + m32 = matrix.m32; + m33 = matrix.m33; + return this; + } + + /** + * set sets the values of this matrix from an array of values + * assuming that the data is rowMajor order; + * + * @param matrix + * the matrix to set the value to. + * @throws Exception + */ + public void set(float[] matrix) throws Exception { + set(matrix, true); + } + + /** + * set sets the values of this matrix from an array of values; + * + * @param matrix + * the matrix to set the value to. + * @param rowMajor + * whether the incoming data is in row or column major order. + * @throws Exception + */ + public void set(float[] matrix, boolean rowMajor) throws Exception { + if (matrix.length != 16) + throw new Exception("Array must be of size 16."); + + if (rowMajor) { + m00 = matrix[0]; + m01 = matrix[1]; + m02 = matrix[2]; + m03 = matrix[3]; + m10 = matrix[4]; + m11 = matrix[5]; + m12 = matrix[6]; + m13 = matrix[7]; + m20 = matrix[8]; + m21 = matrix[9]; + m22 = matrix[10]; + m23 = matrix[11]; + m30 = matrix[12]; + m31 = matrix[13]; + m32 = matrix[14]; + m33 = matrix[15]; + } else { + m00 = matrix[0]; + m01 = matrix[4]; + m02 = matrix[8]; + m03 = matrix[12]; + m10 = matrix[1]; + m11 = matrix[5]; + m12 = matrix[9]; + m13 = matrix[13]; + m20 = matrix[2]; + m21 = matrix[6]; + m22 = matrix[10]; + m23 = matrix[14]; + m30 = matrix[3]; + m31 = matrix[7]; + m32 = matrix[11]; + m33 = matrix[15]; + } + } + + public Matrix4f transpose() throws Exception { + float[] tmp = new float[16]; + get(tmp, true); + return new Matrix4f(tmp); + } + + /** + * transpose locally transposes this Matrix. + * + * @return this object for chaining. + */ + public Matrix4f transposeLocal() { + float tmp = m01; + m01 = m10; + m10 = tmp; + + tmp = m02; + m02 = m20; + m20 = tmp; + + tmp = m03; + m03 = m30; + m30 = tmp; + + tmp = m12; + m12 = m21; + m21 = tmp; + + tmp = m13; + m13 = m31; + m31 = tmp; + + tmp = m23; + m23 = m32; + m32 = tmp; + + return this; + } + + /** + * fillFloatBuffer fills a FloatBuffer object with the matrix + * data. + * + * @param fb + * the buffer to fill, must be correct size + * @return matrix data as a FloatBuffer. + */ + public FloatBuffer fillFloatBuffer(FloatBuffer fb) { + return fillFloatBuffer(fb, false); + } + + /** + * fillFloatBuffer fills a FloatBuffer object with the matrix + * data. + * + * @param fb + * the buffer to fill, starting at current position. Must have + * room for 16 more floats. + * @param columnMajor + * if true, this buffer should be filled with column major data, + * otherwise it will be filled row major. + * @return matrix data as a FloatBuffer. (position is advanced by 16 and any + * limit set is not changed). + */ + public FloatBuffer fillFloatBuffer(FloatBuffer fb, boolean columnMajor) { + if (columnMajor) { + fb.put(m00).put(m10).put(m20).put(m30); + fb.put(m01).put(m11).put(m21).put(m31); + fb.put(m02).put(m12).put(m22).put(m32); + fb.put(m03).put(m13).put(m23).put(m33); + } else { + fb.put(m00).put(m01).put(m02).put(m03); + fb.put(m10).put(m11).put(m12).put(m13); + fb.put(m20).put(m21).put(m22).put(m23); + fb.put(m30).put(m31).put(m32).put(m33); + } + return fb; + } + + /** + * readFloatBuffer reads value for this matrix from a + * FloatBuffer. + * + * @param fb + * the buffer to read from, must be correct size + * @return this data as a FloatBuffer. + */ + public Matrix4f readFloatBuffer(FloatBuffer fb) { + return readFloatBuffer(fb, false); + } + + /** + * readFloatBuffer reads value for this matrix from a + * FloatBuffer. + * + * @param fb + * the buffer to read from, must be correct size + * @param columnMajor + * if true, this buffer should be filled with column major data, + * otherwise it will be filled row major. + * @return this data as a FloatBuffer. + */ + public Matrix4f readFloatBuffer(FloatBuffer fb, boolean columnMajor) { + + if (columnMajor) { + m00 = fb.get(); + m10 = fb.get(); + m20 = fb.get(); + m30 = fb.get(); + m01 = fb.get(); + m11 = fb.get(); + m21 = fb.get(); + m31 = fb.get(); + m02 = fb.get(); + m12 = fb.get(); + m22 = fb.get(); + m32 = fb.get(); + m03 = fb.get(); + m13 = fb.get(); + m23 = fb.get(); + m33 = fb.get(); + } else { + m00 = fb.get(); + m01 = fb.get(); + m02 = fb.get(); + m03 = fb.get(); + m10 = fb.get(); + m11 = fb.get(); + m12 = fb.get(); + m13 = fb.get(); + m20 = fb.get(); + m21 = fb.get(); + m22 = fb.get(); + m23 = fb.get(); + m30 = fb.get(); + m31 = fb.get(); + m32 = fb.get(); + m33 = fb.get(); + } + return this; + } + + /** + * Legacy wrapper. This name implies that an identity matrix is "loaded", + * but one is not. Instead, the elements of 'this' identity are set. + */ + public void loadIdentity() { + setIdentity(); + } + + /** + * Sets this matrix to the identity matrix, namely all zeros with ones along + * the diagonal. + * + */ + public void setIdentity() { + m01 = m02 = m03 = m10 = m12 = m13 = m20 = m21 = m23 = m30 = m31 = m32 = 0.0f; + m00 = m11 = m22 = m33 = 1.0f; + } + + /** + * fromAngleAxis sets this matrix4f to the values specified by + * an angle and an axis of rotation. This method creates an object, so use + * fromAngleNormalAxis if your axis is already normalized. + * + * @param angle + * the angle to rotate (in radians). + * @param axis + * the axis of rotation. + */ + public void fromAngleAxis(float angle, Vector3f axis) { + Vector3f normAxis = axis.normalize(); + fromAngleNormalAxis(angle, normAxis); + } + + /** + * fromAngleNormalAxis sets this matrix4f to the values + * specified by an angle and a normalized axis of rotation. + * + * @param angle + * the angle to rotate (in radians). + * @param axis + * the axis of rotation (already normalized). + */ + public void fromAngleNormalAxis(float angle, Vector3f axis) { + zero(); + m33 = 1; + + float fCos = FastMath.cos(angle); + float fSin = FastMath.sin(angle); + float fOneMinusCos = ((float) 1.0) - fCos; + float fX2 = axis.x * axis.x; + float fY2 = axis.y * axis.y; + float fZ2 = axis.z * axis.z; + float fXYM = axis.x * axis.y * fOneMinusCos; + float fXZM = axis.x * axis.z * fOneMinusCos; + float fYZM = axis.y * axis.z * fOneMinusCos; + float fXSin = axis.x * fSin; + float fYSin = axis.y * fSin; + float fZSin = axis.z * fSin; + + m00 = fX2 * fOneMinusCos + fCos; + m01 = fXYM - fZSin; + m02 = fXZM + fYSin; + m10 = fXYM + fZSin; + m11 = fY2 * fOneMinusCos + fCos; + m12 = fYZM - fXSin; + m20 = fXZM - fYSin; + m21 = fYZM + fXSin; + m22 = fZ2 * fOneMinusCos + fCos; + } + + /** + * mult multiplies this matrix by a scalar. + * + * @param scalar + * the scalar to multiply this matrix by. + */ + public void multLocal(float scalar) { + m00 *= scalar; + m01 *= scalar; + m02 *= scalar; + m03 *= scalar; + m10 *= scalar; + m11 *= scalar; + m12 *= scalar; + m13 *= scalar; + m20 *= scalar; + m21 *= scalar; + m22 *= scalar; + m23 *= scalar; + m30 *= scalar; + m31 *= scalar; + m32 *= scalar; + m33 *= scalar; + } + + public Matrix4f mult(float scalar) { + Matrix4f out = new Matrix4f(); + out.set(this); + out.multLocal(scalar); + return out; + } + + public Matrix4f mult(float scalar, Matrix4f store) { + store.set(this); + store.multLocal(scalar); + return store; + } + + /** + * mult multiplies this matrix with another matrix. The result + * matrix will then be returned. This matrix will be on the left hand side, + * while the parameter matrix will be on the right. + * + * @param in2 + * the matrix to multiply this matrix by. + * @return the resultant matrix + */ + public Matrix4f mult(Matrix4f in2) { + return mult(in2, null); + } + + /** + * mult multiplies this matrix with another matrix. The result + * matrix will then be returned. This matrix will be on the left hand side, + * while the parameter matrix will be on the right. + * + * @param in2 + * the matrix to multiply this matrix by. + * @param store + * where to store the result. It is safe for in2 and store to be + * the same object. + * @return the resultant matrix + */ + public Matrix4f mult(Matrix4f in2, Matrix4f store) { + if (store == null) + store = new Matrix4f(); + + float temp00, temp01, temp02, temp03; + float temp10, temp11, temp12, temp13; + float temp20, temp21, temp22, temp23; + float temp30, temp31, temp32, temp33; + + temp00 = m00 * in2.m00 + m01 * in2.m10 + m02 * in2.m20 + m03 * in2.m30; + temp01 = m00 * in2.m01 + m01 * in2.m11 + m02 * in2.m21 + m03 * in2.m31; + temp02 = m00 * in2.m02 + m01 * in2.m12 + m02 * in2.m22 + m03 * in2.m32; + temp03 = m00 * in2.m03 + m01 * in2.m13 + m02 * in2.m23 + m03 * in2.m33; + + temp10 = m10 * in2.m00 + m11 * in2.m10 + m12 * in2.m20 + m13 * in2.m30; + temp11 = m10 * in2.m01 + m11 * in2.m11 + m12 * in2.m21 + m13 * in2.m31; + temp12 = m10 * in2.m02 + m11 * in2.m12 + m12 * in2.m22 + m13 * in2.m32; + temp13 = m10 * in2.m03 + m11 * in2.m13 + m12 * in2.m23 + m13 * in2.m33; + + temp20 = m20 * in2.m00 + m21 * in2.m10 + m22 * in2.m20 + m23 * in2.m30; + temp21 = m20 * in2.m01 + m21 * in2.m11 + m22 * in2.m21 + m23 * in2.m31; + temp22 = m20 * in2.m02 + m21 * in2.m12 + m22 * in2.m22 + m23 * in2.m32; + temp23 = m20 * in2.m03 + m21 * in2.m13 + m22 * in2.m23 + m23 * in2.m33; + + temp30 = m30 * in2.m00 + m31 * in2.m10 + m32 * in2.m20 + m33 * in2.m30; + temp31 = m30 * in2.m01 + m31 * in2.m11 + m32 * in2.m21 + m33 * in2.m31; + temp32 = m30 * in2.m02 + m31 * in2.m12 + m32 * in2.m22 + m33 * in2.m32; + temp33 = m30 * in2.m03 + m31 * in2.m13 + m32 * in2.m23 + m33 * in2.m33; + + store.m00 = temp00; + store.m01 = temp01; + store.m02 = temp02; + store.m03 = temp03; + store.m10 = temp10; + store.m11 = temp11; + store.m12 = temp12; + store.m13 = temp13; + store.m20 = temp20; + store.m21 = temp21; + store.m22 = temp22; + store.m23 = temp23; + store.m30 = temp30; + store.m31 = temp31; + store.m32 = temp32; + store.m33 = temp33; + + return store; + } + + /** + * mult multiplies this matrix with another matrix. The results + * are stored internally and a handle to this matrix will then be returned. + * This matrix will be on the left hand side, while the parameter matrix + * will be on the right. + * + * @param in2 + * the matrix to multiply this matrix by. + * @return the resultant matrix + */ + public Matrix4f multLocal(Matrix4f in2) { + + return mult(in2, this); + } + + /** + * mult multiplies a vector about a rotation matrix. The + * resulting vector is returned as a new Vector3f. + * + * @param vec + * vec to multiply against. + * @return the rotated vector. + */ + public Vector3f mult(Vector3f vec) { + return mult(vec, null); + } + + /** + * mult multiplies a vector about a rotation matrix and adds + * translation. The resulting vector is returned. + * + * @param vec + * vec to multiply against. + * @param store + * a vector to store the result in. Created if null is passed. + * @return the rotated vector. + */ + public Vector3f mult(Vector3f vec, Vector3f store) { + if (store == null) + store = new Vector3f(); + + float vx = vec.x, vy = vec.y, vz = vec.z; + store.x = m00 * vx + m01 * vy + m02 * vz + m03; + store.y = m10 * vx + m11 * vy + m12 * vz + m13; + store.z = m20 * vx + m21 * vy + m22 * vz + m23; + + return store; + } + + /** + * mult multiplies a vector about a rotation matrix. The + * resulting vector is returned. + * + * @param vec + * vec to multiply against. + * @param store + * a vector to store the result in. created if null is passed. + * @return the rotated vector. + */ + public Vector3f multAcross(Vector3f vec, Vector3f store) { + if (null == vec) { + return null; + } + if (store == null) + store = new Vector3f(); + + float vx = vec.x, vy = vec.y, vz = vec.z; + store.x = m00 * vx + m10 * vy + m20 * vz + m30 * 1; + store.y = m01 * vx + m11 * vy + m21 * vz + m31 * 1; + store.z = m02 * vx + m12 * vy + m22 * vz + m32 * 1; + + return store; + } + + /** + * mult multiplies a quaternion about a matrix. The resulting + * vector is returned. + * + * @param vec + * vec to multiply against. + * @param store + * a quaternion to store the result in. created if null is + * passed. + * @return store = this * vec + */ + public Quaternion mult(Quaternion vec, Quaternion store) { + + if (null == vec) { + return null; + } + if (store == null) + store = new Quaternion(); + + float x = m00 * vec.x + m10 * vec.y + m20 * vec.z + m30 * vec.w; + float y = m01 * vec.x + m11 * vec.y + m21 * vec.z + m31 * vec.w; + float z = m02 * vec.x + m12 * vec.y + m22 * vec.z + m32 * vec.w; + float w = m03 * vec.x + m13 * vec.y + m23 * vec.z + m33 * vec.w; + store.x = x; + store.y = y; + store.z = z; + store.w = w; + + return store; + } + + /** + * mult multiplies an array of 4 floats against this rotation + * matrix. The results are stored directly in the array. (vec4f x mat4f) + * + * @param vec4f + * float array (size 4) to multiply against the matrix. + * @return the vec4f for chaining. + */ + public float[] mult(float[] vec4f) { + if (null == vec4f || vec4f.length != 4) { + return null; + } + + float x = vec4f[0], y = vec4f[1], z = vec4f[2], w = vec4f[3]; + + vec4f[0] = m00 * x + m01 * y + m02 * z + m03 * w; + vec4f[1] = m10 * x + m11 * y + m12 * z + m13 * w; + vec4f[2] = m20 * x + m21 * y + m22 * z + m23 * w; + vec4f[3] = m30 * x + m31 * y + m32 * z + m33 * w; + + return vec4f; + } + + /** + * mult multiplies an array of 4 floats against this rotation + * matrix. The results are stored directly in the array. (vec4f x mat4f) + * + * @param vec4f + * float array (size 4) to multiply against the matrix. + * @return the vec4f for chaining. + */ + public float[] multAcross(float[] vec4f) { + if (null == vec4f || vec4f.length != 4) { + return null; + } + + float x = vec4f[0], y = vec4f[1], z = vec4f[2], w = vec4f[3]; + + vec4f[0] = m00 * x + m10 * y + m20 * z + m30 * w; + vec4f[1] = m01 * x + m11 * y + m21 * z + m31 * w; + vec4f[2] = m02 * x + m12 * y + m22 * z + m32 * w; + vec4f[3] = m03 * x + m13 * y + m23 * z + m33 * w; + + return vec4f; + } + + /** + * Inverts this matrix as a new Matrix4f. + * + * @return The new inverse matrix + */ + public Matrix4f invert() { + return invert(null); + } + + /** + * Inverts this matrix and stores it in the given store. + * + * @return The store + */ + public Matrix4f invert(Matrix4f store) { + if (store == null) + store = new Matrix4f(); + + float fA0 = m00 * m11 - m01 * m10; + float fA1 = m00 * m12 - m02 * m10; + float fA2 = m00 * m13 - m03 * m10; + float fA3 = m01 * m12 - m02 * m11; + float fA4 = m01 * m13 - m03 * m11; + float fA5 = m02 * m13 - m03 * m12; + float fB0 = m20 * m31 - m21 * m30; + float fB1 = m20 * m32 - m22 * m30; + float fB2 = m20 * m33 - m23 * m30; + float fB3 = m21 * m32 - m22 * m31; + float fB4 = m21 * m33 - m23 * m31; + float fB5 = m22 * m33 - m23 * m32; + float fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0; + + if (FastMath.abs(fDet) <= 0) + return store.zero(); + + store.m00 = +m11 * fB5 - m12 * fB4 + m13 * fB3; + store.m10 = -m10 * fB5 + m12 * fB2 - m13 * fB1; + store.m20 = +m10 * fB4 - m11 * fB2 + m13 * fB0; + store.m30 = -m10 * fB3 + m11 * fB1 - m12 * fB0; + store.m01 = -m01 * fB5 + m02 * fB4 - m03 * fB3; + store.m11 = +m00 * fB5 - m02 * fB2 + m03 * fB1; + store.m21 = -m00 * fB4 + m01 * fB2 - m03 * fB0; + store.m31 = +m00 * fB3 - m01 * fB1 + m02 * fB0; + store.m02 = +m31 * fA5 - m32 * fA4 + m33 * fA3; + store.m12 = -m30 * fA5 + m32 * fA2 - m33 * fA1; + store.m22 = +m30 * fA4 - m31 * fA2 + m33 * fA0; + store.m32 = -m30 * fA3 + m31 * fA1 - m32 * fA0; + store.m03 = -m21 * fA5 + m22 * fA4 - m23 * fA3; + store.m13 = +m20 * fA5 - m22 * fA2 + m23 * fA1; + store.m23 = -m20 * fA4 + m21 * fA2 - m23 * fA0; + store.m33 = +m20 * fA3 - m21 * fA1 + m22 * fA0; + + float fInvDet = 1.0f / fDet; + store.multLocal(fInvDet); + + return store; + } + + /** + * Inverts this matrix locally. + * + * @return this + */ + public Matrix4f invertLocal() { + + float fA0 = m00 * m11 - m01 * m10; + float fA1 = m00 * m12 - m02 * m10; + float fA2 = m00 * m13 - m03 * m10; + float fA3 = m01 * m12 - m02 * m11; + float fA4 = m01 * m13 - m03 * m11; + float fA5 = m02 * m13 - m03 * m12; + float fB0 = m20 * m31 - m21 * m30; + float fB1 = m20 * m32 - m22 * m30; + float fB2 = m20 * m33 - m23 * m30; + float fB3 = m21 * m32 - m22 * m31; + float fB4 = m21 * m33 - m23 * m31; + float fB5 = m22 * m33 - m23 * m32; + float fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0; + + if (FastMath.abs(fDet) <= FastMath.FLT_EPSILON) + return zero(); + + float f00 = +m11 * fB5 - m12 * fB4 + m13 * fB3; + float f10 = -m10 * fB5 + m12 * fB2 - m13 * fB1; + float f20 = +m10 * fB4 - m11 * fB2 + m13 * fB0; + float f30 = -m10 * fB3 + m11 * fB1 - m12 * fB0; + float f01 = -m01 * fB5 + m02 * fB4 - m03 * fB3; + float f11 = +m00 * fB5 - m02 * fB2 + m03 * fB1; + float f21 = -m00 * fB4 + m01 * fB2 - m03 * fB0; + float f31 = +m00 * fB3 - m01 * fB1 + m02 * fB0; + float f02 = +m31 * fA5 - m32 * fA4 + m33 * fA3; + float f12 = -m30 * fA5 + m32 * fA2 - m33 * fA1; + float f22 = +m30 * fA4 - m31 * fA2 + m33 * fA0; + float f32 = -m30 * fA3 + m31 * fA1 - m32 * fA0; + float f03 = -m21 * fA5 + m22 * fA4 - m23 * fA3; + float f13 = +m20 * fA5 - m22 * fA2 + m23 * fA1; + float f23 = -m20 * fA4 + m21 * fA2 - m23 * fA0; + float f33 = +m20 * fA3 - m21 * fA1 + m22 * fA0; + + m00 = f00; + m01 = f01; + m02 = f02; + m03 = f03; + m10 = f10; + m11 = f11; + m12 = f12; + m13 = f13; + m20 = f20; + m21 = f21; + m22 = f22; + m23 = f23; + m30 = f30; + m31 = f31; + m32 = f32; + m33 = f33; + + float fInvDet = 1.0f / fDet; + multLocal(fInvDet); + + return this; + } + + /** + * Returns a new matrix representing the adjoint of this matrix. + * + * @return The adjoint matrix + */ + public Matrix4f adjoint() { + return adjoint(null); + } + + /** + * Places the adjoint of this matrix in store (creates store if null.) + * + * @param store + * The matrix to store the result in. If null, a new matrix is + * created. + * @return store + */ + public Matrix4f adjoint(Matrix4f store) { + if (store == null) + store = new Matrix4f(); + + float fA0 = m00 * m11 - m01 * m10; + float fA1 = m00 * m12 - m02 * m10; + float fA2 = m00 * m13 - m03 * m10; + float fA3 = m01 * m12 - m02 * m11; + float fA4 = m01 * m13 - m03 * m11; + float fA5 = m02 * m13 - m03 * m12; + float fB0 = m20 * m31 - m21 * m30; + float fB1 = m20 * m32 - m22 * m30; + float fB2 = m20 * m33 - m23 * m30; + float fB3 = m21 * m32 - m22 * m31; + float fB4 = m21 * m33 - m23 * m31; + float fB5 = m22 * m33 - m23 * m32; + + store.m00 = +m11 * fB5 - m12 * fB4 + m13 * fB3; + store.m10 = -m10 * fB5 + m12 * fB2 - m13 * fB1; + store.m20 = +m10 * fB4 - m11 * fB2 + m13 * fB0; + store.m30 = -m10 * fB3 + m11 * fB1 - m12 * fB0; + store.m01 = -m01 * fB5 + m02 * fB4 - m03 * fB3; + store.m11 = +m00 * fB5 - m02 * fB2 + m03 * fB1; + store.m21 = -m00 * fB4 + m01 * fB2 - m03 * fB0; + store.m31 = +m00 * fB3 - m01 * fB1 + m02 * fB0; + store.m02 = +m31 * fA5 - m32 * fA4 + m33 * fA3; + store.m12 = -m30 * fA5 + m32 * fA2 - m33 * fA1; + store.m22 = +m30 * fA4 - m31 * fA2 + m33 * fA0; + store.m32 = -m30 * fA3 + m31 * fA1 - m32 * fA0; + store.m03 = -m21 * fA5 + m22 * fA4 - m23 * fA3; + store.m13 = +m20 * fA5 - m22 * fA2 + m23 * fA1; + store.m23 = -m20 * fA4 + m21 * fA2 - m23 * fA0; + store.m33 = +m20 * fA3 - m21 * fA1 + m22 * fA0; + + return store; + } + + /** + * determinant generates the determinate of this matrix. + * + * @return the determinate + */ + public float determinant() { + float fA0 = m00 * m11 - m01 * m10; + float fA1 = m00 * m12 - m02 * m10; + float fA2 = m00 * m13 - m03 * m10; + float fA3 = m01 * m12 - m02 * m11; + float fA4 = m01 * m13 - m03 * m11; + float fA5 = m02 * m13 - m03 * m12; + float fB0 = m20 * m31 - m21 * m30; + float fB1 = m20 * m32 - m22 * m30; + float fB2 = m20 * m33 - m23 * m30; + float fB3 = m21 * m32 - m22 * m31; + float fB4 = m21 * m33 - m23 * m31; + float fB5 = m22 * m33 - m23 * m32; + return fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0; + } + + /** + * Sets all of the values in this matrix to zero. + * + * @return this matrix + */ + public Matrix4f zero() { + m00 = m01 = m02 = m03 = m10 = m11 = m12 = m13 = m20 = m21 = m22 = m23 = m30 = m31 = m32 = m33 = 0.0f; + return this; + } + + public Matrix4f add(Matrix4f mat) { + Matrix4f result = new Matrix4f(); + result.m00 = this.m00 + mat.m00; + result.m01 = this.m01 + mat.m01; + result.m02 = this.m02 + mat.m02; + result.m03 = this.m03 + mat.m03; + result.m10 = this.m10 + mat.m10; + result.m11 = this.m11 + mat.m11; + result.m12 = this.m12 + mat.m12; + result.m13 = this.m13 + mat.m13; + result.m20 = this.m20 + mat.m20; + result.m21 = this.m21 + mat.m21; + result.m22 = this.m22 + mat.m22; + result.m23 = this.m23 + mat.m23; + result.m30 = this.m30 + mat.m30; + result.m31 = this.m31 + mat.m31; + result.m32 = this.m32 + mat.m32; + result.m33 = this.m33 + mat.m33; + return result; + } + + /** + * add adds the values of a parameter matrix to this matrix. + * + * @param mat + * the matrix to add to this. + */ + public void addLocal(Matrix4f mat) { + m00 += mat.m00; + m01 += mat.m01; + m02 += mat.m02; + m03 += mat.m03; + m10 += mat.m10; + m11 += mat.m11; + m12 += mat.m12; + m13 += mat.m13; + m20 += mat.m20; + m21 += mat.m21; + m22 += mat.m22; + m23 += mat.m23; + m30 += mat.m30; + m31 += mat.m31; + m32 += mat.m32; + m33 += mat.m33; + } + + public Vector3f toTranslationVector() { + return new Vector3f(m03, m13, m23); + } + + public void toTranslationVector(Vector3f vector) { + vector.set(m03, m13, m23); + } + + public Quaternion toRotationQuat() { + Quaternion quat = new Quaternion(); + quat.fromRotationMatrix(toRotationMatrix()); + return quat; + } + + public void toRotationQuat(Quaternion q) { + q.fromRotationMatrix(toRotationMatrix()); + } + + public Matrix3f toRotationMatrix() { + return new Matrix3f(m00, m01, m02, m10, m11, m12, m20, m21, m22); + + } + + public void toRotationMatrix(Matrix3f mat) { + mat.m00 = m00; + mat.m01 = m01; + mat.m02 = m02; + mat.m10 = m10; + mat.m11 = m11; + mat.m12 = m12; + mat.m20 = m20; + mat.m21 = m21; + mat.m22 = m22; + + } + + /** + * setTranslation will set the matrix's translation values. + * + * @param translation + * the new values for the translation. + * @throws Exception + * if translation is not size 3. + */ + public void setTranslation(float[] translation) throws Exception { + if (translation.length != 3) { + throw new Exception("Translation size must be 3."); + } + m03 = translation[0]; + m13 = translation[1]; + m23 = translation[2]; + } + + /** + * setTranslation will set the matrix's translation values. + * + * @param x + * value of the translation on the x axis + * @param y + * value of the translation on the y axis + * @param z + * value of the translation on the z axis + */ + public void setTranslation(float x, float y, float z) { + m03 = x; + m13 = y; + m23 = z; + } + + /** + * setTranslation will set the matrix's translation values. + * + * @param translation + * the new values for the translation. + */ + public void setTranslation(Vector3f translation) { + m03 = translation.x; + m13 = translation.y; + m23 = translation.z; + } + + /** + * setInverseTranslation will set the matrix's inverse + * translation values. + * + * @param translation + * the new values for the inverse translation. + * @throws Exception + * if translation is not size 3. + */ + public void setInverseTranslation(float[] translation) throws Exception { + if (translation.length != 3) { + throw new Exception("Translation size must be 3."); + } + m03 = -translation[0]; + m13 = -translation[1]; + m23 = -translation[2]; + } + + /** + * angleRotation sets this matrix to that of a rotation about + * three axes (x, y, z). Where each axis has a specified rotation in + * degrees. These rotations are expressed in a single Vector3f + * object. + * + * @param angles + * the angles to rotate. + */ + public void angleRotation(Vector3f angles) { + float angle; + float sr, sp, sy, cr, cp, cy; + + angle = (angles.z * FastMath.DEG_TO_RAD); + sy = FastMath.sin(angle); + cy = FastMath.cos(angle); + angle = (angles.y * FastMath.DEG_TO_RAD); + sp = FastMath.sin(angle); + cp = FastMath.cos(angle); + angle = (angles.x * FastMath.DEG_TO_RAD); + sr = FastMath.sin(angle); + cr = FastMath.cos(angle); + + // matrix = (Z * Y) * X + m00 = cp * cy; + m10 = cp * sy; + m20 = -sp; + m01 = sr * sp * cy + cr * -sy; + m11 = sr * sp * sy + cr * cy; + m21 = sr * cp; + m02 = (cr * sp * cy + -sr * -sy); + m12 = (cr * sp * sy + -sr * cy); + m22 = cr * cp; + m03 = 0.0f; + m13 = 0.0f; + m23 = 0.0f; + } + + /** + * setRotationQuaternion builds a rotation from a + * Quaternion. + * + * @param quat + * the quaternion to build the rotation from. + * @throws NullPointerException + * if quat is null. + */ + public void setRotationQuaternion(Quaternion quat) { + quat.toRotationMatrix(this); + } + + /** + * setInverseRotationRadians builds an inverted rotation from + * Euler angles that are in radians. + * + * @param angles + * the Euler angles in radians. + * @throws Exception + * if angles is not size 3. + */ + public void setInverseRotationRadians(float[] angles) throws Exception { + if (angles.length != 3) { + throw new Exception("Angles must be of size 3."); + } + double cr = FastMath.cos(angles[0]); + double sr = FastMath.sin(angles[0]); + double cp = FastMath.cos(angles[1]); + double sp = FastMath.sin(angles[1]); + double cy = FastMath.cos(angles[2]); + double sy = FastMath.sin(angles[2]); + + m00 = (float) (cp * cy); + m10 = (float) (cp * sy); + m20 = (float) (-sp); + + double srsp = sr * sp; + double crsp = cr * sp; + + m01 = (float) (srsp * cy - cr * sy); + m11 = (float) (srsp * sy + cr * cy); + m21 = (float) (sr * cp); + + m02 = (float) (crsp * cy + sr * sy); + m12 = (float) (crsp * sy - sr * cy); + m22 = (float) (cr * cp); + } + + /** + * setInverseRotationDegrees builds an inverted rotation from + * Euler angles that are in degrees. + * + * @param angles + * the Euler angles in degrees. + * @throws Exception + * if angles is not size 3. + */ + public void setInverseRotationDegrees(float[] angles) throws Exception { + if (angles.length != 3) { + throw new Exception("Angles must be of size 3."); + } + float vec[] = new float[3]; + vec[0] = (angles[0] * FastMath.RAD_TO_DEG); + vec[1] = (angles[1] * FastMath.RAD_TO_DEG); + vec[2] = (angles[2] * FastMath.RAD_TO_DEG); + setInverseRotationRadians(vec); + } + + /** + * + * inverseTranslateVect translates a given Vector3f by the + * translation part of this matrix. + * + * @param vec + * the Vector3f data to be translated. + * @throws Exception + * if the size of the Vector3f is not 3. + */ + public void inverseTranslateVect(float[] vec) throws Exception { + if (vec.length != 3) { + throw new Exception("vec must be of size 3."); + } + + vec[0] -= m03; + vec[1] -= m13; + vec[2] -= m23; + } + + /** + * + * inverseTranslateVect translates a given Vector3f by the + * translation part of this matrix. + * + * @param data + * the Vector3f to be translated. + * @throws Exception + * if the size of the Vector3f is not 3. + */ + public void inverseTranslateVect(Vector3f data) { + data.x -= m03; + data.y -= m13; + data.z -= m23; + } + + /** + * + * inverseTranslateVect translates a given Vector3f by the + * translation part of this matrix. + * + * @param data + * the Vector3f to be translated. + * @throws Exception + * if the size of the Vector3f is not 3. + */ + public void translateVect(Vector3f data) { + data.x += m03; + data.y += m13; + data.z += m23; + } + + /** + * + * inverseRotateVect rotates a given Vector3f by the rotation + * part of this matrix. + * + * @param vec + * the Vector3f to be rotated. + */ + public void inverseRotateVect(Vector3f vec) { + float vx = vec.x, vy = vec.y, vz = vec.z; + + vec.x = vx * m00 + vy * m10 + vz * m20; + vec.y = vx * m01 + vy * m11 + vz * m21; + vec.z = vx * m02 + vy * m12 + vz * m22; + } + + public void rotateVect(Vector3f vec) { + float vx = vec.x, vy = vec.y, vz = vec.z; + + vec.x = vx * m00 + vy * m01 + vz * m02; + vec.y = vx * m10 + vy * m11 + vz * m12; + vec.z = vx * m20 + vy * m21 + vz * m22; + } + + /** + * toString returns the string representation of this object. + * It is in a format of a 4x4 matrix. For example, an identity matrix would + * be represented by the following string. com.jme.math.Matrix3f
+ * [
+ * 1.0 0.0 0.0 0.0
+ * 0.0 1.0 0.0 0.0
+ * 0.0 0.0 1.0 0.0
+ * 0.0 0.0 0.0 1.0
+ * ]
+ * + * @return the string representation of this object. + */ + @Override + public String toString() { + StringBuffer result = new StringBuffer("com.jme.math.Matrix4f\n[\n"); + result.append(' '); + result.append(m00); + result.append(" "); + result.append(m01); + result.append(" "); + result.append(m02); + result.append(" "); + result.append(m03); + result.append(" \n"); + result.append(' '); + result.append(m10); + result.append(" "); + result.append(m11); + result.append(" "); + result.append(m12); + result.append(" "); + result.append(m13); + result.append(" \n"); + result.append(' '); + result.append(m20); + result.append(" "); + result.append(m21); + result.append(" "); + result.append(m22); + result.append(" "); + result.append(m23); + result.append(" \n"); + result.append(' '); + result.append(m30); + result.append(" "); + result.append(m31); + result.append(" "); + result.append(m32); + result.append(" "); + result.append(m33); + result.append(" \n]"); + return result.toString(); + } + + /** + * + * hashCode returns the hash code value as an integer and is + * supported for the benefit of hashing based collection classes such as + * Hashtable, HashMap, HashSet etc. + * + * @return the hashcode for this instance of Matrix4f. + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + int hash = 37; + hash = 37 * hash + Float.floatToIntBits(m00); + hash = 37 * hash + Float.floatToIntBits(m01); + hash = 37 * hash + Float.floatToIntBits(m02); + hash = 37 * hash + Float.floatToIntBits(m03); + + hash = 37 * hash + Float.floatToIntBits(m10); + hash = 37 * hash + Float.floatToIntBits(m11); + hash = 37 * hash + Float.floatToIntBits(m12); + hash = 37 * hash + Float.floatToIntBits(m13); + + hash = 37 * hash + Float.floatToIntBits(m20); + hash = 37 * hash + Float.floatToIntBits(m21); + hash = 37 * hash + Float.floatToIntBits(m22); + hash = 37 * hash + Float.floatToIntBits(m23); + + hash = 37 * hash + Float.floatToIntBits(m30); + hash = 37 * hash + Float.floatToIntBits(m31); + hash = 37 * hash + Float.floatToIntBits(m32); + hash = 37 * hash + Float.floatToIntBits(m33); + + return hash; + } + + /** + * are these two matrices the same? they are is they both have the same mXX + * values. + * + * @param o + * the object to compare for equality + * @return true if they are equal + */ + @Override + public boolean equals(Object o) { + if (!(o instanceof Matrix4f) || o == null) { + return false; + } + + if (this == o) { + return true; + } + + Matrix4f comp = (Matrix4f) o; + if (Float.compare(m00, comp.m00) != 0) + return false; + if (Float.compare(m01, comp.m01) != 0) + return false; + if (Float.compare(m02, comp.m02) != 0) + return false; + if (Float.compare(m03, comp.m03) != 0) + return false; + + if (Float.compare(m10, comp.m10) != 0) + return false; + if (Float.compare(m11, comp.m11) != 0) + return false; + if (Float.compare(m12, comp.m12) != 0) + return false; + if (Float.compare(m13, comp.m13) != 0) + return false; + + if (Float.compare(m20, comp.m20) != 0) + return false; + if (Float.compare(m21, comp.m21) != 0) + return false; + if (Float.compare(m22, comp.m22) != 0) + return false; + if (Float.compare(m23, comp.m23) != 0) + return false; + + if (Float.compare(m30, comp.m30) != 0) + return false; + if (Float.compare(m31, comp.m31) != 0) + return false; + if (Float.compare(m32, comp.m32) != 0) + return false; + return Float.compare(m33, comp.m33) == 0; + } + + /** + * @return true if this matrix is identity + */ + public boolean isIdentity() { + return (m00 == 1 && m01 == 0 && m02 == 0 && m03 == 0) && (m10 == 0 && m11 == 1 && m12 == 0 && m13 == 0) + && (m20 == 0 && m21 == 0 && m22 == 1 && m23 == 0) && (m30 == 0 && m31 == 0 && m32 == 0 && m33 == 1); + } + + /** + * Apply a scale to this matrix. + * + * @param scale + * the scale to apply + */ + public void scale(Vector3f scale) { + m00 *= scale.getX(); + m10 *= scale.getX(); + m20 *= scale.getX(); + m30 *= scale.getX(); + m01 *= scale.getY(); + m11 *= scale.getY(); + m21 *= scale.getY(); + m31 *= scale.getY(); + m02 *= scale.getZ(); + m12 *= scale.getZ(); + m22 *= scale.getZ(); + m32 *= scale.getZ(); + } + + static boolean equalIdentity(Matrix4f mat) { + if (Math.abs(mat.m00 - 1) > 1e-4) + return false; + if (Math.abs(mat.m11 - 1) > 1e-4) + return false; + if (Math.abs(mat.m22 - 1) > 1e-4) + return false; + if (Math.abs(mat.m33 - 1) > 1e-4) + return false; + + if (Math.abs(mat.m01) > 1e-4) + return false; + if (Math.abs(mat.m02) > 1e-4) + return false; + if (Math.abs(mat.m03) > 1e-4) + return false; + + if (Math.abs(mat.m10) > 1e-4) + return false; + if (Math.abs(mat.m12) > 1e-4) + return false; + if (Math.abs(mat.m13) > 1e-4) + return false; + + if (Math.abs(mat.m20) > 1e-4) + return false; + if (Math.abs(mat.m21) > 1e-4) + return false; + if (Math.abs(mat.m23) > 1e-4) + return false; + + if (Math.abs(mat.m30) > 1e-4) + return false; + if (Math.abs(mat.m31) > 1e-4) + return false; + return !(Math.abs(mat.m32) > 1e-4); + } + + // XXX: This tests more solid than converting the q to a matrix and + // multiplying... why? + public void multLocal(Quaternion rotation) { + Vector3f axis = new Vector3f(); + float angle = rotation.toAngleAxis(axis); + Matrix4f matrix4f = new Matrix4f(); + matrix4f.fromAngleAxis(angle, axis); + multLocal(matrix4f); + } + + @Override + public Matrix4f clone() { + try { + return (Matrix4f) super.clone(); + } catch (CloneNotSupportedException e) { + Logger.error( e); + throw new AssertionError(); // can not happen + } + } +} diff --git a/src/engine/math/Quaternion.java b/src/engine/math/Quaternion.java new file mode 100644 index 00000000..d5016a86 --- /dev/null +++ b/src/engine/math/Quaternion.java @@ -0,0 +1,1264 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.math; + +import org.pmw.tinylog.Logger; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + + +/** + * Quaternion defines a single example of a more general class of + * hypercomplex numbers. Quaternions extends a rotation in three dimensions to a + * rotation in four dimensions. This avoids "gimbal lock" and allows for smooth + * continuous rotation. + * + * Quaternion is defined by four floating point numbers: {x y z w}. + * + */ + +public class Quaternion { + + public float x, y, z, w; + public float angleX, angleY,angleZ; + + /** + * Constructor instantiates a new Quaternion object + * initializing all values to zero, except w which is initialized to 1. + * + */ + public Quaternion() { + x = 0; + y = 0; + z = 0; + w = 1; + } + + /** + * Constructor instantiates a new Quaternion object from the + * given list of parameters. + * + * @param x + * the x value of the quaternion. + * @param y + * the y value of the quaternion. + * @param z + * the z value of the quaternion. + * @param w + * the w value of the quaternion. + */ + public Quaternion(float x, float y, float z, float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + float[] angles = new float[3]; + angles = this.toAngles(angles); + angleX = angles[0]; + angleY = angles[1]; + angleZ = angles[2]; + } + + /** + * sets the data in a Quaternion object from the given list of + * parameters. + * + * @param x + * the x value of the quaternion. + * @param y + * the y value of the quaternion. + * @param z + * the z value of the quaternion. + * @param w + * the w value of the quaternion. + */ + public void set(float x, float y, float z, float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + /** + * Sets the data in this Quaternion object to be equal to the + * passed Quaternion object. The values are copied producing a + * new object. + * + * @param q + * The Quaternion to copy values from. + * @return this for chaining + */ + public Quaternion set(Quaternion q) { + this.x = q.x; + this.y = q.y; + this.z = q.z; + this.w = q.w; + return this; + } + + /** + * Constructor instantiates a new Quaternion object from a + * collection of rotation angles. + * + * @param angles + * the angles of rotation (x, y, z) that will define the + * Quaternion. + */ + public Quaternion(float[] angles) { + fromAngles(angles); + } + + /** + * Constructor instantiates a new Quaternion object from an + * interpolation between two other quaternions. + * + * @param q1 + * the first quaternion. + * @param q2 + * the second quaternion. + * @param interp + * the amount to interpolate between the two quaternions. + */ + public Quaternion(Quaternion q1, Quaternion q2, float interp) { + slerp(q1, q2, interp); + } + + /** + * Constructor instantiates a new Quaternion object from an + * existing quaternion, creating a copy. + * + * @param q + * the quaternion to copy. + */ + public Quaternion(Quaternion q) { + this.x = q.x; + this.y = q.y; + this.z = q.z; + this.w = q.w; + } + + /** + * Sets this Quaternion to {0, 0, 0, 1}. Same as calling set(0,0,0,1). + */ + public void loadIdentity() { + x = y = z = 0; + w = 1; + } + + /** + * @return true if this Quaternion is {0,0,0,1} + */ + public boolean isIdentity() { + return x == 0 && y == 0 && z == 0 && w == 1; + } + + /** + * fromAngles builds a quaternion from the Euler rotation + * angles (y,r,p). + * + * @param angles + * the Euler angles of rotation (in radians). + */ + public void fromAngles(float[] angles) { + if (angles.length != 3) + throw new IllegalArgumentException( + "Angles array must have three elements"); + + fromAngles(angles[0], angles[1], angles[2]); + } + + /** + * fromAngles builds a Quaternion from the Euler rotation + * angles (y,r,p). Note that we are applying in order: roll, pitch, yaw but + * we've ordered them in x, y, and z for convenience. See: + * http://www.euclideanspace + * .com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm + * + * @param yaw + * the Euler yaw of rotation (in radians). (aka Bank, often rot + * around x) + * @param roll + * the Euler roll of rotation (in radians). (aka Heading, often + * rot around y) + * @param pitch + * the Euler pitch of rotation (in radians). (aka Attitude, often + * rot around z) + */ + public Quaternion fromAngles(float yaw, float roll, float pitch) { + float angle; + float sinRoll, sinPitch, sinYaw, cosRoll, cosPitch, cosYaw; + angle = pitch * 0.5f; + sinPitch = FastMath.sin(angle); + cosPitch = FastMath.cos(angle); + angle = roll * 0.5f; + sinRoll = FastMath.sin(angle); + cosRoll = FastMath.cos(angle); + angle = yaw * 0.5f; + sinYaw = FastMath.sin(angle); + cosYaw = FastMath.cos(angle); + + // variables used to reduce multiplication calls. + float cosRollXcosPitch = cosRoll * cosPitch; + float sinRollXsinPitch = sinRoll * sinPitch; + float cosRollXsinPitch = cosRoll * sinPitch; + float sinRollXcosPitch = sinRoll * cosPitch; + + w = (cosRollXcosPitch * cosYaw - sinRollXsinPitch * sinYaw); + x = (cosRollXcosPitch * sinYaw + sinRollXsinPitch * cosYaw); + y = (sinRollXcosPitch * cosYaw + cosRollXsinPitch * sinYaw); + z = (cosRollXsinPitch * cosYaw - sinRollXcosPitch * sinYaw); + + normalize(); + return this; + } + + /** + * toAngles returns this quaternion converted to Euler rotation + * angles (yaw,roll,pitch).
+ * See http://www.euclideanspace.com/maths/geometry/rotations/conversions/ + * quaternionToEuler/index.htm + * + * @param angles + * the float[] in which the angles should be stored, or null if + * you want a new float[] to be created + * @return the float[] in which the angles are stored. + */ + public float[] toAngles(float[] angles) { + if (angles == null) + angles = new float[3]; + else if (angles.length != 3) + throw new IllegalArgumentException( + "Angles array must have three elements"); + + float sqw = w * w; + float sqx = x * x; + float sqy = y * y; + float sqz = z * z; + float unit = sqx + sqy + sqz + sqw; // if normalized is one, otherwise + // is correction factor + float test = x * y + z * w; + if (test > 0.499 * unit) { // singularity at north pole + angles[1] = 2 * FastMath.atan2(x, w); + angles[2] = FastMath.HALF_PI; + angles[0] = 0; + } else if (test < -0.499 * unit) { // singularity at south pole + angles[1] = -2 * FastMath.atan2(x, w); + angles[2] = -FastMath.HALF_PI; + angles[0] = 0; + } else { + angles[1] = FastMath.atan2(2 * y * w - 2 * x * z, sqx - sqy - sqz + + sqw); // roll or heading + angles[2] = FastMath.asin(2 * test / unit); // pitch or attitude + angles[0] = FastMath.atan2(2 * x * w - 2 * y * z, -sqx + sqy - sqz + + sqw); // yaw or bank + } + return angles; + } + + /** + * + * fromRotationMatrix generates a quaternion from a supplied + * matrix. This matrix is assumed to be a rotational matrix. + * + * @param matrix + * the matrix that defines the rotation. + */ + public Quaternion fromRotationMatrix(Matrix3f matrix) { + return fromRotationMatrix(matrix.m00, matrix.m01, matrix.m02, + matrix.m10, matrix.m11, matrix.m12, matrix.m20, matrix.m21, + matrix.m22); + } + + public Quaternion fromRotationMatrix(float m00, float m01, float m02, + float m10, float m11, float m12, float m20, float m21, float m22) { + // Use the Graphics Gems code, from + // ftp://ftp.cis.upenn.edu/pub/graphics/shoemake/quatut.ps.Z + // *NOT* the "Matrix and Quaternions FAQ", which has errors! + + // the trace is the sum of the diagonal elements; see + // http://mathworld.wolfram.com/MatrixTrace.html + float t = m00 + m11 + m22; + + // we protect the division by s by ensuring that s>=1 + if (t >= 0) { // |w| >= .5 + float s = FastMath.sqrt(t + 1); // |s|>=1 ... + w = 0.5f * s; + s = 0.5f / s; // so this division isn't bad + x = (m21 - m12) * s; + y = (m02 - m20) * s; + z = (m10 - m01) * s; + } else if ((m00 > m11) && (m00 > m22)) { + float s = FastMath.sqrt(1.0f + m00 - m11 - m22); // |s|>=1 + x = s * 0.5f; // |x| >= .5 + s = 0.5f / s; + y = (m10 + m01) * s; + z = (m02 + m20) * s; + w = (m21 - m12) * s; + } else if (m11 > m22) { + float s = FastMath.sqrt(1.0f + m11 - m00 - m22); // |s|>=1 + y = s * 0.5f; // |y| >= .5 + s = 0.5f / s; + x = (m10 + m01) * s; + z = (m21 + m12) * s; + w = (m02 - m20) * s; + } else { + float s = FastMath.sqrt(1.0f + m22 - m00 - m11); // |s|>=1 + z = s * 0.5f; // |z| >= .5 + s = 0.5f / s; + x = (m02 + m20) * s; + y = (m21 + m12) * s; + w = (m10 - m01) * s; + } + + return this; + } + + /** + * toRotationMatrix converts this quaternion to a rotational + * matrix. Note: the result is created from a normalized version of this + * quat. + * + * @return the rotation matrix representation of this quaternion. + */ + public Matrix3f toRotationMatrix() { + Matrix3f matrix = new Matrix3f(); + return toRotationMatrix(matrix); + } + + /** + * toRotationMatrix converts this quaternion to a rotational + * matrix. The result is stored in result. + * + * @param result + * The Matrix3f to store the result in. + * @return the rotation matrix representation of this quaternion. + */ + public Matrix3f toRotationMatrix(Matrix3f result) { + + float norm = norm(); + // we explicitly test norm against one here, saving a division + // at the cost of a test and branch. Is it worth it? + float s = (norm == 1f) ? 2f : (norm > 0f) ? 2f / norm : 0; + + // compute xs/ys/zs first to save 6 multiplications, since xs/ys/zs + // will be used 2-4 times each. + float xs = x * s; + float ys = y * s; + float zs = z * s; + float xx = x * xs; + float xy = x * ys; + float xz = x * zs; + float xw = w * xs; + float yy = y * ys; + float yz = y * zs; + float yw = w * ys; + float zz = z * zs; + float zw = w * zs; + + // using s=2/norm (instead of 1/norm) saves 9 multiplications by 2 here + result.m00 = 1 - (yy + zz); + result.m01 = (xy - zw); + result.m02 = (xz + yw); + result.m10 = (xy + zw); + result.m11 = 1 - (xx + zz); + result.m12 = (yz - xw); + result.m20 = (xz - yw); + result.m21 = (yz + xw); + result.m22 = 1 - (xx + yy); + + return result; + } + + /** + * toRotationMatrix converts this quaternion to a rotational + * matrix. The result is stored in result. 4th row and 4th column values are + * untouched. Note: the result is created from a normalized version of this + * quat. + * + * @param result + * The Matrix4f to store the result in. + * @return the rotation matrix representation of this quaternion. + */ + public Matrix4f toRotationMatrix(Matrix4f result) { + + float norm = norm(); + // we explicitly test norm against one here, saving a division + // at the cost of a test and branch. Is it worth it? + float s = (norm == 1f) ? 2f : (norm > 0f) ? 2f / norm : 0; + + // compute xs/ys/zs first to save 6 multiplications, since xs/ys/zs + // will be used 2-4 times each. + float xs = x * s; + float ys = y * s; + float zs = z * s; + float xx = x * xs; + float xy = x * ys; + float xz = x * zs; + float xw = w * xs; + float yy = y * ys; + float yz = y * zs; + float yw = w * ys; + float zz = z * zs; + float zw = w * zs; + + // using s=2/norm (instead of 1/norm) saves 9 multiplications by 2 here + result.m00 = 1 - (yy + zz); + result.m01 = (xy - zw); + result.m02 = (xz + yw); + result.m10 = (xy + zw); + result.m11 = 1 - (xx + zz); + result.m12 = (yz - xw); + result.m20 = (xz - yw); + result.m21 = (yz + xw); + result.m22 = 1 - (xx + yy); + + return result; + } + + /** + * getRotationColumn returns one of three columns specified by + * the parameter. This column is returned as a Vector3f object. + * + * @param i + * the column to retrieve. Must be between 0 and 2. + * @return the column specified by the index. + * @throws Exception + */ + public Vector3f getRotationColumn(int i) throws Exception { + return getRotationColumn(i, null); + } + + /** + * getRotationColumn returns one of three columns specified by + * the parameter. This column is returned as a Vector3f object. + * The value is retrieved as if this quaternion was first normalized. + * + * @param i + * the column to retrieve. Must be between 0 and 2. + * @param store + * the vector object to store the result in. if null, a new one + * is created. + * @return the column specified by the index. + * @throws Exception + */ + public Vector3f getRotationColumn(int i, Vector3f store) throws Exception { + if (store == null) + store = new Vector3f(); + + float norm = norm(); + if (norm != 1.0f) { + norm = FastMath.invSqrt(norm); + } + + float xx = x * x * norm; + float xy = x * y * norm; + float xz = x * z * norm; + float xw = x * w * norm; + float yy = y * y * norm; + float yz = y * z * norm; + float yw = y * w * norm; + float zz = z * z * norm; + float zw = z * w * norm; + + switch (i) { + case 0: + store.x = 1 - 2 * (yy + zz); + store.y = 2 * (xy + zw); + store.z = 2 * (xz - yw); + break; + case 1: + store.x = 2 * (xy - zw); + store.y = 1 - 2 * (xx + zz); + store.z = 2 * (yz + xw); + break; + case 2: + store.x = 2 * (xz + yw); + store.y = 2 * (yz - xw); + store.z = 1 - 2 * (xx + yy); + break; + default: + throw new Exception("Invalid column index. " + i); + } + + return store; + } + + /** + * fromAngleAxis sets this quaternion to the values specified + * by an angle and an axis of rotation. This method creates an object, so + * use fromAngleNormalAxis if your axis is already normalized. + * + * @param angle + * the angle to rotate (in radians). + * @param axis + * the axis of rotation. + * @return this quaternion + */ + public Quaternion fromAngleAxis(float angle, Vector3f axis) { + Vector3f normAxis = axis.normalize(); + fromAngleNormalAxis(angle, normAxis); + return this; + } + + /** + * fromAngleNormalAxis sets this quaternion to the values + * specified by an angle and a normalized axis of rotation. + * + * @param angle + * the angle to rotate (in radians). + * @param axis + * the axis of rotation (already normalized). + */ + public Quaternion fromAngleNormalAxis(float angle, Vector3f axis) { + if (axis.x == 0 && axis.y == 0 && axis.z == 0) { + loadIdentity(); + } else { + float halfAngle = 0.5f * angle; + float sin = FastMath.sin(halfAngle); + w = FastMath.cos(halfAngle); + x = sin * axis.x; + y = sin * axis.y; + z = sin * axis.z; + } + return this; + } + + /** + * toAngleAxis sets a given angle and axis to that represented + * by the current quaternion. The values are stored as following: The axis + * is provided as a parameter and built by the method, the angle is returned + * as a float. + * + * @param axisStore + * the object we'll store the computed axis in. + * @return the angle of rotation in radians. + */ + public float toAngleAxis(Vector3f axisStore) { + float sqrLength = x * x + y * y + z * z; + float angle; + if (sqrLength == 0.0f) { + angle = 0.0f; + if (axisStore != null) { + axisStore.x = 1.0f; + axisStore.y = 0.0f; + axisStore.z = 0.0f; + } + } else { + angle = (2.0f * FastMath.acos(w)); + if (axisStore != null) { + float invLength = (1.0f / FastMath.sqrt(sqrLength)); + axisStore.x = x * invLength; + axisStore.y = y * invLength; + axisStore.z = z * invLength; + } + } + + return angle; + } + + /** + * slerp sets this quaternion's value as an interpolation + * between two other quaternions. + * + * @param q1 + * the first quaternion. + * @param q2 + * the second quaternion. + * @param t + * the amount to interpolate between the two quaternions. + */ + public Quaternion slerp(Quaternion q1, Quaternion q2, float t) { + // Create a local quaternion to store the interpolated quaternion + if (q1.x == q2.x && q1.y == q2.y && q1.z == q2.z && q1.w == q2.w) { + this.set(q1); + return this; + } + + float result = (q1.x * q2.x) + (q1.y * q2.y) + (q1.z * q2.z) + + (q1.w * q2.w); + + if (result < 0.0f) { + // Negate the second quaternion and the result of the dot product + q2.x = -q2.x; + q2.y = -q2.y; + q2.z = -q2.z; + q2.w = -q2.w; + result = -result; + } + + // Set the first and second scale for the interpolation + float scale0 = 1 - t; + float scale1 = t; + + // Check if the angle between the 2 quaternions was big enough to + // warrant such calculations + if ((1 - result) > 0.1f) {// Get the angle between the 2 quaternions, + // and then store the sin() of that angle + float theta = FastMath.acos(result); + float invSinTheta = 1f / FastMath.sin(theta); + + // Calculate the scale for q1 and q2, according to the angle and + // it's sine value + scale0 = FastMath.sin((1 - t) * theta) * invSinTheta; + scale1 = FastMath.sin((t * theta)) * invSinTheta; + } + + // Calculate the x, y, z and w values for the quaternion by using a + // special + // form of linear interpolation for quaternions. + this.x = (scale0 * q1.x) + (scale1 * q2.x); + this.y = (scale0 * q1.y) + (scale1 * q2.y); + this.z = (scale0 * q1.z) + (scale1 * q2.z); + this.w = (scale0 * q1.w) + (scale1 * q2.w); + + // Return the interpolated quaternion + return this; + } + + /** + * Sets the values of this quaternion to the slerp from itself to q2 by + * changeAmnt + * + * @param q2 + * Final interpolation value + * @param changeAmnt + * The amount difference + */ + public void slerp(Quaternion q2, float changeAmnt) { + if (this.x == q2.x && this.y == q2.y && this.z == q2.z + && this.w == q2.w) { + return; + } + + float result = (this.x * q2.x) + (this.y * q2.y) + (this.z * q2.z) + + (this.w * q2.w); + + if (result < 0.0f) { + // Negate the second quaternion and the result of the dot product + q2.x = -q2.x; + q2.y = -q2.y; + q2.z = -q2.z; + q2.w = -q2.w; + result = -result; + } + + // Set the first and second scale for the interpolation + float scale0 = 1 - changeAmnt; + float scale1 = changeAmnt; + + // Check if the angle between the 2 quaternions was big enough to + // warrant such calculations + if ((1 - result) > 0.1f) { + // Get the angle between the 2 quaternions, and then store the sin() + // of that angle + float theta = FastMath.acos(result); + float invSinTheta = 1f / FastMath.sin(theta); + + // Calculate the scale for q1 and q2, according to the angle and + // it's sine value + scale0 = FastMath.sin((1 - changeAmnt) * theta) * invSinTheta; + scale1 = FastMath.sin((changeAmnt * theta)) * invSinTheta; + } + + // Calculate the x, y, z and w values for the quaternion by using a + // special + // form of linear interpolation for quaternions. + this.x = (scale0 * this.x) + (scale1 * q2.x); + this.y = (scale0 * this.y) + (scale1 * q2.y); + this.z = (scale0 * this.z) + (scale1 * q2.z); + this.w = (scale0 * this.w) + (scale1 * q2.w); + } + + /** + * add adds the values of this quaternion to those of the + * parameter quaternion. The result is returned as a new quaternion. + * + * @param q + * the quaternion to add to this. + * @return the new quaternion. + */ + public Quaternion add(Quaternion q) { + return new Quaternion(x + q.x, y + q.y, z + q.z, w + q.w); + } + + /** + * add adds the values of this quaternion to those of the + * parameter quaternion. The result is stored in this Quaternion. + * + * @param q + * the quaternion to add to this. + * @return This Quaternion after addition. + */ + public Quaternion addLocal(Quaternion q) { + this.x += q.x; + this.y += q.y; + this.z += q.z; + this.w += q.w; + return this; + } + + /** + * subtract subtracts the values of the parameter quaternion + * from those of this quaternion. The result is returned as a new + * quaternion. + * + * @param q + * the quaternion to subtract from this. + * @return the new quaternion. + */ + public Quaternion subtract(Quaternion q) { + return new Quaternion(x - q.x, y - q.y, z - q.z, w - q.w); + } + + /** + * subtract subtracts the values of the parameter quaternion + * from those of this quaternion. The result is stored in this Quaternion. + * + * @param q + * the quaternion to subtract from this. + * @return This Quaternion after subtraction. + */ + public Quaternion subtractLocal(Quaternion q) { + this.x -= q.x; + this.y -= q.y; + this.z -= q.z; + this.w -= q.w; + return this; + } + + /** + * mult multiplies this quaternion by a parameter quaternion. + * The result is returned as a new quaternion. It should be noted that + * quaternion multiplication is not cummulative so q * p != p * q. + * + * @param q + * the quaternion to multiply this quaternion by. + * @return the new quaternion. + */ + public Quaternion mult(Quaternion q) { + return mult(q, null); + } + + /** + * mult multiplies this quaternion by a parameter quaternion + * (q). 'this' is not modified. It should be noted that quaternion + * multiplication is not cummulative so q * p != p * q. + * + * It IS safe for q and res to be the same object. + * + * @param q + * the quaternion to multiply this quaternion by. + * @param res + * the quaternion to store the result in (may be null). If + * non-null, the input values of 'res' will be ignored and + * replaced. + * @return If specified res is null, then a new Quaternion; otherwise + * returns the populated 'res'. + */ + public Quaternion mult(Quaternion q, Quaternion res) { + if (res == null) + res = new Quaternion(); + float qw = q.w, qx = q.x, qy = q.y, qz = q.z; + res.x = x * qw + y * qz - z * qy + w * qx; + res.y = -x * qz + y * qw + z * qx + w * qy; + res.z = x * qy - y * qx + z * qw + w * qz; + res.w = -x * qx - y * qy - z * qz + w * qw; + + float[] angles = new float[3]; + angles = res.toAngles(angles); + res.angleX = angles[0]; + res.angleY = angles[1]; + res.angleZ = angles[2]; + return res; + } + + /** + * apply multiplies this quaternion by a parameter matrix + * internally. + * + * @param matrix + * the matrix to apply to this quaternion. + */ + public void apply(Matrix3f matrix) { + float oldX = x, oldY = y, oldZ = z, oldW = w; + fromRotationMatrix(matrix); + float tempX = x, tempY = y, tempZ = z, tempW = w; + + x = oldX * tempW + oldY * tempZ - oldZ * tempY + oldW * tempX; + y = -oldX * tempZ + oldY * tempW + oldZ * tempX + oldW * tempY; + z = oldX * tempY - oldY * tempX + oldZ * tempW + oldW * tempZ; + w = -oldX * tempX - oldY * tempY - oldZ * tempZ + oldW * tempW; + } + + /** + * + * fromAxes creates a Quaternion that represents + * the coordinate system defined by three axes. These axes are assumed to be + * orthogonal and no error checking is applied. Thus, the user must insure + * that the three axes being provided indeed represents a proper right + * handed coordinate system. + * + * @param axis + * the array containing the three vectors representing the + * coordinate system. + */ + public Quaternion fromAxes(Vector3f[] axis) { + if (axis.length != 3) + throw new IllegalArgumentException( + "Axis array must have three elements"); + return fromAxes(axis[0], axis[1], axis[2]); + } + + /** + * + * fromAxes creates a Quaternion that represents + * the coordinate system defined by three axes. These axes are assumed to be + * orthogonal and no error checking is applied. Thus, the user must insure + * that the three axes being provided indeed represents a proper right + * handed coordinate system. + * + * @param xAxis + * vector representing the x-axis of the coordinate system. + * @param yAxis + * vector representing the y-axis of the coordinate system. + * @param zAxis + * vector representing the z-axis of the coordinate system. + */ + public Quaternion fromAxes(Vector3f xAxis, Vector3f yAxis, Vector3f zAxis) { + return fromRotationMatrix(xAxis.x, yAxis.x, zAxis.x, xAxis.y, yAxis.y, + zAxis.y, xAxis.z, yAxis.z, zAxis.z); + } + + /** + * + * toAxes takes in an array of three vectors. Each vector + * corresponds to an axis of the coordinate system defined by the quaternion + * rotation. + * + * @param axis + * the array of vectors to be filled. + * @throws Exception + */ + public void toAxes(Vector3f axis[]) throws Exception { + Matrix3f tempMat = toRotationMatrix(); + axis[0] = tempMat.getColumn(0, axis[0]); + axis[1] = tempMat.getColumn(1, axis[1]); + axis[2] = tempMat.getColumn(2, axis[2]); + } + + /** + * mult multiplies this quaternion by a parameter vector. The + * result is returned as a new vector. 'this' is not modified. + * + * @param v + * the vector to multiply this quaternion by. + * @return the new vector. + */ + public Vector3f mult(Vector3f v) { + return mult(v, null); + } + + /** + * mult multiplies this quaternion by a parameter vector. The + * result is stored in the supplied vector This method is very poorly named, + * since the specified vector is modified and, contrary to the other *Local + * methods in this and other jME classes, 'this' remains unchanged. + * + * @param v + * the vector which this Quaternion multiplies. + * @return v + */ + public Vector3f multLocal(Vector3f v) { + float tempX, tempY; + tempX = w * w * v.x + 2 * y * w * v.z - 2 * z * w * v.y + x * x * v.x + + 2 * y * x * v.y + 2 * z * x * v.z - z * z * v.x - y * y * v.x; + tempY = 2 * x * y * v.x + y * y * v.y + 2 * z * y * v.z + 2 * w * z + * v.x - z * z * v.y + w * w * v.y - 2 * x * w * v.z - x * x + * v.y; + v.z = 2 * x * z * v.x + 2 * y * z * v.y + z * z * v.z - 2 * w * y * v.x + - y * y * v.z + 2 * w * x * v.y - x * x * v.z + w * w * v.z; + v.x = tempX; + v.y = tempY; + return v; + } + + /** + * Multiplies this Quaternion by the supplied quaternion. The result is + * stored in this Quaternion, which is also returned for chaining. Similar + * to this *= q. + * + * @param q + * The Quaternion to multiply this one by. + * @return This Quaternion, after multiplication. + */ + public Quaternion multLocal(Quaternion q) { + float x1 = x * q.w + y * q.z - z * q.y + w * q.x; + float y1 = -x * q.z + y * q.w + z * q.x + w * q.y; + float z1 = x * q.y - y * q.x + z * q.w + w * q.z; + w = -x * q.x - y * q.y - z * q.z + w * q.w; + x = x1; + y = y1; + z = z1; + return this; + } + + /** + * Multiplies this Quaternion by the supplied quaternion. The result is + * stored in this Quaternion, which is also returned for chaining. Similar + * to this *= q. + * + * @param qx + * - quat x value + * @param qy + * - quat y value + * @param qz + * - quat z value + * @param qw + * - quat w value + * + * @return This Quaternion, after multiplication. + */ + public Quaternion multLocal(float qx, float qy, float qz, float qw) { + float x1 = x * qw + y * qz - z * qy + w * qx; + float y1 = -x * qz + y * qw + z * qx + w * qy; + float z1 = x * qy - y * qx + z * qw + w * qz; + w = -x * qx - y * qy - z * qz + w * qw; + x = x1; + y = y1; + z = z1; + return this; + } + + /** + * mult multiplies this quaternion by a parameter vector. The + * result is returned as a new vector. 'this' is not modified. + * + * @param v + * the vector to multiply this quaternion by. + * @param store + * the vector to store the result in. It IS safe for v and store + * to be the same object. + * @return the result vector. + */ + public Vector3f mult(Vector3f v, Vector3f store) { + if (store == null) + store = new Vector3f(); + if (v.x == 0 && v.y == 0 && v.z == 0) { + store.set(0, 0, 0); + } else { + float vx = v.x, vy = v.y, vz = v.z; + store.x = w * w * vx + 2 * y * w * vz - 2 * z * w * vy + x * x * vx + + 2 * y * x * vy + 2 * z * x * vz - z * z * vx - y * y * vx; + store.y = 2 * x * y * vx + y * y * vy + 2 * z * y * vz + 2 * w * z + * vx - z * z * vy + w * w * vy - 2 * x * w * vz - x * x + * vy; + store.z = 2 * x * z * vx + 2 * y * z * vy + z * z * vz - 2 * w * y + * vx - y * y * vz + 2 * w * x * vy - x * x * vz + w * w + * vz; + } + return store; + } + + /** + * mult multiplies this quaternion by a parameter scalar. The + * result is returned as a new quaternion. + * + * @param scalar + * the quaternion to multiply this quaternion by. + * @return the new quaternion. + */ + public Quaternion mult(float scalar) { + return new Quaternion(scalar * x, scalar * y, scalar * z, scalar * w); + } + + /** + * mult multiplies this quaternion by a parameter scalar. The + * result is stored locally. + * + * @param scalar + * the quaternion to multiply this quaternion by. + * @return this. + */ + public Quaternion multLocal(float scalar) { + w *= scalar; + x *= scalar; + y *= scalar; + z *= scalar; + return this; + } + + /** + * dot calculates and returns the dot product of this + * quaternion with that of the parameter quaternion. + * + * @param q + * the quaternion to calculate the dot product of. + * @return the dot product of this and the parameter quaternion. + */ + public float dot(Quaternion q) { + return w * q.w + x * q.x + y * q.y + z * q.z; + } + + /** + * norm returns the norm of this quaternion. This is the dot + * product of this quaternion with itself. + * + * @return the norm of the quaternion. + */ + public float norm() { + return w * w + x * x + y * y + z * z; + } + + /** + * normalize normalizes the current Quaternion + */ + public void normalize() { + float n = FastMath.invSqrt(norm()); + x *= n; + y *= n; + z *= n; + w *= n; + } + + /** + * inverse returns the inverse of this quaternion as a new + * quaternion. If this quaternion does not have an inverse (if its normal is + * 0 or less), then null is returned. + * + * @return the inverse of this quaternion or null if the inverse does not + * exist. + */ + public Quaternion inverse() { + float norm = norm(); + if (norm > 0.0) { + float invNorm = 1.0f / norm; + return new Quaternion(-x * invNorm, -y * invNorm, -z * invNorm, w + * invNorm); + } + // return an invalid result to flag the error + return null; + } + + /** + * inverse calculates the inverse of this quaternion and + * returns this quaternion after it is calculated. If this quaternion does + * not have an inverse (if it's norma is 0 or less), nothing happens + * + * @return the inverse of this quaternion + */ + public Quaternion inverseLocal() { + float norm = norm(); + if (norm > 0.0) { + float invNorm = 1.0f / norm; + x *= -invNorm; + y *= -invNorm; + z *= -invNorm; + w *= invNorm; + } + return this; + } + + /** + * negate inverts the values of the quaternion. + * + */ + public void negate() { + x *= -1; + y *= -1; + z *= -1; + w *= -1; + } + + /** + * equals determines if two quaternions are logically equal, + * that is, if the values of (x, y, z, w) are the same for both quaternions. + * + * @param o + * the object to compare for equality + * @return true if they are equal, false otherwise. + */ + @Override + public boolean equals(Object o) { + if (!(o instanceof Quaternion)) { + return false; + } + + if (this == o) { + return true; + } + + Quaternion comp = (Quaternion) o; + if (Float.compare(x, comp.x) != 0) + return false; + if (Float.compare(y, comp.y) != 0) + return false; + if (Float.compare(z, comp.z) != 0) + return false; + return Float.compare(w, comp.w) == 0; + } + + /** + * + * hashCode returns the hash code value as an integer and is + * supported for the benefit of hashing based collection classes such as + * Hashtable, HashMap, HashSet etc. + * + * @return the hashcode for this instance of Quaternion. + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + int hash = 37; + hash = 37 * hash + Float.floatToIntBits(x); + hash = 37 * hash + Float.floatToIntBits(y); + hash = 37 * hash + Float.floatToIntBits(z); + hash = 37 * hash + Float.floatToIntBits(w); + return hash; + + } + + /** + * readExternal builds a quaternion from an + * ObjectInput object.
+ * NOTE: Used with serialization. Not to be called manually. + * + * @param in + * the ObjectInput value to read from. + * @throws IOException + * if the ObjectInput value has problems reading a float. + * @see java.io.Externalizable + */ + public void readExternal(ObjectInput in) throws IOException { + x = in.readFloat(); + y = in.readFloat(); + z = in.readFloat(); + w = in.readFloat(); + } + + /** + * writeExternal writes this quaternion out to a + * ObjectOutput object. NOTE: Used with serialization. Not to + * be called manually. + * + * @param out + * the object to write to. + * @throws IOException + * if writing to the ObjectOutput fails. + * @see java.io.Externalizable + */ + public void writeExternal(ObjectOutput out) throws IOException { + out.writeFloat(x); + out.writeFloat(y); + out.writeFloat(z); + out.writeFloat(w); + } + + private static final Vector3f tmpYaxis = new Vector3f(); + private static final Vector3f tmpZaxis = new Vector3f(); + private static final Vector3f tmpXaxis = new Vector3f(); + + /** + * lookAt is a convienence method for auto-setting the + * quaternion based on a direction and an up vector. It computes the + * rotation to transform the z-axis to point into 'direction' and the y-axis + * to 'up'. + * + * @param direction + * where to look at in terms of local coordinates + * @param up + * a vector indicating the local up direction. (typically {0, 1, + * 0} in jME.) + */ + public void lookAt(Vector3f direction, Vector3f up) { + tmpZaxis.set(direction).normalizeLocal(); + tmpXaxis.set(up).crossLocal(direction).normalizeLocal(); + tmpYaxis.set(direction).crossLocal(tmpXaxis).normalizeLocal(); + fromAxes(tmpXaxis, tmpYaxis, tmpZaxis); + } + + /** + * @return A new quaternion that describes a rotation that would point you + * in the exact opposite direction of this Quaternion. + */ + public Quaternion opposite() { + return opposite(null); + } + + /** + * @param store + * A Quaternion to store our result in. If null, a new one is + * created. + * @return The store quaternion (or a new Quaterion, if store is null) that + * describes a rotation that would point you in the exact opposite + * direction of this Quaternion. + */ + public Quaternion opposite(Quaternion store) { + if (store == null) + store = new Quaternion(); + + Vector3f axis = new Vector3f(); + float angle = toAngleAxis(axis); + + store.fromAngleAxis(FastMath.PI + angle, axis); + return store; + } + + /** + * @return This Quaternion, altered to describe a rotation that would point + * you in the exact opposite direction of where it is pointing + * currently. + */ + public Quaternion oppositeLocal() { + return opposite(this); + } + + @Override + public Quaternion clone() { + try { + return (Quaternion) super.clone(); + } catch (CloneNotSupportedException e) { + Logger.error( e); + throw new AssertionError(); // can not happen + } + } + + public float getX() { + return x; + } + + public void setX(float x) { + this.x = x; + } + + public float getY() { + return y; + } + + public void setY(float y) { + this.y = y; + } + + public float getZ() { + return z; + } + + public void setZ(float z) { + this.z = z; + } + + public float getW() { + return w; + } + + public void setW(float w) { + this.w = w; + } +} diff --git a/src/engine/math/Vector2f.java b/src/engine/math/Vector2f.java new file mode 100644 index 00000000..06281efc --- /dev/null +++ b/src/engine/math/Vector2f.java @@ -0,0 +1,662 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.math; + +/** + * Vector2f defines a Vector for a two float value vector. + * + */ +public class Vector2f { + + /** + * the x value of the vector. + */ + public float x; + /** + * the y value of the vector. + */ + public float y; + + /** + * Creates a Vector2f with the given initial x and y values. + * + * @param x + * The x value of this Vector2f. + * @param y + * The y value of this Vector2f. + */ + public Vector2f(float x, float y) { + this.x = x; + this.y = y; + } + + /** + * Creates a Vector2f with x and y set to 0. Equivalent to Vector2f(0,0). + */ + public Vector2f() { + x = y = 0; + } + + /** + * Creates a new Vector2f that contains the passed vector's information + * + * @param vector2f + * The vector to copy + */ + public Vector2f(Vector2f vector2f) { + this.x = vector2f.x; + this.y = vector2f.y; + } + + /** + * set the x and y values of the vector + * + * @param x + * the x value of the vector. + * @param y + * the y value of the vector. + * @return this vector + */ + public Vector2f set(float x, float y) { + this.x = x; + this.y = y; + return this; + } + + /** + * set the x and y values of the vector from another vector + * + * @param vec + * the vector to copy from + * @return this vector + */ + public Vector2f set(Vector2f vec) { + this.x = vec.x; + this.y = vec.y; + return this; + } + + /** + * add adds a provided vector to this vector creating a + * resultant vector which is returned. If the provided vector is null, null + * is returned. + * + * @param vec + * the vector to add to this. + * @return the resultant vector. + */ + public Vector2f add(Vector2f vec) { + if (null == vec) { + return null; + } + return new Vector2f(x + vec.x, y + vec.y); + } + + /** + * addLocal adds a provided vector to this vector internally, + * and returns a handle to this vector for easy chaining of calls. If the + * provided vector is null, null is returned. + * + * @param vec + * the vector to add to this vector. + * @return this + */ + public Vector2f addLocal(Vector2f vec) { + if (null == vec) { + return null; + } + x += vec.x; + y += vec.y; + return this; + } + + /** + * addLocal adds the provided values to this vector internally, + * and returns a handle to this vector for easy chaining of calls. + * + * @param addX + * value to add to x + * @param addY + * value to add to y + * @return this + */ + public Vector2f addLocal(float addX, float addY) { + x += addX; + y += addY; + return this; + } + + /** + * add adds this vector by vec and stores the + * result in result. + * + * @param vec + * The vector to add. + * @param result + * The vector to store the result in. + * @return The result vector, after adding. + */ + public Vector2f add(Vector2f vec, Vector2f result) { + if (null == vec) { + return null; + } + if (result == null) + result = new Vector2f(); + result.x = x + vec.x; + result.y = y + vec.y; + return result; + } + + /** + * dot calculates the dot product of this vector with a + * provided vector. If the provided vector is null, 0 is returned. + * + * @param vec + * the vector to dot with this vector. + * @return the resultant dot product of this vector and a given vector. + */ + public float dot(Vector2f vec) { + if (null == vec) { + return 0; + } + return x * vec.x + y * vec.y; + } + + /** + * cross calculates the cross product of this vector with a + * parameter vector v. + * + * @param v + * the vector to take the cross product of with this. + * @return the cross product vector. + */ + public Vector3f cross(Vector2f v) { + return new Vector3f(0, 0, determinant(v)); + } + + public float determinant(Vector2f v) { + return (x * v.y) - (y * v.x); + } + + /** + * Sets this vector to the interpolation by changeAmnt from this to the + * finalVec this=(1-changeAmnt)*this + changeAmnt * finalVec + * + * @param finalVec + * The final vector to interpolate towards + * @param changeAmnt + * An amount between 0.0 - 1.0 representing a percentage change + * from this towards finalVec + */ + public void interpolate(Vector2f finalVec, float changeAmnt) { + this.x = (1 - changeAmnt) * this.x + changeAmnt * finalVec.x; + this.y = (1 - changeAmnt) * this.y + changeAmnt * finalVec.y; + } + + /** + * Sets this vector to the interpolation by changeAmnt from beginVec to + * finalVec this=(1-changeAmnt)*beginVec + changeAmnt * finalVec + * + * @param beginVec + * The begining vector (delta=0) + * @param finalVec + * The final vector to interpolate towards (delta=1) + * @param changeAmnt + * An amount between 0.0 - 1.0 representing a precentage change + * from beginVec towards finalVec + */ + public void interpolate(Vector2f beginVec, Vector2f finalVec, + float changeAmnt) { + this.x = (1 - changeAmnt) * beginVec.x + changeAmnt * finalVec.x; + this.y = (1 - changeAmnt) * beginVec.y + changeAmnt * finalVec.y; + } + + /** + * Check a vector... if it is null or its floats are NaN or infinite, return + * false. Else return true. + * + * @param vector + * the vector to check + * @return true or false as stated above. + */ + public static boolean isValidVector(Vector2f vector) { + if (vector == null) + return false; + if (Float.isNaN(vector.x) || Float.isNaN(vector.y)) + return false; + return !Float.isInfinite(vector.x) && !Float.isInfinite(vector.y); + } + + public static boolean isZeroVector(Vector2f vector) { + + return (vector.x == 0) && + (vector.y == 0); + + } + /** + * length calculates the magnitude of this vector. + * + * @return the length or magnitude of the vector. + */ + public float length() { + return FastMath.sqrt(lengthSquared()); + } + + /** + * lengthSquared calculates the squared value of the magnitude + * of the vector. + * + * @return the magnitude squared of the vector. + */ + public float lengthSquared() { + return x * x + y * y; + } + + /** + * distanceSquared calculates the distance squared between this + * vector and vector v. + * + * @param v + * the second vector to determine the distance squared. + * @return the distance squared between the two vectors. + */ + public float distanceSquared(Vector2f v) { + double dx = x - v.x; + double dy = y - v.y; + return (float) (dx * dx + dy * dy); + } + + /** + * distanceSquared calculates the distance squared between this + * vector and vector v. + * + * @return the distance squared between the two vectors. + */ + public float distanceSquared(float otherX, float otherY) { + double dx = x - otherX; + double dy = y - otherY; + return (float) (dx * dx + dy * dy); + } + + /** + * distance calculates the distance between this vector and + * vector v. + * + * @param v + * the second vector to determine the distance. + * @return the distance between the two vectors. + */ + public float distance(Vector2f v) { + return FastMath.sqrt(distanceSquared(v)); + } + + /** + * mult multiplies this vector by a scalar. The resultant + * vector is returned. + * + * @param scalar + * the value to multiply this vector by. + * @return the new vector. + */ + public Vector2f mult(float scalar) { + return new Vector2f(x * scalar, y * scalar); + } + + /** + * multLocal multiplies this vector by a scalar internally, and + * returns a handle to this vector for easy chaining of calls. + * + * @param scalar + * the value to multiply this vector by. + * @return this + */ + public Vector2f multLocal(float scalar) { + x *= scalar; + y *= scalar; + return this; + } + + /** + * multLocal multiplies a provided vector to this vector + * internally, and returns a handle to this vector for easy chaining of + * calls. If the provided vector is null, null is returned. + * + * @param vec + * the vector to mult to this vector. + * @return this + */ + public Vector2f multLocal(Vector2f vec) { + if (null == vec) { + return null; + } + x *= vec.x; + y *= vec.y; + return this; + } + + /** + * Multiplies this Vector2f's x and y by the scalar and stores the result in + * product. The result is returned for chaining. Similar to + * product=this*scalar; + * + * @param scalar + * The scalar to multiply by. + * @param product + * The vector2f to store the result in. + * @return product, after multiplication. + */ + public Vector2f mult(float scalar, Vector2f product) { + if (null == product) { + product = new Vector2f(); + } + + product.x = x * scalar; + product.y = y * scalar; + return product; + } + + /** + * divide divides the values of this vector by a scalar and + * returns the result. The values of this vector remain untouched. + * + * @param scalar + * the value to divide this vectors attributes by. + * @return the result Vector. + */ + public Vector2f divide(float scalar) { + return new Vector2f(x / scalar, y / scalar); + } + + /** + * divideLocal divides this vector by a scalar internally, and + * returns a handle to this vector for easy chaining of calls. Dividing by + * zero will result in an exception. + * + * @param scalar + * the value to divides this vector by. + * @return this + */ + public Vector2f divideLocal(float scalar) { + x /= scalar; + y /= scalar; + return this; + } + + /** + * negate returns the negative of this vector. All values are + * negated and set to a new vector. + * + * @return the negated vector. + */ + public Vector2f negate() { + return new Vector2f(-x, -y); + } + + /** + * negateLocal negates the internal values of this vector. + * + * @return this. + */ + public Vector2f negateLocal() { + x = -x; + y = -y; + return this; + } + + /** + * subtract subtracts the values of a given vector from those + * of this vector creating a new vector object. If the provided vector is + * null, an exception is thrown. + * + * @param vec + * the vector to subtract from this vector. + * @return the result vector. + */ + public Vector2f subtract(Vector2f vec) { + return subtract(vec, null); + } + + /** + * subtract subtracts the values of a given vector from those + * of this vector storing the result in the given vector object. If the + * provided vector is null, an exception is thrown. + * + * @param vec + * the vector to subtract from this vector. + * @param store + * the vector to store the result in. It is safe for this to be + * the same as vec. If null, a new vector is created. + * @return the result vector. + */ + public Vector2f subtract(Vector2f vec, Vector2f store) { + if (store == null) + store = new Vector2f(); + store.x = x - vec.x; + store.y = y - vec.y; + return store; + } + + /** + * subtract subtracts the given x,y values from those of this + * vector creating a new vector object. + * + * @param valX + * value to subtract from x + * @param valY + * value to subtract from y + * @return this + */ + public Vector2f subtract(float valX, float valY) { + return new Vector2f(x - valX, y - valY); + } + + /** + * subtractLocal subtracts a provided vector to this vector + * internally, and returns a handle to this vector for easy chaining of + * calls. If the provided vector is null, null is returned. + * + * @param vec + * the vector to subtract + * @return this + */ + public Vector2f subtractLocal(Vector2f vec) { + if (null == vec) { + return null; + } + x -= vec.x; + y -= vec.y; + return this; + } + + /** + * subtractLocal subtracts the provided values from this vector + * internally, and returns a handle to this vector for easy chaining of + * calls. + * + * @param valX + * value to subtract from x + * @param valY + * value to subtract from y + * @return this + */ + public Vector2f subtractLocal(float valX, float valY) { + x -= valX; + y -= valY; + return this; + } + + /** + * normalize returns the unit vector of this vector. + * + * @return unit vector of this vector. + */ + public Vector2f normalize() { + float length = length(); + if (length != 0) { + return divide(length); + } + + return divide(1); + } + + /** + * normalizeLocal makes this vector into a unit vector of + * itself. + * + * @return this. + */ + public Vector2f normalizeLocal() { + float length = length(); + if (length != 0) { + return divideLocal(length); + } + + return divideLocal(1); + } + + /** + * smallestAngleBetween returns (in radians) the minimum angle + * between two vectors. It is assumed that both this vector and the given + * vector are unit vectors (iow, normalized). + * + * @param otherVector + * a unit vector to find the angle against + * @return the angle in radians. + */ + public float smallestAngleBetween(Vector2f otherVector) { + float dotProduct = dot(otherVector); + return FastMath.acos(dotProduct); + } + + /** + * angleBetween returns (in radians) the angle required to + * rotate a ray represented by this vector to lie colinear to a ray + * described by the given vector. It is assumed that both this vector and + * the given vector are unit vectors (iow, normalized). + * + * @param otherVector + * the "destination" unit vector + * @return the angle in radians. + */ + public float angleBetween(Vector2f otherVector) { + return FastMath.atan2(otherVector.y, otherVector.x) + - FastMath.atan2(y, x); + } + + public float getX() { + return x; + } + + public void setX(float x) { + this.x = x; + } + + public float getY() { + return y; + } + + public void setY(float y) { + this.y = y; + } + + /** + * getAngle returns (in radians) the angle represented by this + * Vector2f as expressed by a conversion from rectangular coordinates ( + * xy) to polar coordinates + * (r, theta). + * + * @return the angle in radians. [-pi, pi) + */ + public float getAngle() { + return -FastMath.atan2(y, x); + } + + /** + * zero resets this vector's data to zero internally. + */ + public void zero() { + x = y = 0; + } + + @Override + public Vector2f clone() { + try { + return (Vector2f) super.clone(); + } catch (CloneNotSupportedException e) { + throw new AssertionError(); // can not happen + } + } + + /** + * Saves this Vector2f into the given float[] object. + * + * @param floats + * The float[] to take this Vector2f. If null, a new float[2] is + * created. + * @return The array, with X, Y float values in that order + */ + public float[] toArray(float[] floats) { + if (floats == null) { + floats = new float[2]; + } + floats[0] = x; + floats[1] = y; + return floats; + } + + /** + * are these two vectors the same? they are is they both have the same x and + * y values. + * + * @param o + * the object to compare for equality + * @return true if they are equal + */ + @Override + public boolean equals(Object o) { + if (!(o instanceof Vector2f)) { + return false; + } + + if (this == o) { + return true; + } + + Vector2f comp = (Vector2f) o; + if (Float.compare(x, comp.x) != 0) + return false; + return Float.compare(y, comp.y) == 0; + } + + public void rotateAroundOrigin(float angle, boolean cw) { + if (cw) + angle = -angle; + float newX = FastMath.cos(angle) * x - FastMath.sin(angle) * y; + float newY = FastMath.sin(angle) * x + FastMath.cos(angle) * y; + x = newX; + y = newY; + } + + public synchronized float getLat() { + return x; + } + + public synchronized float getLong() { + return y; + } + + public synchronized void setLat(float lat) { + this.x = lat; + } + + public synchronized void setLong(float lon) { + this.y = lon; + } + +} diff --git a/src/engine/math/Vector3f.java b/src/engine/math/Vector3f.java new file mode 100644 index 00000000..06d95d19 --- /dev/null +++ b/src/engine/math/Vector3f.java @@ -0,0 +1,1188 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.math; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + + +/** + * Vector3f defines a Vector for a three float value tuple. + * Vector3f can represent any three dimensional value, such as a + * vertex, a normal, etc. Utility methods are also included to aid in + * mathematical calculations. + * + */ + +public class Vector3f { + public final static Vector3f ZERO = new Vector3f(0, 0, 0); + + public final static Vector3f UNIT_X = new Vector3f(1, 0, 0); + public final static Vector3f UNIT_Y = new Vector3f(0, 1, 0); + public final static Vector3f UNIT_Z = new Vector3f(0, 0, 1); + public final static Vector3f UNIT_XYZ = new Vector3f(1, 1, 1); + + /** + * the x value of the vector. + */ + public float x; + + /** + * the y value of the vector. + */ + public float y; + + /** + * the z value of the vector. + */ + public float z; + + /** + * Constructor instantiates a new Vector3f with default values + * of (0,0,0). + * + */ + public Vector3f() { + x = y = z = 0.0f; + } + + /** + * Constructor instantiates a new Vector3f with provides + * values. + * + * @param x + * the x value of the vector. + * @param y + * the y value of the vector. + * @param z + * the z value of the vector. + */ + public Vector3f(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + } + + public Vector3f(Vector3fImmutable original) { + this.x = original.x; + this.y = original.y; + this.z = original.z; + } + + /** + * Constructor instantiates a new Vector3f that is a copy of + * the provided vector + * + * @param copy + * The Vector3f to copy + */ + public Vector3f(Vector3f copy) { + this.set(copy); + } + + /** + * set sets the x,y,z values of the vector based on passed + * parameters. + * + * @param x + * the x value of the vector. + * @param y + * the y value of the vector. + * @param z + * the z value of the vector. + * @return this vector + */ + public Vector3f set(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + /** + * set sets the x,y,z values of the vector by copying the + * supplied vector. + * + * @param vect + * the vector to copy. + * @return this vector + */ + public Vector3f set(Vector3f vect) { + this.x = vect.x; + this.y = vect.y; + this.z = vect.z; + return this; + } + + /** + * + * add adds a provided vector to this vector creating a + * resultant vector which is returned. If the provided vector is null, null + * is returned. + * + * Neither 'this' nor 'vec' are modified. + * + * @param vec + * the vector to add to this. + * @return the resultant vector. + */ + public Vector3f add(Vector3f vec) { + if (null == vec) { + return null; + } + return new Vector3f(x + vec.x, y + vec.y, z + vec.z); + } + + /** + * + * add adds the values of a provided vector storing the values + * in the supplied vector. + * + * @param vec + * the vector to add to this + * @param result + * the vector to store the result in + * @return result returns the supplied result vector. + */ + public Vector3f add(Vector3f vec, Vector3f result) { + result.x = x + vec.x; + result.y = y + vec.y; + result.z = z + vec.z; + return result; + } + + /** + * addLocal adds a provided vector to this vector internally, + * and returns a handle to this vector for easy chaining of calls. If the + * provided vector is null, null is returned. + * + * @param vec + * the vector to add to this vector. + * @return this + */ + public Vector3f addLocal(Vector3f vec) { + if (null == vec) { + return null; + } + x += vec.x; + y += vec.y; + z += vec.z; + return this; + } + + /** + * + * add adds the provided values to this vector, creating a new + * vector that is then returned. + * + * @param addX + * the x value to add. + * @param addY + * the y value to add. + * @param addZ + * the z value to add. + * @return the result vector. + */ + public Vector3f add(float addX, float addY, float addZ) { + return new Vector3f(x + addX, y + addY, z + addZ); + } + + /** + * addLocal adds the provided values to this vector internally, + * and returns a handle to this vector for easy chaining of calls. + * + * @param addX + * value to add to x + * @param addY + * value to add to y + * @param addZ + * value to add to z + * @return this + */ + public Vector3f addLocal(float addX, float addY, float addZ) { + x += addX; + y += addY; + z += addZ; + return this; + } + + /** + * + * scaleAdd multiplies this vector by a scalar then adds the + * given Vector3f. + * + * @param scalar + * the value to multiply this vector by. + * @param add + * the value to add + */ + public void scaleAdd(float scalar, Vector3f add) { + x = x * scalar + add.x; + y = y * scalar + add.y; + z = z * scalar + add.z; + } + + /** + * + * scaleAdd multiplies the given vector by a scalar then adds + * the given vector. + * + * @param scalar + * the value to multiply this vector by. + * @param mult + * the value to multiply the scalar by + * @param add + * the value to add + */ + public void scaleAdd(float scalar, Vector3f mult, Vector3f add) { + this.x = mult.x * scalar + add.x; + this.y = mult.y * scalar + add.y; + this.z = mult.z * scalar + add.z; + } + + /** + * + * dot calculates the dot product of this vector with a + * provided vector. If the provided vector is null, 0 is returned. + * + * @param vec + * the vector to dot with this vector. + * @return the resultant dot product of this vector and a given vector. + */ + public float dot(Vector3f vec) { + if (null == vec) { + return 0; + } + return x * vec.x + y * vec.y + z * vec.z; + } + + /** + * Returns a new vector which is the cross product of this vector with the + * specified vector. + *

+ * Neither 'this' nor v are modified. The starting value of 'result' + *

+ * + * @param v + * the vector to take the cross product of with this. + * @return the cross product vector. + */ + public Vector3f cross(Vector3f v) { + return cross(v, null); + } + + /** + * cross calculates the cross product of this vector with a + * parameter vector v. The result is stored in result + *

+ * Neither 'this' nor v are modified. The starting value of 'result' (if + * any) is ignored. + *

+ * + * @param v + * the vector to take the cross product of with this. + * @param result + * the vector to store the cross product result. + * @return result, after receiving the cross product vector. + */ + public Vector3f cross(Vector3f v, Vector3f result) { + return cross(v.x, v.y, v.z, result); + } + + /** + * cross calculates the cross product of this vector with a + * Vector comprised of the specified other* elements. The result is stored + * in result, without modifying either 'this' or the 'other*' + * values. + * + * @param otherX + * x component of the vector to take the cross product of with + * this. + * @param otherY + * y component of the vector to take the cross product of with + * this. + * @param otherZ + * z component of the vector to take the cross product of with + * this. + * @param result + * the vector to store the cross product result. + * @return result, after receiving the cross product vector. + */ + public Vector3f cross(float otherX, float otherY, float otherZ, Vector3f result) { + if (result == null) + result = new Vector3f(); + float resX = ((y * otherZ) - (z * otherY)); + float resY = ((z * otherX) - (x * otherZ)); + float resZ = ((x * otherY) - (y * otherX)); + result.set(resX, resY, resZ); + return result; + } + + /** + * crossLocal calculates the cross product of this vector with + * a parameter vector v. + * + * @param v + * the vector to take the cross product of with this. + * @return this. + */ + public Vector3f crossLocal(Vector3f v) { + return crossLocal(v.x, v.y, v.z); + } + + /** + * crossLocal calculates the cross product of this vector with + * a parameter vector v. + * + * @param otherX + * x component of the vector to take the cross product of with + * this. + * @param otherY + * y component of the vector to take the cross product of with + * this. + * @param otherZ + * z component of the vector to take the cross product of with + * this. + * @return this. + */ + public Vector3f crossLocal(float otherX, float otherY, float otherZ) { + float tempx = (y * otherZ) - (z * otherY); + float tempy = (z * otherX) - (x * otherZ); + z = (x * otherY) - (y * otherX); + x = tempx; + y = tempy; + return this; + } + + /** + * length calculates the magnitude of this vector. + * + * @return the length or magnitude of the vector. + */ + public float length() { + return FastMath.sqrt(lengthSquared()); + } + + /** + * lengthSquared calculates the squared value of the magnitude + * of the vector. + * + * @return the magnitude squared of the vector. + */ + public float lengthSquared() { + return x * x + y * y + z * z; + } + + /** + * distanceSquared calculates the distance squared between this + * vector and vector v. + * + * @param v + * the second vector to determine the distance squared. + * @return the distance squared between the two vectors. + */ + public float distanceSquared(Vector3f v) { + double dx = x - v.x; + double dy = y - v.y; + double dz = z - v.z; + return (float) (dx * dx + dy * dy + dz * dz); + } + + public float distanceSquared2D(Vector3f v) { + double dx = x - v.x; + double dz = z - v.z; + return (float) (dx * dx + dz * dz); + } + + /** + * distance calculates the distance between this vector and + * vector v. + * + * @param v + * the second vector to determine the distance. + * @return the distance between the two vectors. + */ + public float distance(Vector3f v) { + return FastMath.sqrt(distanceSquared(v)); + } + + public float distance2D(Vector3f v) { + return FastMath.sqrt(distanceSquared2D(v)); + } + + /** + * mult multiplies this vector by a scalar. The resultant + * vector is returned. "this" is not modified. + * + * @param scalar + * the value to multiply this vector by. + * @return the new vector. + */ + public Vector3f mult(float scalar) { + return new Vector3f(x * scalar, y * scalar, z * scalar); + } + + /** + * + * mult multiplies this vector by a scalar. The resultant + * vector is supplied as the second parameter and returned. "this" is not + * modified. + * + * @param scalar + * the scalar to multiply this vector by. + * @param product + * the product to store the result in. + * @return product + */ + public Vector3f mult(float scalar, Vector3f product) { + if (null == product) { + product = new Vector3f(); + } + + product.x = x * scalar; + product.y = y * scalar; + product.z = z * scalar; + return product; + } + + /** + * multLocal multiplies this vector by a scalar internally, and + * returns a handle to this vector for easy chaining of calls. + * + * @param scalar + * the value to multiply this vector by. + * @return this + */ + public Vector3f multLocal(float scalar) { + x *= scalar; + y *= scalar; + z *= scalar; + return this; + } + + /** + * multLocal multiplies a provided vector to this vector + * internally, and returns a handle to this vector for easy chaining of + * calls. If the provided vector is null, null is returned. The provided + * 'vec' is not modified. + * + * @param vec + * the vector to mult to this vector. + * @return this + */ + public Vector3f multLocal(Vector3f vec) { + if (null == vec) { + return null; + } + x *= vec.x; + y *= vec.y; + z *= vec.z; + return this; + } + + /** + * Returns a new Vector instance comprised of elements which are the product + * of the corresponding vector elements. (N.b. this is not a cross product). + *

+ * Neither 'this' nor 'vec' are modified. + *

+ * + * @param vec + * the vector to mult to this vector. + */ + public Vector3f mult(Vector3f vec) { + if (null == vec) { + return null; + } + return mult(vec, null); + } + + /** + * Multiplies a provided 'vec' vector with this vector. If the specified + * 'store' is null, then a new Vector instance is returned. Otherwise, + * 'store' with replaced values will be returned, to facilitate chaining. + *

+ *

+ *'This' is not modified; and the starting value of 'store' (if any) is + * ignored (and over-written). + *

+ * The resultant Vector is comprised of elements which are the product of + * the corresponding vector elements. (N.b. this is not a cross product). + *

+ * + * @param vec + * the vector to mult to this vector. + * @param store + * result vector (null to create a new vector) + * @return 'store', or a new Vector3f + */ + public Vector3f mult(Vector3f vec, Vector3f store) { + if (null == vec) { + return null; + } + if (store == null) + store = new Vector3f(); + return store.set(x * vec.x, y * vec.y, z * vec.z); + } + + /** + * divide divides the values of this vector by a scalar and + * returns the result. The values of this vector remain untouched. + * + * @param scalar + * the value to divide this vectors attributes by. + * @return the result Vector. + */ + public Vector3f divide(float scalar) { + scalar = 1f / scalar; + return new Vector3f(x * scalar, y * scalar, z * scalar); + } + + /** + * divideLocal divides this vector by a scalar internally, and + * returns a handle to this vector for easy chaining of calls. Dividing by + * zero will result in an exception. + * + * @param scalar + * the value to divides this vector by. + * @return this + */ + public Vector3f divideLocal(float scalar) { + scalar = 1f / scalar; + x *= scalar; + y *= scalar; + z *= scalar; + return this; + } + + /** + * divide divides the values of this vector by a scalar and + * returns the result. The values of this vector remain untouched. + * + * @param scalar + * the value to divide this vectors attributes by. + * @return the result Vector. + */ + public Vector3f divide(Vector3f scalar) { + return new Vector3f(x / scalar.x, y / scalar.y, z / scalar.z); + } + + /** + * divideLocal divides this vector by a scalar internally, and + * returns a handle to this vector for easy chaining of calls. Dividing by + * zero will result in an exception. + * + * @param scalar + * the value to divides this vector by. + * @return this + */ + public Vector3f divideLocal(Vector3f scalar) { + x /= scalar.x; + y /= scalar.y; + z /= scalar.z; + return this; + } + + /** + * + * negate returns the negative of this vector. All values are + * negated and set to a new vector. + * + * @return the negated vector. + */ + public Vector3f negate() { + return new Vector3f(-x, -y, -z); + } + + /** + * + * negateLocal negates the internal values of this vector. + * + * @return this. + */ + public Vector3f negateLocal() { + x = -x; + y = -y; + z = -z; + return this; + } + + /** + * + * subtract subtracts the values of a given vector from those + * of this vector creating a new vector object. If the provided vector is + * null, null is returned. + * + * @param vec + * the vector to subtract from this vector. + * @return the result vector. + */ + public Vector3f subtract(Vector3f vec) { + return new Vector3f(x - vec.x, y - vec.y, z - vec.z); + } + + public Vector3f subtract2D(Vector3f vec) { + return new Vector3f(x - vec.x, 0, z - vec.z); + } + + /** + * subtractLocal subtracts a provided vector to this vector + * internally, and returns a handle to this vector for easy chaining of + * calls. If the provided vector is null, null is returned. + * + * @param vec + * the vector to subtract + * @return this + */ + public Vector3f subtractLocal(Vector3f vec) { + if (null == vec) { + return null; + } + x -= vec.x; + y -= vec.y; + z -= vec.z; + return this; + } + + /** + * + * subtract + * + * @param vec + * the vector to subtract from this + * @param result + * the vector to store the result in + * @return result + */ + public Vector3f subtract(Vector3f vec, Vector3f result) { + if (result == null) { + result = new Vector3f(); + } + result.x = x - vec.x; + result.y = y - vec.y; + result.z = z - vec.z; + return result; + } + + /** + * + * subtract subtracts the provided values from this vector, + * creating a new vector that is then returned. + * + * @param subtractX + * the x value to subtract. + * @param subtractY + * the y value to subtract. + * @param subtractZ + * the z value to subtract. + * @return the result vector. + */ + public Vector3f subtract(float subtractX, float subtractY, float subtractZ) { + return new Vector3f(x - subtractX, y - subtractY, z - subtractZ); + } + + /** + * subtractLocal subtracts the provided values from this vector + * internally, and returns a handle to this vector for easy chaining of + * calls. + * + * @param subtractX + * the x value to subtract. + * @param subtractY + * the y value to subtract. + * @param subtractZ + * the z value to subtract. + * @return this + */ + public Vector3f subtractLocal(float subtractX, float subtractY, float subtractZ) { + x -= subtractX; + y -= subtractY; + z -= subtractZ; + return this; + } + + /** + * normalize returns the unit vector of this vector. + * + * @return unit vector of this vector. + */ + public Vector3f normalize() { + float length = length(); + if (length != 0) { + return divide(length); + } + + return divide(1); + } + + /** + * normalizeLocal makes this vector into a unit vector of + * itself. + * + * @return this. + */ + public Vector3f normalizeLocal() { + float length = length(); + if (length != 0) { + return divideLocal(length); + } + + return this; + } + + /** + * zero resets this vector's data to zero internally. + */ + public void zero() { + x = y = z = 0; + } + + /** + * angleBetween returns (in radians) the angle between two + * vectors. It is assumed that both this vector and the given vector are + * unit vectors (iow, normalized). + * + * @param otherVector + * a unit vector to find the angle against + * @return the angle in radians. + */ + public float angleBetween(Vector3f otherVector) { + float dotProduct = dot(otherVector); + return FastMath.acos(dotProduct); + } + + /** + * Sets this vector to the interpolation by changeAmnt from this to the + * finalVec this=(1-changeAmnt)*this + changeAmnt * finalVec + * + * @param finalVec + * The final vector to interpolate towards + * @param changeAmnt + * An amount between 0.0 - 1.0 representing a percentage change + * from this towards finalVec + */ + public void interpolate(Vector3f finalVec, float changeAmnt) { + this.x = (1 - changeAmnt) * this.x + changeAmnt * finalVec.x; + this.y = (1 - changeAmnt) * this.y + changeAmnt * finalVec.y; + this.z = (1 - changeAmnt) * this.z + changeAmnt * finalVec.z; + } + + + public Vector3f lerp(Vector3f finalVec, float changeAmnt) { + float x = (1 - changeAmnt) * this.x + changeAmnt * finalVec.x; + float y = (1 - changeAmnt) * this.y + changeAmnt * finalVec.y; + float z = (1 - changeAmnt) * this.z + changeAmnt * finalVec.z; + return new Vector3f(x,y,z); + } + + /** + * Sets this vector to the interpolation by changeAmnt from beginVec to + * finalVec this=(1-changeAmnt)*beginVec + changeAmnt * finalVec + * + * @param beginVec + * the beginning vector (changeAmnt=0) + * @param finalVec + * The final vector to interpolate towards + * @param changeAmnt + * An amount between 0.0 - 1.0 representing a percentage change + * from beginVec towards finalVec + */ + public void interpolate(Vector3f beginVec, Vector3f finalVec, float changeAmnt) { + this.x = (1 - changeAmnt) * beginVec.x + changeAmnt * finalVec.x; + this.y = (1 - changeAmnt) * beginVec.y + changeAmnt * finalVec.y; + this.z = (1 - changeAmnt) * beginVec.z + changeAmnt * finalVec.z; + } + + /** + * Check a vector... if it is null or its floats are NaN or infinite, return + * false. Else return true. + * + * @param vector + * the vector to check + * @return true or false as stated above. + */ + public static boolean isValidVector(Vector3f vector) { + if (vector == null) + return false; + if (Float.isNaN(vector.x) || Float.isNaN(vector.y) || Float.isNaN(vector.z)) + return false; + return !Float.isInfinite(vector.x) && !Float.isInfinite(vector.y) && !Float.isInfinite(vector.z); + } + + public static void generateOrthonormalBasis(Vector3f u, Vector3f v, Vector3f w) { + w.normalizeLocal(); + generateComplementBasis(u, v, w); + } + + public static void generateComplementBasis(Vector3f u, Vector3f v, Vector3f w) { + float fInvLength; + + if (FastMath.abs(w.x) >= FastMath.abs(w.y)) { + // w.x or w.z is the largest magnitude component, swap them + fInvLength = FastMath.invSqrt(w.x * w.x + w.z * w.z); + u.x = -w.z * fInvLength; + u.y = 0.0f; + u.z = +w.x * fInvLength; + v.x = w.y * u.z; + v.y = w.z * u.x - w.x * u.z; + v.z = -w.y * u.x; + } else { + // w.y or w.z is the largest magnitude component, swap them + fInvLength = FastMath.invSqrt(w.y * w.y + w.z * w.z); + u.x = 0.0f; + u.y = +w.z * fInvLength; + u.z = -w.y * fInvLength; + v.x = w.y * u.z - w.z * u.y; + v.y = -w.x * u.z; + v.z = w.x * u.y; + } + } + + @Override + public Vector3f clone() { + try { + return (Vector3f) super.clone(); + } catch (CloneNotSupportedException e) { + throw new AssertionError(); // can not happen + } + } + + /** + * Saves this Vector3f into the given float[] object. + * + * @param floats + * The float[] to take this Vector3f. If null, a new float[3] is + * created. + * @return The array, with X, Y, Z float values in that order + */ + public float[] toArray(float[] floats) { + if (floats == null) { + floats = new float[3]; + } + floats[0] = x; + floats[1] = y; + floats[2] = z; + return floats; + } + + /** + * are these two vectors the same? they are is they both have the same x,y, + * and z values. + * + * @param o + * the object to compare for equality + * @return true if they are equal + */ + @Override + public boolean equals(Object o) { + if (!(o instanceof Vector3f)) { + return false; + } + + if (this == o) { + return true; + } + + Vector3f comp = (Vector3f) o; + if (Float.compare(x, comp.x) != 0) + return false; + if (Float.compare(y, comp.y) != 0) + return false; + return Float.compare(z, comp.z) == 0; + } + + /** + * hashCode returns a unique code for this vector object based + * on it's values. If two vectors are logically equivalent, they will return + * the same hash code value. + * + * @return the hash code value of this vector. + */ + @Override + public int hashCode() { + int hash = 37; + hash += 37 * hash + Float.floatToIntBits(x); + hash += 37 * hash + Float.floatToIntBits(y); + hash += 37 * hash + Float.floatToIntBits(z); + return hash; + } + + /** + * Used with serialization. Not to be called manually. + * + * @param in + * input + * @throws IOException + * @throws ClassNotFoundException + * @see java.io.Externalizable + */ + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + x = in.readFloat(); + y = in.readFloat(); + z = in.readFloat(); + } + + /** + * Used with serialization. Not to be called manually. + * + * @param out + * output + * @throws IOException + * @see java.io.Externalizable + */ + public void writeExternal(ObjectOutput out) throws IOException { + out.writeFloat(x); + out.writeFloat(y); + out.writeFloat(z); + } + + public float getX() { + return x; + } + + public void setX(float x) { + this.x = x; + } + + public float getY() { + return y; + } + + public void setY(float y) { + this.y = y; + } + + public float getZ() { + return z; + } + + public void setZ(float z) { + this.z = z; + } + + /** + * @param index + * @return x value if index == 0, y value if index == 1 or z value if index + * == 2 + * @throws IllegalArgumentException + * if index is not one of 0, 1, 2. + */ + public float get(int index) { + switch (index) { + case 0: + return x; + case 1: + return y; + case 2: + return z; + } + throw new IllegalArgumentException("index must be either 0, 1 or 2"); + } + + /** + * @param index + * which field index in this vector to set. + * @param value + * to set to one of x, y or z. + * @throws IllegalArgumentException + * if index is not one of 0, 1, 2. + */ + public void set(int index, float value) { + switch (index) { + case 0: + x = value; + return; + case 1: + y = value; + return; + case 2: + z = value; + return; + } + throw new IllegalArgumentException("index must be either 0, 1 or 2"); + } + + /** + * Gets an offset from this position based on rotation around Y(up/down)-axis. + * + * @param rotation + * Rotation in radians + * @param xOffset + * Amount to offset along x axis (left negative, right positive) + * @param yOffset + * Amount to offset along y axis (down negative, up positive) + * @param zOffset + * Amount to offset along z axis (backwards negative, forwards positive) + * @param invertZ + * whether to invert the z axis + */ + public Vector3f getOffset(float rotation, float xOffset, float yOffset, float zOffset, boolean invertZ) { + float sin = FastMath.sin(rotation); + float cos = FastMath.cos(rotation); + Vector3f faceDir = new Vector3f(sin, 0f, cos); + Vector3f crossDir = new Vector3f(cos, 0f, sin); + faceDir.multLocal(zOffset); + crossDir.multLocal(xOffset); + if (invertZ) { + faceDir.z = -faceDir.z; + crossDir.z = -crossDir.z; + } + Vector3f loc = new Vector3f(this); + loc.addLocal(faceDir); + loc.addLocal(crossDir); + loc.y += yOffset; + return loc; + } + + /** + * Returns the 2D face direction from rotation. + * + * @param rotation + * Rotation in radians + */ + public static Vector3f getFaceDir(float rotation) { + return new Vector3f(FastMath.sin(rotation), 0f, FastMath.cos(rotation)); + } + + /** + * Returns the 2D cross direction (perpendicular face direction) from rotation. + * + * @param rotation + * Rotation in radians + */ + public static Vector3f getCrossDir(float rotation) { + return new Vector3f(FastMath.cos(rotation), 0f, FastMath.sin(rotation)); + } + + /** + * Returns the 2D rotation (around Y-axis) in radians. + * + * @return + */ + public float getRotation() { + return 3.14f + FastMath.atan2(-x, -z); + } + + /** + * Gets the XYZ component of this Vector3f + * + * @return + */ + public Vector2f getLatLong() { + return new Vector2f(this.x, this.z); + } + + public synchronized float getLat() { + return x; + } + + public synchronized float getLong() { + return z; + } + + public synchronized float getAlt() { + return y; + } + + public synchronized void setLat(float lat) { + this.x = lat; + } + + public synchronized void setLong(float lon) { + this.z = lon; + } + + public synchronized void setAlt(float alt) { + this.y = alt; + } + + public static Vector3f rotateAroundPoint(Vector3f origin, Vector3f point, double angle) { + + float angleRadians; + double modifiedAngle; + + // Convert angle to radians + + modifiedAngle = angle; + + if (angle < 0) + modifiedAngle = 360 + modifiedAngle; + + angleRadians = (float) Math.toRadians(modifiedAngle); + + return rotateAroundPoint(origin, point, angleRadians); + } + + public static Vector3f rotateAroundPoint(Vector3f origin, Vector3f point, float radians) { + + Vector3f outVector; + Vector3f directionVector; + Quaternion angleRotation; + + // Build direction vector relative to origin + + directionVector = new Vector3f(point.subtract(origin)); + + // Build quaternion rotation + + angleRotation = new Quaternion().fromAngleAxis(radians, new Vector3f(0,1,0)); + + // Apply rotation to direction vector + + directionVector = angleRotation.mult(directionVector); + + // Translate from origin back to new rotated point + + outVector = origin.add(directionVector); + + return outVector; + + } + + @Override + public String toString() { + String out = ""; + out += "x=" + x + ", "; + out += "y=" + y + ", "; + out += "z=" + z; + return out; + } + + public static Vector3f min(Vector3f vectorA, Vector3f vectorB) { + + return new Vector3f(Math.min(vectorA.x, vectorB.x), + Math.min(vectorA.y, vectorB.y), + Math.min(vectorA.z, vectorB.z)); + } + + public static Vector3f max(Vector3f vectorA, Vector3f vectorB) { + + return new Vector3f(Math.max(vectorA.x, vectorB.x), + Math.max(vectorA.y, vectorB.y), + Math.max(vectorA.z, vectorB.z)); + } + + public static Vector3f rotateAroundPoint(Vector3f origin, Vector3f point,Quaternion angleRotation) { + + Vector3f outVector; + Vector3f directionVector; + // Build direction vector relative to origin + directionVector = new Vector3f(point.subtract(origin)); + directionVector = angleRotation.mult(directionVector); + + // Translate from origin back to new rotated point + + outVector = origin.add(directionVector); + + return outVector; + + } + +} diff --git a/src/engine/math/Vector3fImmutable.java b/src/engine/math/Vector3fImmutable.java new file mode 100644 index 00000000..5e9bd32b --- /dev/null +++ b/src/engine/math/Vector3fImmutable.java @@ -0,0 +1,595 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.math; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.util.concurrent.ThreadLocalRandom; + +import static engine.math.FastMath.sqr; + +public class Vector3fImmutable { + + public final float x, y, z; + + public Vector3fImmutable() { + x = y = z = 0.0f; + } + + public Vector3fImmutable(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + } + + public Vector3fImmutable(Vector3f original) { + this.x = original.x; + this.y = original.y; + this.z = original.z; + } + + public Vector3fImmutable(Vector3fImmutable original) { + this.x = original.x; + this.y = original.y; + this.z = original.z; + } + + public boolean isInsideCircle(Vector3fImmutable circleCenter, float radius) { + + return (circleCenter.distanceSquared2D(this) < sqr(radius)); + } + + public Vector3fImmutable add(Vector3f vec) { + if (null == vec) + return null; + return new Vector3fImmutable(x + vec.x, y + vec.y, z + vec.z); + } + + public Vector3fImmutable add(Vector3fImmutable vec) { + if (null == vec) + return null; + return new Vector3fImmutable(x + vec.x, y + vec.y, z + vec.z); + } + + public Vector3fImmutable add(float x, float y, float z) { + return new Vector3fImmutable(this.x + x, this.y + y, this.z + z); + } + + public Vector3fImmutable scaleAdd(float scalar, Vector3fImmutable add) { + return new Vector3fImmutable(x * scalar + add.x, y * scalar + add.y , z + * scalar + add.z); + } + + public static Vector3fImmutable scaleAdd(float scalar, Vector3fImmutable mult, + Vector3fImmutable add) { + return new Vector3fImmutable(mult.x * scalar + add.x, mult.y * scalar + + add.y, mult.z * scalar + add.z); + } + + public float dot(Vector3fImmutable vec) { + if (null == vec) { + return 0.0f; + } + + return x * vec.x + y * vec.y + z * vec.z; + } + + public float dot2D(Vector3fImmutable vec) { + if (null == vec) { + return 0.0f; + } + + return x * vec.x + z * vec.z; + } + + public Vector3fImmutable cross(Vector3fImmutable v) { + return cross(v.x, v.y, v.z); + } + + public Vector3fImmutable cross(float x, float y, float z) { + return new Vector3fImmutable(this.y * z - this.z * y, this.z * x + - this.x * z, this.x * y - this.y * x); + } + + public float length() { + return FastMath.sqrt(lengthSquared()); + } + + public float lengthSquared() { + return x * x + y * y + z * z; + } + + public float distanceSquared(Vector3fImmutable v) { + double dx = x - v.x; + double dy = y - v.y; + double dz = z - v.z; + return (float) (dx * dx + dy * dy + dz * dz); + } + + public float magnitude() { + return FastMath.sqrt(sqrMagnitude()); + } + + public float sqrMagnitude() { + return x * x + y * y + z * z; + } + + public Vector3fImmutable moveTowards (Vector3fImmutable target, float maxDistanceDelta) + { + Vector3fImmutable outVector; + + Vector3fImmutable direction = target.subtract2D(this); + float magnitude = direction.magnitude(); + + if (magnitude <= maxDistanceDelta || magnitude == 0f) + { + return target; + } + + outVector = direction.divide(magnitude).mult(maxDistanceDelta); + outVector = this.add(outVector); + return outVector; + } + + public float distanceSquared2D(Vector3fImmutable v) { + double dx = x - v.x; + double dz = z - v.z; + return (float) (dx * dx + dz * dz); + } + + public float distance(Vector3fImmutable v) { + return FastMath.sqrt(distanceSquared(v)); + } + + public float distance2D(Vector3fImmutable v) { + return FastMath.sqrt(distanceSquared2D(v)); + } + + public Vector3fImmutable mult(float scalar) { + return new Vector3fImmutable(x * scalar, y * scalar, z * scalar); + } + + public Vector3fImmutable mult(Vector3fImmutable vec) { + if (null == vec) { + return null; + } + + return new Vector3fImmutable(x * vec.x, y * vec.y, z * vec.z); + } + + public Vector3fImmutable divide(float scalar) { + scalar = 1f / scalar; + return new Vector3fImmutable(x * scalar, y * scalar, z * scalar); + } + + public Vector3fImmutable divide(Vector3fImmutable scalar) { + return new Vector3fImmutable(x / scalar.x, y / scalar.y, z / scalar.z); + } + + public Vector3fImmutable negate() { + return new Vector3fImmutable(-x, -y, -z); + } + + public Vector3fImmutable subtract(Vector3fImmutable vec) { + return new Vector3fImmutable(x - vec.x, y - vec.y, z - vec.z); + } + + public Vector3fImmutable subtract2D(Vector3fImmutable vec) { + return new Vector3fImmutable(x - vec.x, 0, z - vec.z); + } + + public Vector3fImmutable subtract(float x, float y, float z) { + return new Vector3fImmutable(this.x - x, this.y - y, this.z - z); + } + + public Vector3fImmutable normalize() { + float length = length(); + if (length != 0) { + return divide(length); + } + + return divide(1); + } + + public float angleBetween(Vector3fImmutable otherVector) { + float dotProduct = dot(otherVector); + return FastMath.acos(dotProduct); + } + + public float angleBetween2D(Vector3fImmutable otherVector) { + float dotProduct = dot(otherVector); + return FastMath.acos(dotProduct); + } + + public Vector3fImmutable interpolate(Vector3f finalVec, float changeAmnt) { + return new Vector3fImmutable((1 - changeAmnt) * this.x + changeAmnt + * finalVec.x, (1 - changeAmnt) * this.y + changeAmnt + * finalVec.y, (1 - changeAmnt) * this.z + changeAmnt + * finalVec.z); + } + + public Vector3fImmutable interpolate(Vector3fImmutable finalVec, float changeAmnt) { + return new Vector3fImmutable((1 - changeAmnt) * this.x + changeAmnt + * finalVec.x, (1 - changeAmnt) * this.y + changeAmnt + * finalVec.y, (1 - changeAmnt) * this.z + changeAmnt + * finalVec.z); + } + + + public static boolean isValidVector(Vector3fImmutable vector) { + if (vector == null) + return false; + if (Float.isNaN(vector.x) || Float.isNaN(vector.y) + || Float.isNaN(vector.z)) + return false; + return !Float.isInfinite(vector.x) && !Float.isInfinite(vector.y) + && !Float.isInfinite(vector.z); + } + + @Override + public Vector3fImmutable clone() throws CloneNotSupportedException { + return (Vector3fImmutable) super.clone(); + } + + public float[] toArray(float[] floats) { + if (floats == null) { + floats = new float[3]; + } + floats[0] = x; + floats[1] = y; + floats[2] = z; + return floats; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Vector3fImmutable)) { + return false; + } + + if (this == o) { + return true; + } + + Vector3fImmutable comp = (Vector3fImmutable) o; + if (Float.compare(x, comp.x) != 0) + return false; + if (Float.compare(y, comp.y) != 0) + return false; + return Float.compare(z, comp.z) == 0; + } + + @Override + public int hashCode() { + int hash = 37; + hash += 37 * hash + Float.floatToIntBits(x); + hash += 37 * hash + Float.floatToIntBits(y); + hash += 37 * hash + Float.floatToIntBits(z); + return hash; + } + + public static Vector3fImmutable readExternal(ObjectInput in) + throws IOException, ClassNotFoundException { + return new Vector3fImmutable(in.readFloat(), in.readFloat(), in + .readFloat()); + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeFloat(x); + out.writeFloat(y); + out.writeFloat(z); + } + + public Vector3fImmutable getOffset(float rotation, float xOffset, float yOffset, float zOffset, boolean invertZ) { + float sin = FastMath.sin(rotation); + float cos = FastMath.cos(rotation); + Vector3f faceDir = new Vector3f(sin, 0f, cos); + Vector3f crossDir = new Vector3f(cos, 0f, sin); + faceDir.multLocal(zOffset); + crossDir.multLocal(xOffset); + if (invertZ) { + faceDir.z = -faceDir.z; + crossDir.z = -crossDir.z; + } + Vector3f loc = new Vector3f(this); + loc.addLocal(faceDir); + loc.addLocal(crossDir); + loc.y += yOffset; + return new Vector3fImmutable(loc); + } + + public float getX() { + return x; + } + + public float getY() { + return y; + } + + public float getZ() { + return z; + } + + public float get(int index) { + switch (index) { + case 0: + return x; + case 1: + return y; + case 2: + return z; + } + throw new IllegalArgumentException("index must be either 0, 1 or 2"); + } + + public Vector2f getLatLong() { + return new Vector2f(this.x, this.z); + } + + public synchronized float getLat() { + return x; + } + + public synchronized float getLong() { + return z; + } + + public synchronized float getAlt() { + return y; + } + + public Vector3fImmutable setX(float x) { + return new Vector3fImmutable(x, y, z); + } + + public Vector3fImmutable setY(float y) { + return new Vector3fImmutable(x, y, z); + } + + public Vector3fImmutable setZ(float z) { + return new Vector3fImmutable(x, y, z); + } + + public float getRotation() { + return 3.14f + FastMath.atan2(-x, -z); + } + public boolean inRange2D(Vector3fImmutable otherVec, float range){ + float distance = this.distanceSquared2D(otherVec); + return !(distance > range * range); + } + + public static String toString(Vector3fImmutable vector) { + + return vector.toString(); + } + + @Override + public String toString() { + + String outString; + + outString = "(" + this.x + '/' + this.y + '/' + this.z; + return outString; + + } + + public String toString2D() { + + String outString; + + outString = "( " + (int)this.x + " , " + (int)(this.z *-1) +" )"; + return outString; + + } + + public static Vector3fImmutable ClosestPointOnLine(Vector3fImmutable lineStart, Vector3fImmutable lineEnd, Vector3fImmutable sourcePoint) { + + Vector3fImmutable closestPoint; + Vector3fImmutable lineStartToTarget; + Vector3fImmutable lineDirection; + float lineLength; + float dotProduct; + + lineStartToTarget = sourcePoint.subtract(lineStart); + lineDirection = lineEnd.subtract(lineStart).normalize(); + lineLength = lineStart.distance2D(lineEnd); + + dotProduct = lineDirection.dot(lineStartToTarget); + + if (dotProduct <= 0) + return lineStart; + + if (dotProduct >= lineLength) + return lineEnd; + + // Project the point by advancing it along the line from + // the starting point. + + closestPoint = lineDirection.mult(dotProduct); + closestPoint = lineStart.add(closestPoint); + + return closestPoint; + } + + public Vector3fImmutable ClosestPointOnLine(Vector3fImmutable lineStart, Vector3fImmutable lineEnd) { + + Vector3fImmutable closestPoint; + Vector3fImmutable lineStartToTarget; + Vector3fImmutable lineDirection; + float lineLength; + float dotProduct; + + lineStartToTarget = this.subtract(lineStart); + lineDirection = lineEnd.subtract(lineStart).normalize(); + lineLength = lineStart.distance2D(lineEnd); + + dotProduct = lineDirection.dot(lineStartToTarget); + + if (dotProduct <= 0) + return lineStart; + + if (dotProduct >= lineLength) + return lineEnd; + + // Project the point by advancing it along the line from + // the starting point. + + closestPoint = lineDirection.mult(dotProduct); + closestPoint = lineStart.add(closestPoint); + + return closestPoint; + } + + public static Vector3fImmutable rotateAroundPoint(Vector3fImmutable origin, Vector3fImmutable point, int angle) { + + float angleRadians; + int modifiedAngle; + + // Convert angle to radians + + modifiedAngle = angle; + + if (angle < 0) + modifiedAngle = 360 + modifiedAngle; + + angleRadians = (float) Math.toRadians(modifiedAngle); + + return rotateAroundPoint(origin, point, angleRadians); + } + + public static Vector3fImmutable rotateAroundPoint(Vector3fImmutable origin, Vector3fImmutable point, float radians) { + + Vector3fImmutable outVector; + Vector3f directionVector; + Quaternion angleRotation; + + // Build direction vector relative to origin + + directionVector = new Vector3f(point.subtract(origin)); + + // Build quaternion rotation + + angleRotation = new Quaternion().fromAngleAxis(radians, new Vector3f(0,1,0)); + // Apply rotation to direction vector + + directionVector = angleRotation.mult(directionVector); + + // Translate from origin back to new rotated point + + outVector = origin.add(directionVector); + + return outVector; + + } + + public static Vector3fImmutable rotateAroundPoint(Vector3fImmutable origin, Vector3fImmutable point,Quaternion angleRotation) { + + Vector3fImmutable outVector; + Vector3f directionVector; + // Build direction vector relative to origin + directionVector = new Vector3f(point.subtract(origin)); + + // Build quaternion rotation + + + // Apply rotation to direction vector + + + directionVector = angleRotation.mult(directionVector); + + // Translate from origin back to new rotated point + + outVector = origin.add(directionVector); + + return outVector; + + } + + public static Vector3fImmutable rotateAroundPoint(Vector3fImmutable origin, Vector3fImmutable point, float w, Vector3f axis) { + + Vector3fImmutable outVector; + Vector3f directionVector; + Quaternion angleRotation; + + // Build direction vector relative to origin + + directionVector = new Vector3f(point.subtract(origin)); + + // Build quaternion rotation + + angleRotation = new Quaternion().fromAngleAxis(w, axis); + // Apply rotation to direction vector + + directionVector = angleRotation.mult(directionVector); + + // Translate from origin back to new rotated point + + outVector = origin.add(directionVector); + + return outVector; + + } + + public static Vector3fImmutable getRandomPointInCircle(Vector3fImmutable origin, float radius) { + // Member variables + + float targetAngle; + float targetRadius; + Vector3fImmutable targetPosition; + + targetAngle = (float) (ThreadLocalRandom.current().nextFloat() * Math.PI * 2); + targetRadius = (float) (Math.sqrt(ThreadLocalRandom.current().nextFloat()) * radius); + targetPosition = new Vector3fImmutable((float) (origin.x + targetRadius * Math.cos(targetAngle)), origin.y, (float) (origin.z + targetRadius * Math.sin(targetAngle))); + return targetPosition; + } + + public static Vector3fImmutable getLocBetween(Vector3fImmutable start, Vector3fImmutable end) { + // Member variables + + Vector3fImmutable faceDirection = end.subtract(start).normalize(); + float distance = end.distance(start) * .5f; + return faceDirection.scaleAdd(distance, start); + } + + public static Vector3fImmutable getRandomPointOnCircle(Vector3fImmutable origin, float radius) { + + // Member variables + + int randomAngle; + Vector3fImmutable targetPosition; + + randomAngle = ThreadLocalRandom.current().nextInt(360); + + targetPosition = new Vector3fImmutable((float) (origin.x + radius * Math.cos(randomAngle)), origin.y, (float) (origin.z + radius * Math.sin(randomAngle))); + return targetPosition; + } + + public static final Vector3fImmutable ZERO = new Vector3fImmutable(0,0,0); + + public static Vector3fImmutable transform(Vector3fImmutable origin,Vector3fImmutable point, float angle){ + + //TRANSLATE TO ORIGIN + float x1 = point.x - origin.x; + float y1 = point.z - origin.z; + + //APPLY ROTATION + float temp_x1 = (float) (x1 * Math.cos(angle) - y1 * Math.sin(angle)); + float temp_z1 = (float) (x1 * Math.sin(angle) + y1 * Math.cos(angle)); + + temp_x1 += origin.x; + temp_z1 += origin.z; + + return new Vector3fImmutable(temp_x1,point.y,temp_z1); + } + public float Lerp(Vector3fImmutable dest, float lerpFactor) + { + return dest.subtract(this).mult(lerpFactor).add(this).y; + } +} diff --git a/src/engine/net/AbstractConnection.java b/src/engine/net/AbstractConnection.java new file mode 100644 index 00000000..30d34ca8 --- /dev/null +++ b/src/engine/net/AbstractConnection.java @@ -0,0 +1,425 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net; + +import engine.job.JobManager; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; +import org.pmw.tinylog.Logger; + +import java.io.IOException; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.NotYetConnectedException; +import java.nio.channels.SocketChannel; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; + +public abstract class AbstractConnection implements +NetMsgHandler { + + private static final String error01 = "A byte buffer is being free()ed that is not of the size stored in ByteBufferPool. Size: %1%"; + private static final String error02 = "(IP=%1%): Socket has reached the maximum outbound buffer length of %2%. Moving on while we wait for data to be sent."; + + protected final SocketChannel sockChan; + protected final AbstractConnectionManager connMan; + protected final NetMsgFactory factory; + + protected long lastMsgTime = System.currentTimeMillis(); + protected long lastKeepAliveTime = System.currentTimeMillis(); + protected long lastOpcode = -1; + + protected ConcurrentLinkedQueue outbox = new ConcurrentLinkedQueue<>(); + protected ByteBuffer outBuf = null; + + protected final AtomicBoolean execTask = new AtomicBoolean(false); + + protected ConcurrentHashMap cacheList; + + protected final ReentrantLock writeLock = new ReentrantLock(); + protected final ReentrantLock readLock = new ReentrantLock(); + protected long nextWriteTime = 0L; + protected Protocol lastProtocol = Protocol.NONE; + protected static final long SOCKET_FULL_WRITE_DELAY = 50L; //wait one second to write on full socket + + //Opcode tracking + + private static final int OPCODEHASH = 1016; //Opcodes are unique modulo this number as of 11/21/10 + + public AbstractConnection(AbstractConnectionManager connMan, + SocketChannel sockChan, boolean clientMode) { + this.connMan = connMan; + this.sockChan = sockChan; + this.factory = new NetMsgFactory(this); + + } + + public void disconnect() { + try { + this.sockChan.close(); + } catch (IOException e) { + Logger.error(e.toString()); + } + } + + /** + * Serializes AbstractNetMsg msg into a ByteBuffer, then queues that + * ByteBuffer for sending on this AbstractConnection's SocketChannel. + * + * @param msg + * - AbstractNetMsg to be sent. + * @return boolean status if queue is successful or not. On false return, + * the field lastError will be set. + */ + public final boolean sendMsg(AbstractNetMsg msg) { + // Logger.info("Send: " + msg.getSimpleClassName()); + try { + return this._sendMsg(msg); + + } catch (Exception e) { // Catch-all + Logger.error(e); + return false; + } + } + + /** + * Serializes AbstractNetMsg msg into a ByteBuffer, then queues that + * ByteBuffer for sending on this AbstractConnection's SocketChannel. This + * internal function is required to be overridden by subclasses. + * + * @param msg + * - AbstractNetMsg to be sent. + * @return boolean status if queue is successful or not. On false return, + * the field lastError will be set. + */ + protected abstract boolean _sendMsg(AbstractNetMsg msg); + + /** + * Queues ByteBuffer bb for sending on this AbstractConnection's + * SocketChannel. + * + * @param bb + * - ByteBuffer to be sent. + * @return boolean status if queue is successful or not. On false return, + * the field lastError will be set. + */ + public final boolean sendBB(ByteBuffer bb) { + + if (bb == null) + return false; + try { + return this._sendBB(bb); + } catch (Exception e) { // Catch-all + Logger.error(e); + e.printStackTrace(); + return false; + } + } + + /** + * Queues ByteBuffer bb for sending on this AbstractConnection's + * SocketChannel. This internal function is designed to be overrideable by + * subclasses if needed. + * + * @param bb + * - ByteBuffer to be sent. + * @return boolean status if queue is successful or not. On false return, + * the field lastError will be set. + */ + protected boolean _sendBB(ByteBuffer bb) { + this.outbox.add(bb); + this.connMan.sendStart(this.sockChan); + return true; + } + + /** + * Move data off the socketChannel's buffer and into the Connection's + * internal NetMsgFactory. + */ + // FIXME the int return value on this means nothing! Clean it up! + protected int read() { + + if (readLock.tryLock()) + try { + + if (this.sockChan.isOpen() == false) { + Logger.info("Sock channel closed. Disconnecting " + this.getRemoteAddressAndPortAsString()); + this.disconnect(); + return 0; + } + + // get socket buffer sized buffer from multipool + ByteBuffer bb = Network.byteBufferPool.getBuffer(16); + + int totalBytesRead = 0; + int lastRead; + do { + try { + bb.clear(); + lastRead = this.sockChan.read(bb); + + // On EOF on the SocketChannel, disconnect. + if (lastRead <= -1) { + Logger.info(" EOF on Socket Channel " + this.getRemoteAddressAndPortAsString()); + this.disconnect(); + break; + } + + if (lastRead == 0) + continue; + + synchronized (this.factory) { + this.factory.addData(bb); + } + this.checkInternalFactory(); + + totalBytesRead += lastRead; + + } catch (NotYetConnectedException e) { + Logger.error(e.toString()); + break; + + } catch (ClosedChannelException e) { + Logger.error(e.toString()); + this.disconnect(); + break; + + } catch (IOException e) { + if (!e.getMessage().startsWith( + "An existing connection was forcibly closed")) + Logger.error(e.toString()); + this.disconnect(); + break; + + } catch (Exception e) { + Logger.error(e.toString()); + break; + } + } while (lastRead > 0); + + // put buffer back into multipool + Network.byteBufferPool.putBuffer(bb); + + this.checkInternalFactory(); + return totalBytesRead; + + } finally { + readLock.unlock(); + } + else { + Logger.debug("Another thread already has a read lock! Skipping."); + return 0; + } + } + + /** + * Move data off the Connection's buffer and into the SocketChannel's + * Buffer. + */ + protected boolean writeAll() { + + //intentional delay if socket is full. Give the socket a chance to clear out. + if (System.currentTimeMillis() < this.nextWriteTime) + return false; + + if (writeLock.tryLock()) + try { + String s = ""; + int written = 0; + + boolean allSentOK = true, bufferDoubled = false; + + while (this.outbox.peek() != null) { + ByteBuffer bb = this.outbox.peek(); + + int toSend = bb.position(); + + try { + bb.flip(); + written = this.sockChan.write(bb); + + // Logger.debug("Using a BB with cap of: " + bb.capacity() + // + ". toSend: " + toSend + ", written: " + written); + if (written != toSend) + bb.compact(); // Clean up in case not all gets sent + + if (toSend == written) { + // Actually remove it from the queue + this.outbox.poll(); + + // Pool it + Network.byteBufferPool.putBuffer(bb); + continue; + + } else + if (written == 0) { + //Socket full, let's delay the next write on socket. + this.nextWriteTime = System.currentTimeMillis() + AbstractConnection.SOCKET_FULL_WRITE_DELAY; + + int currentBufferSize = this.sockChan.socket() + .getSendBufferSize(); + + if (!bufferDoubled && currentBufferSize <= Network.INITIAL_SOCKET_BUFFER_SIZE) { + this.doubleSocketSendBufferSize(); + bufferDoubled = true; + } else { + + // s = error02; + // s = s.replaceAll("%1%", this + // .getRemoteAddressAndPortAsString() + ":" + + // this.printLastOpcodes()); + // s = s.replaceAll("%2%", currentBufferSize + ""); + // this.warn(s); + + allSentOK = false; + break; + } + } + + } catch (ClosedChannelException e) { + // Catches AsynchronousCloseException, + // and ClosedByInterruptException + Logger.error(e); + break; + + } catch (Exception e) { + // Catches NotYetConnectedException + // and IOException + Logger.error(e); + this.disconnect(); + break; + } + + } + return allSentOK; + } finally { + writeLock.unlock(); + } + else { + Logger.debug("Another thread already has a write lock! Skipping."); + return false; + } + } + + private boolean doubleSocketSendBufferSize() { + String s; + try { + int currentSize = this.sockChan.socket().getSendBufferSize(); + int newSize = currentSize << 1; + + this.sockChan.socket().setSendBufferSize(newSize); + + // s = "(IP=" + this.getRemoteAddressAndPortAsString() + "): "; + // s += this.printLastOpcodes(); + // s += "Socket has reached the maximum outbound buffer length of "; + // s += currentSize + ". Attempting to double the outbound buffer size."; + // + // this.warn(s); + return true; + } catch (SocketException e) { + Logger.error( e.toString()); + return false; + } + } + + public boolean isConnected() { + return this.sockChan.isConnected(); + } + + public boolean isOpen() { + return this.sockChan.isOpen(); + } + + /* + * Getters n Setters + */ + public SocketChannel getSocketChannel() { + return this.sockChan; + } + + public final void checkInternalFactory() { + CheckNetMsgFactoryJob j = new CheckNetMsgFactoryJob(this); + JobManager.getInstance().submitJob(j); + } + + public NetMsgFactory getFactory() { + return factory; + } + + public String getRemoteAddressAndPortAsString() { + String out = ""; + + if (this.sockChan == null) + out += "NotConnected"; + else if (this.sockChan.socket() == null) + out += "NotConnected"; + else if (this.sockChan.socket().getRemoteSocketAddress() == null) + out += "NotConnected"; + else + out += this.sockChan.socket().getRemoteSocketAddress().toString(); + + return out; + } + + public String getLocalAddressAndPortAsString() { + String out = ""; + + if (this.sockChan == null) + out += "NotConnected"; + else if (this.sockChan.socket() == null) + out += "NotConnected"; + else if (this.sockChan.socket().getRemoteSocketAddress() == null) + out += "NotConnected"; + else { + out += this.sockChan.socket().getLocalSocketAddress().toString(); + out += ":"; + out += this.sockChan.socket().getLocalPort(); + } + + return out; + } + + /** + * Gives the Connection a chance to act on a msg prior to sending it to the + * provided NetMsgHandler + * + */ + @Override + public abstract boolean handleClientMsg(ClientNetMsg msg); + + protected long getLastMsgTime() { + return this.lastMsgTime; + } + + protected void setLastMsgTime() { + // TODO Consider making this a static to latest system time + this.lastMsgTime = System.currentTimeMillis(); + } + + protected long getLastKeepAliveTime() { + return this.lastKeepAliveTime; + } + + protected void setLastKeepAliveTime() { + this.lastKeepAliveTime = System.currentTimeMillis(); + } + + public long getLastOpcode() { + return lastOpcode; + } + + public void setLastOpcode(long lastOpcode) { + this.lastOpcode = lastOpcode; + } + +} diff --git a/src/engine/net/AbstractConnectionManager.java b/src/engine/net/AbstractConnectionManager.java new file mode 100644 index 00000000..22f6bd5e --- /dev/null +++ b/src/engine/net/AbstractConnectionManager.java @@ -0,0 +1,708 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net; + +import engine.core.ControlledRunnable; +import engine.job.AbstractJob; +import engine.job.JobManager; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.io.IOException; +import java.net.*; +import java.nio.channels.*; +import java.nio.channels.spi.SelectorProvider; +import java.util.Iterator; +import java.util.concurrent.ConcurrentLinkedQueue; + +public abstract class AbstractConnectionManager extends ControlledRunnable { + + private final Selector selector; + private final ServerSocketChannel listenChannel; + private final ConcurrentLinkedQueue chngReqs = new ConcurrentLinkedQueue<>(); + + /* + * + */ + public AbstractConnectionManager(String nodeName, InetAddress hostAddress, + int port) throws IOException { + super(nodeName); + + this.selector = SelectorProvider.provider().openSelector(); + + // Create a new non-blocking Server channel + this.listenChannel = ServerSocketChannel.open(); + this.listenChannel.configureBlocking(false); + + // Bind + InetSocketAddress isa = new InetSocketAddress(hostAddress, port); + this.listenChannel.socket().bind(isa); + + Logger.info(this.getLocalNodeName() + " Configured to listen: " + + isa.getAddress().toString() + ':' + port); + + // register an interest in Accepting new connections. + SelectionKey sk = this.listenChannel.register(this.selector, SelectionKey.OP_ACCEPT); + sk.attach(this); + } + + /* + * ControlledRunnable implementations + */ + @Override + protected void _startup() { + } + + @Override + protected void _shutdown() { + this.selector.wakeup(); + this.disconnectAll(); + this.selector.wakeup(); + } + + @Override + protected boolean _preRun() { + this.runStatus = true; + return true; + } + + @Override + protected boolean _Run() { + while (this.runCmd) { + try { + this.runLoopHook(); + + this.processChangeRequests(); + this.auditSocketChannelToConnectionMap(); + this.selector.select(250L); + this.processNewEvents(); + + } catch (Exception e) { + Logger.error(e.toString()); + } + } + return true; + } + + @Override + protected boolean _postRun() { + + this.runStatus = false; + + this.disconnectAll(); + + try { + this.selector.close(); + } catch (IOException e) { + Logger.error( e.toString()); + } + + return true; + } + + /** + * Hook for subclasses to use. + * + */ + protected void runLoopHook() { + } + + /* + * Accept / New Connection FNs + */ + private AbstractConnection acceptNewConnection(final SelectionKey key) + throws IOException { + + this.preAcceptNewConnectionHook(key); + + // Cancel incoming connections if server isn't set to listen + if (this.listenChannel == null || this.listenChannel.isOpen() == false) { + key.cancel(); + return null; + } + + // For an accept to be pending, the key contains a reference to the + // ServerSocketChannel + ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); + + // Get SocketChannel + SocketChannel sockChan = ssc.accept(); + sockChan.configureBlocking(false); + + //Configure the Socket + Socket socket = sockChan.socket(); + socket.setSendBufferSize(Network.INITIAL_SOCKET_BUFFER_SIZE); + socket.setReceiveBufferSize(Network.INITIAL_SOCKET_BUFFER_SIZE); + socket.setTcpNoDelay(MBServerStatics.TCP_NO_DELAY_DEFAULT); + + //Register with the selector + SelectionKey sk = sockChan.register(this.selector, SelectionKey.OP_READ); + if (sk == null) { + Logger.error("FIX ME! NULL SELECTION KEY!"); + return null; + } + + //Initialize Connection + AbstractConnection ac = this.getNewIncomingConnection(sockChan); + sk.attach(ac); + + this.postAcceptNewConnectionHook(ac); + return ac; + } + + /** + * Hook for subclasses to use. + * + * @param key + */ + protected void preAcceptNewConnectionHook(SelectionKey key) { + } + + /** + * Hook for subclasses to use. + * + * @param ac + */ + protected void postAcceptNewConnectionHook(AbstractConnection ac) { + } + + protected abstract AbstractConnection getNewIncomingConnection(SocketChannel sockChan); + + protected abstract AbstractConnection getNewOutgoingConnection(SocketChannel sockChan); + + /* + * Disconnect / Destroy Connection FNs + */ + protected boolean disconnect(final SelectionKey key) { + + this.disconnect((AbstractConnection) key.attachment()); + + key.attach(null); + key.cancel(); + return key.isValid(); + } + + protected boolean disconnect(final AbstractConnection c) { + if (c == null) + return false; + + c.disconnect(); + + return c.getSocketChannel().isConnected(); + } + + protected void disconnectAll() { + synchronized (this.selector.keys()) { + for (SelectionKey sk : this.selector.keys()) { + if (sk.channel() instanceof SocketChannel) + disconnect(sk); + } + } + } + + /* + * Data IO + */ + + /* + * WRITE SEQUENCE + */ + /** + * Submits a request to set this Connection to WRITE mode. + * + */ + protected void sendStart(final SocketChannel sockChan) { + synchronized (this.chngReqs) { + // Indicate we want the interest ops set changed + this.chngReqs.add(new ChangeRequest(sockChan, ChangeType.CHANGEOPS, SelectionKey.OP_WRITE)); + } + + this.selector.wakeup(); + } + + /** + * + * @param key + * @return Boolean indication emit success. + */ + protected boolean sendFinish(final SelectionKey key) { + SocketChannel sockChan = (SocketChannel) key.channel(); + + // Check to see if the SocketChannel the selector offered up + // is null. + if (sockChan == null) { + Logger.error(": null sockChannel."); + this.disconnect(key); + return false; + } + + AbstractConnection c = (AbstractConnection) key.attachment(); + + if (c == null) { + Logger.error(": null Connection."); + this.disconnect(key); + return false; + } + + // long startTime = System.currentTimeMillis(); + boolean allSent = c.writeAll(); + + // if ((System.currentTimeMillis() - startTime) > 20) + // this.logDirectWARNING(c.getRemoteAddressAndPortAsString() + " took " + (System.currentTimeMillis() - startTime) + "ms to handle!"); + + // If all was written, switch back to Read Mode. + if (allSent || !c.isConnected()) { + + // Indicate we want the interest ops set changed + ChangeRequest chReq = new ChangeRequest(c.getSocketChannel(), ChangeType.CHANGEOPS, SelectionKey.OP_READ); + synchronized (this.chngReqs) { + this.chngReqs.add(chReq); + } + + this.selector.wakeup(); + } + return true; + } + + /* + * READ SEQUENCE + */ + /** + * + * @param key + * @return Boolean indication of success. + */ + private boolean receive(final SelectionKey key) { + SocketChannel sockChan = (SocketChannel) key.channel(); + + // Check to see if the SocketChannel the selector offered up + // is null. + if (sockChan == null) { + Logger.error("null sockChannel."); + this.disconnect(key); + return false; + } + + AbstractConnection c = (AbstractConnection) key.attachment(); + + if (c == null) { + Logger.error("null Connection."); + this.disconnect(key); + return false; + } + + c.read(); + + return true; + } + + /* + * Main Loop And Loop Controls + */ + private void processChangeRequests() { + SelectionKey selKey = null; + ChangeRequest sccr = null; + ChangeType change = null; + SocketChannel sockChan = null; + + synchronized (this.chngReqs) { + Iterator it = this.chngReqs.iterator(); + while (it.hasNext()) { + sccr = it.next(); + + if (sccr == null) { + it.remove(); + continue; + } + + change = sccr.getChangeType(); + sockChan = sccr.getSocketChannel(); + + switch (change) { + case CHANGEOPS: + selKey = sockChan.keyFor(this.selector); + + if (selKey == null || selKey.isValid() == false) + continue; + + selKey.interestOps(sccr.getOps()); + break; + + case REGISTER: + try { + sockChan.register(this.selector, sccr.getOps()); + + } catch (ClosedChannelException e) { + // TODO Should a closed channel be logged or just + // cleaned up? + // Logger.error(this.getLocalNodeName(), e); + } + break; + } + } + this.chngReqs.clear(); + } + } + + private void processNewEvents() { + SelectionKey thisKey = null; + Iterator selectedKeys = null; + JobManager jm = JobManager.getInstance(); + + selectedKeys = this.selector.selectedKeys().iterator(); + + if (selectedKeys.hasNext() == false) + return; + + while (selectedKeys.hasNext()) { + thisKey = selectedKeys.next(); + + //To shake out any issues + if (thisKey.attachment() == null) + Logger.error("Found null attachment! PANIC!"); + + if (thisKey.attachment() instanceof AbstractConnection) + if (((AbstractConnection) thisKey.attachment()).execTask.get() == true) + continue; + + selectedKeys.remove(); + + try { + if (thisKey.isValid() == false) + break; // Changed from continue + else if (thisKey.isAcceptable()) + this.acceptNewConnection(thisKey); + else if (thisKey.isReadable()) + jm.submitJob(new ReadOperationHander(thisKey)); + else if (thisKey.isWritable()) + jm.submitJob(new WriteOperationHander(thisKey)); + else if (thisKey.isConnectable()) + this.finishConnectingTo(thisKey); + else + Logger.error("Unhandled keystate: " + thisKey.toString()); + } catch (CancelledKeyException cke) { + Logger.error(this.getLocalNodeName(), cke); + this.disconnect(thisKey); + + } catch (IOException e) { + Logger.error(this.getLocalNodeName(), e); + } + } + } + + protected void connectTo(String host, int port) { + try { + this.connectTo(InetAddress.getByName(host), port); + } catch (UnknownHostException e) { + Logger.error(this.getLocalNodeName(), e); + } + } + + protected void connectTo(InetAddress host, int port) { + try { + this.startConnectingTo(host, port); + this.selector.wakeup(); + } catch (IOException e) { + Logger.error(this.getLocalNodeName(), e); + } + } + + protected final void startConnectingTo(InetAddress host, int port) + throws IOException { + // Create a non-blocking socket channel + SocketChannel sockChan = SocketChannel.open(); + sockChan.configureBlocking(false); + sockChan.socket().setSendBufferSize(Network.INITIAL_SOCKET_BUFFER_SIZE); + sockChan.socket().setReceiveBufferSize(Network.INITIAL_SOCKET_BUFFER_SIZE); + + // Make a new Connection object + this.getNewOutgoingConnection(sockChan); + + // Kick off connection establishment + sockChan.connect(new InetSocketAddress(host, port)); + + synchronized (this.chngReqs) { + this.chngReqs.add(new ChangeRequest(sockChan, ChangeType.REGISTER, SelectionKey.OP_CONNECT)); + } + + this.selector.wakeup(); + } + + private void finishConnectingTo(SelectionKey key) throws IOException { + this.preFinishConnectingToHook(key); + + // Get sockChan + SocketChannel sockChan = (SocketChannel) key.channel(); + + // Get AbstractConnection + AbstractConnection ac = (AbstractConnection) key.attachment(); + + if (sockChan == null) { + Logger.error(this.getLocalNodeName(), "null socketChannel"); + this.disconnect(key); + return; + } + + if (ac == null) { + Logger.error(this.getLocalNodeName(), "null AbstractConnection"); + this.disconnect(key); + return; + } + + // Finish the connection. If the connection operation failed + // this will raise an IOException. + try { + sockChan.finishConnect(); + } catch (IOException e) { + if (e.getMessage().startsWith("Connection refused:") + || e.getMessage().startsWith( + "An existing connection was forcibly closed")) { + // eat this type of IOException + } else + Logger.error(this.getLocalNodeName(), e); + + // Cancel the channel's registration with our selector + key.cancel(); + return; + } + + Socket socket = sockChan.socket(); + Logger.debug("Connected to: " + + socket.getInetAddress() + ':' + + socket.getPort()); + + sockChan.configureBlocking(false); + sockChan.register(this.selector, SelectionKey.OP_READ); + + this.postFinishConnectingToHook(ac); + } + + /** + * Hook for subclasses to use. + * + * @param key + */ + protected void preFinishConnectingToHook(SelectionKey key) { + } + + /** + * Hook for subclasses to use. + * + * @param ac + */ + protected void postFinishConnectingToHook(AbstractConnection ac) { + } + + public final String getLocalNodeName() { + return this.getThreadName(); + } + + /** + * Removes the mapping that contains the key 'sockChan' + * + * @param sockChan + */ + private long lastAuditTime = 0; + + protected int auditSocketChannelToConnectionMap() { + long startTime = System.currentTimeMillis(); + int numberOfItemsToProcess = 0; + + if (lastAuditTime + MBServerStatics.TIMEOUT_CHECKS_TIMER_MS > startTime) + return -1; + + synchronized (this.selector.keys()) { + for (SelectionKey sk : this.selector.keys()) { + if (!(sk.channel() instanceof SocketChannel)) + continue; + + SocketChannel sockChan = (SocketChannel) sk.channel(); + AbstractConnection conn = (AbstractConnection) sk.attachment(); + + if (sockChan == null) + continue; + + if (!sockChan.isOpen()) { + numberOfItemsToProcess++; + Logger.info("sockChan closed. Disconnecting.."); + disconnect(sk); + continue; + } + + if (conn == null) { + numberOfItemsToProcess++; + Logger.info("Connection is null, Disconnecting."); + disconnect(sk); + continue; + } + + //removed keep alive timeout. Believe failmu used this for disconnecting players on force quit, but a closed socket will already disconnect. +// if (conn.getLastKeepAliveTime() + MBServerStatics.KEEPALIVE_TIMEOUT_MS < startTime) { +// numberOfItemsToProcess++; +// Logger.info("Keep alive Disconnecting " + conn.getRemoteAddressAndPortAsString()); +// conn.disconnect(); +// continue; +// } + + if (conn.getLastMsgTime() + MBServerStatics.AFK_TIMEOUT_MS < startTime) { + numberOfItemsToProcess++; + Logger.info("AFK TIMEOUT Disconnecting " + conn.getRemoteAddressAndPortAsString()); + conn.disconnect(); + } + } + } + + if (numberOfItemsToProcess != 0) + Logger.info( "Cleaned " + + numberOfItemsToProcess + + " dead connections in " + + (System.currentTimeMillis() - startTime) + + " millis."); + + lastAuditTime = System.currentTimeMillis(); + return numberOfItemsToProcess; + } + + /* + * + */ + protected static enum ChangeType { + + REGISTER, CHANGEOPS + } + + private class ChangeRequest { + + private final SocketChannel sockChan; + private final ChangeType changeType; + private final Integer ops; + + public ChangeRequest(SocketChannel sockChan, ChangeType changeType, + int ops) { + this.sockChan = sockChan; + this.changeType = changeType; + this.ops = ops; + } + + public SocketChannel getSocketChannel() { + synchronized (this.sockChan) { + return this.sockChan; + } + } + + public ChangeType getChangeType() { + synchronized (this.changeType) { + return this.changeType; + } + } + + public int getOps() { + synchronized (this.ops) { + return this.ops; + } + } + } + + public int getConnectionSize() { + if (this.selector == null) + return -1; + if (this.selector.keys() == null) + return -1; + return this.selector.keys().size(); + } + + /** + * Returns the port on which this socket is listening. + * + * @return the port number to which this socket is listening or -1 if the + * socket is not bound yet. + * + */ + public int getListeningPort() { + if (this.listenChannel == null) + return -1; + if (this.listenChannel.socket() == null) + return -1; + return this.listenChannel.socket().getLocalPort(); + } + + /** + * Returns the address of the endpoint this socket is bound to, or null if + * it is not bound yet. + * + * @return a SocketAddress representing the local endpoint of this socket, + * or null if it is not bound yet. + */ + public SocketAddress getListeningAddress() { + if (this.listenChannel == null) + return null; + if (this.listenChannel.socket() == null) + return null; + return this.listenChannel.socket().getLocalSocketAddress(); + } + + private class ReadOperationHander extends AbstractJob { + + private final SelectionKey sk; + private final AbstractConnection ac; + private final boolean runStatus; + + public ReadOperationHander(final SelectionKey sk) { + this.sk = sk; + + if (sk.attachment() instanceof AbstractConnection) { + this.ac = (AbstractConnection) sk.attachment(); + this.runStatus = this.ac.execTask.compareAndSet(false, true); + } else { + this.ac = null; + this.runStatus = false; + Logger.error("Passed selection key did not have a corresponding Connection!(Read)"); + } + } + + @Override + protected void doJob() { + if (runStatus) { + this.ac.connMan.receive(sk); + this.ac.execTask.compareAndSet(true, false); + } + } + } + + private class WriteOperationHander extends AbstractJob { + + private final SelectionKey sk; + private final AbstractConnection ac; + private final boolean runStatus; + + public WriteOperationHander(final SelectionKey sk) { + this.sk = sk; + + if (sk.attachment() instanceof AbstractConnection) { + this.ac = (AbstractConnection) sk.attachment(); + this.runStatus = this.ac.execTask.compareAndSet(false, true); + } else { + this.runStatus = false; + this.ac = null; + Logger.error("Passed selection key did not have a corresponding Connection!(Write)"); + } + + } + + @Override + protected void doJob() { + if (runStatus) { + this.ac.connMan.sendFinish(sk); + this.ac.execTask.compareAndSet(true, false); + } + } + } + +} diff --git a/src/engine/net/AbstractNetMsg.java b/src/engine/net/AbstractNetMsg.java new file mode 100644 index 00000000..1e8e9c1f --- /dev/null +++ b/src/engine/net/AbstractNetMsg.java @@ -0,0 +1,245 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net; + +import engine.exception.SerializationException; +import engine.net.client.Protocol; +import engine.server.MBServerStatics; +import engine.util.StringUtils; +import org.pmw.tinylog.Logger; + +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; +import java.util.concurrent.ConcurrentHashMap; + +/** + * This class represents the NetMsgs set to/from the SBClient and in between + * MBServer Server Suite components. Note that since the NetMsgs sent to/from + * the SBClient do NOT include a MsgLen or DataLen parameter, special + * serialization/deserialization must be implemented. + * + */ +public abstract class AbstractNetMsg { + + protected final Protocol protocolMsg; + private AbstractConnection origin; + + private static ConcurrentHashMap stats = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_HIGH); + + /** + * This is the general purpose constructor. + * + * @param protocolMsg + */ + protected AbstractNetMsg(Protocol protocolMsg) { + super(); + this.protocolMsg = protocolMsg; + } + + protected AbstractNetMsg(Protocol protocolMsg, AbstractConnection origin) { + super(); + this.protocolMsg = protocolMsg; + this.origin = origin; + } + + protected AbstractNetMsg(Protocol protocolMsg, AbstractNetMsg msg) { + super(); + this.protocolMsg = protocolMsg; + this.origin = msg.origin; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + * + * @param reader + */ + protected AbstractNetMsg(Protocol protocolMsg, AbstractConnection origin, + ByteBufferReader reader) + { + this.protocolMsg = protocolMsg; + this.origin = origin; + + // Call the subclass specific deserializer + try { + this._deserialize(reader); + } catch (NullPointerException e) { + Logger.error(e); + } + } + + /** + * Deserializes the subclass specific items from the supplied + * ByteBufferReader + * + * @param reader + */ + protected abstract void _deserialize(ByteBufferReader reader); + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter + * + * @param writer + * @throws Exception + */ + protected abstract void _serialize(ByteBufferWriter writer) + throws SerializationException; + + /** + * Attempts to serialize this NetMsg into a ByteBuffer. ByteBuffer is + * obtained from a pool, so to retain max efficiency, the caller needs to + * return this BB to the pool. Header size and layout is entirely defined by + * the subclass of AbstractNetMsg + * + * @return a ByteBuffer + */ + public final ByteBuffer serialize() { + + NetMsgStat stat; + + if (!AbstractNetMsg.stats.containsKey(this.protocolMsg)) { + stat = new NetMsgStat(this.protocolMsg, this.getPowerOfTwoBufferSize()); + AbstractNetMsg.stats.put(this.protocolMsg, stat); + } else + stat = AbstractNetMsg.stats.get(this.protocolMsg); + int lowerPow = stat.getMax(); + int upperPow = lowerPow + 4; + + ByteBuffer bb = null; + + int startPos = 0; + ByteBufferWriter writer = null; + + for (int i = lowerPow; i < upperPow; ++i) { + + // get an appropriate sized BB from pool + + bb = Network.byteBufferPool.getBuffer(i); + + // Mark start position + + startPos = bb.position(); + + // Make a writer + + writer = new ByteBufferWriter(bb); // FIXME inefficient to + + // Set aside header here. + + AbstractNetMsg.allocHeader(writer, this.getHeaderSize()); + + // Now serialize the object's specifics + + try { + this._serialize(writer); + + //Serialize successful, update NetMsgStat + + stat.updateStat(i); + + } catch (BufferOverflowException boe) { + Logger.error("BufferSize PowerOfTwo: " + i + + " is too small for " + protocolMsg != null? protocolMsg.name() : this.getClass().getName() + ", trying again with " + (i + 1)); + + //Return buffer. + + Network.byteBufferPool.putBuffer(bb); + continue; + + } catch (Exception e) { + + //Return buffer. + Logger.error(e); + e.printStackTrace(); + + Network.byteBufferPool.putBuffer(bb); + return null; + } + + // This shouldn't throw any errors since this part of the BB has + // already been allocated + + this.writeHeaderAt(startPos, writer); + return writer.getBb(); + } + + // If we get here, its not a successful serialization and lastError + // should be set + + return null; + } + + private static void allocHeader(ByteBufferWriter writer, int bytes) { + byte zero = 0; // prevents the int->byte cast + for (int h = 0; h < bytes; ++h) { + writer.put(zero); + } + } + + /** + * Function allows for setting other than default initial Buffer size for + * the Serializer. Override and return the size of the buffer in power of + * two bytes. + * + * Example, if you would like a buffer of 65535, then return 16 from this + * value since 2^16 = 65535 + * + * @return the power to raise two to. + */ + protected int getPowerOfTwoBufferSize() { + return (10); // 2^10 == 1024 + } + + /** + * Forces subclass to define how large (in bytes) the message's header is. + * + * @return the length (in bytes) of this message type's header. + */ + protected abstract int getHeaderSize(); + + /** + * Forces subclasses to implement how to write its own header into a + * byteBuffer + * + * @param startPos + * - starting position for the header write + * @param writer + * - ByteBufferWriter to write the header to. + */ + protected abstract void writeHeaderAt(int startPos, ByteBufferWriter writer); + + /** + * @return The protocolMsg of this Msg. + */ + public Protocol getProtocolMsg() { + return protocolMsg; + } + + /** + * @return The protocolMsg As a string. + */ + public String getOpcodeAsString() { + return StringUtils.toHexString(protocolMsg.opcode); + } + + /** + * @return the origin + */ + public AbstractConnection getOrigin() { + return origin; + } + + public void setOrigin(AbstractConnection conn) { + this.origin = conn; + } + +} diff --git a/src/engine/net/ByteBufferReader.java b/src/engine/net/ByteBufferReader.java new file mode 100644 index 00000000..f5bb71b1 --- /dev/null +++ b/src/engine/net/ByteBufferReader.java @@ -0,0 +1,370 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net; + +import engine.math.Vector3f; +import engine.math.Vector3fImmutable; +import engine.util.ByteBufferUtils; +import engine.util.ByteUtils; + +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.UUID; + +public class ByteBufferReader { + + private final ByteBuffer bb; + private final boolean endianFlip; + + /** + * Helper class for getting structured information out of a ByteBuffer + * easily. ByteBuffer passed in is copied to an internal ByteBuffer. + * bbin must have the position attribute at the end of the + * data to be copied over, since bbin.flip() is called in this + * constructor. + * + * @param bbin + * @param endianFlip + */ + public ByteBufferReader(ByteBuffer bbin, boolean endianFlip) { + super(); + + // Copy supplied BB. + this.bb = ByteBuffer.allocate(bbin.position()); //FIXME Do we want to get this from pool? + bbin.flip(); + this.bb.put(bbin); + + // prepare bb for reading + this.bb.flip(); + + this.endianFlip = endianFlip; + } + + /* + * Getters + */ + + /** + * @return + * @see java.nio.ByteBuffer#get() + */ + public byte get() { + return bb.get(); + } + + public void get(byte[] ba) { + this.bb.get(ba); + } + + /** + * @return + * @see java.nio.ByteBuffer#getChar() + */ + public char getChar() { + char x = bb.getChar(); + if (this.endianFlip) { + x = Character.reverseBytes(x); + } + return x; + } + + /** + * @return + * @see java.nio.ByteBuffer#getDouble() + */ + public double getDouble() { + double x = 0; + if (this.endianFlip) { + x = bb.order(ByteOrder.LITTLE_ENDIAN).getDouble(); + bb.order(ByteOrder.BIG_ENDIAN); + } else { + x = bb.getDouble(); + } + return x; + } + + /** + * @return + * @see java.nio.ByteBuffer#getFloat() + */ + public float getFloat() { + float x = 0; + if (this.endianFlip) { + x = bb.order(ByteOrder.LITTLE_ENDIAN).getFloat(); + bb.order(ByteOrder.BIG_ENDIAN); + } else { + x = bb.getFloat(); + } + return x; + } + + /** + * @return + * @see java.nio.ByteBuffer#getInt() + */ + public int getInt() { + int x = bb.getInt(); + if (this.endianFlip) { + x = Integer.reverseBytes(x); + } + return x; + } + + /** + * @return + * @see java.nio.ByteBuffer#getLong() + */ + public long getLong() { + long x = bb.getLong(); + if (this.endianFlip) { + x = Long.reverseBytes(x); + } + return x; + } + + /** + * @return + * @see java.nio.ByteBuffer#getShort() + */ + public short getShort() { + short x = bb.getShort(); + if (this.endianFlip) { + x = Short.reverseBytes(x); + } + return x; + } + + public final String getString() { + if (this.endianFlip) { + return ByteBufferUtils.getString(this.bb, true, false); + } else { + return ByteBufferUtils.getString(this.bb, false, false); + } + } + + public final String getSmallString() { + if (this.endianFlip) { + return ByteBufferUtils.getString(this.bb, true, true); + } else { + return ByteBufferUtils.getString(this.bb, false, true); + } + } + + public final String getHexString() { + if (this.endianFlip) { + return ByteBufferUtils.getHexString(this.bb, true); + } else { + return ByteBufferUtils.getHexString(this.bb); + } + } + + public final String getUnicodeString() { + if (this.endianFlip) { + return ByteBufferUtils.getUnicodeString(this.bb, true); + } else { + return ByteBufferUtils.getUnicodeString(this.bb); + } + } + + public String get1ByteAsHexString() { + return getBytesAsHexStringCommon(new byte[1]); + } + + public String get2BytesAsHexString() { + return getBytesAsHexStringCommon(new byte[2]); + } + + public String get4BytesAsHexString() { + return getBytesAsHexStringCommon(new byte[4]); + } + + public String get8BytesAsHexString() { + return getBytesAsHexStringCommon(new byte[8]); + } + + private String getBytesAsHexStringCommon(byte[] ba) { + this.bb.get(ba); + if (this.endianFlip) { + return ByteUtils.byteArrayToStringHex(ByteUtils + .switchByteArrayEndianness(ba)); + } else { + return ByteUtils.byteArrayToStringHex(ba); + } + } + + public Vector3f getVector3f() { + Vector3f out = new Vector3f(); + if (this.endianFlip) { + out.x = Float + .intBitsToFloat(Integer.reverseBytes(this.bb.getInt())); + out.y = Float + .intBitsToFloat(Integer.reverseBytes(this.bb.getInt())); + out.z = Float + .intBitsToFloat(Integer.reverseBytes(this.bb.getInt())); + } else { + out.x = this.bb.getFloat(); + out.y = this.bb.getFloat(); + out.z = this.bb.getFloat(); + } + return out; + } + + public Vector3fImmutable getVector3fImmutable() { + Vector3fImmutable out; + if (this.endianFlip) { + out = new Vector3fImmutable(Float.intBitsToFloat(Integer + .reverseBytes(this.bb.getInt())), Float + .intBitsToFloat(Integer.reverseBytes(this.bb.getInt())), + Float + .intBitsToFloat(Integer.reverseBytes(this.bb + .getInt()))); + } else { + out = new Vector3fImmutable(this.bb.getFloat(), this.bb.getFloat(), + this.bb.getFloat()); + } + return out; + } + + public final UUID getUUID() { + final byte[] buffer = new byte[16]; + this.bb.get(buffer); + + long msb = 0; + long lsb = 0; + + for (int i = 0; i < 8; i++) { + msb = (msb << 8) | (buffer[i] & 0xff); + } + + for (int i = 8; i < 16; i++) { + lsb = (lsb << 8) | (buffer[i] & 0xff); + } + + return new UUID(msb, lsb); + } + + + /* + * Monitors + */ + + public byte monitorByte(byte expectedValue, String label) { + return this.monitorByte(expectedValue, label, false); + } + + public byte monitorByte(byte expectedValue, String label, boolean peek) { + // if (x != expectedValue) { +// Logger.info("MonitorTrip: " + label + ". Expected: " +// + expectedValue + " Got: " + x); +// } + return this.get(); + } + + public short monitorShort(short expectedValue, String label) { + return this.monitorShort(expectedValue, label, false); + } + + public short monitorShort(short expectedValue, String label, boolean peek) { + // if (x != expectedValue) { +// Logger.info("MonitorTrip: " + label + ". Expected: " +// + expectedValue + " Got: " + x); +// } + return this.getShort(); + } + + public int monitorInt(int expectedValue, String label) { + return this.monitorInt(expectedValue, label, false); + } + + public int monitorInt(int expectedValue, String label, boolean peek) { + // if (x != expectedValue) { +// Logger.info("MonitorTrip: " + label + ". Expected: " +// + expectedValue + " Got: " + x); +// } + return this.getInt(); + } + + public long monitorLong(long expectedValue, String label) { + return this.monitorLong(expectedValue, label, false); + } + + public long monitorLong(long expectedValue, String label, boolean peek) { + // if (x != expectedValue) { +// Logger.info("MonitorTrip: " + label + ". Expected: " +// + expectedValue + " Got: " + x); +// } + return this.getLong(); + } + + /* + * ByteBuffer delegates + */ + + /** + * @return + * @see java.nio.Buffer#hasRemaining() + */ + public final boolean hasRemaining() { + return bb.hasRemaining(); + } + + /** + * @return + * @see java.nio.Buffer#limit() + */ + public final int limit() { + return bb.limit(); + } + + /** + * @return + * @see java.nio.Buffer#position() + */ + public final int position() { + return bb.position(); + } + public final Buffer position(int newPosition){ + return bb.position(newPosition); + } + /** + * @return + * @see java.nio.Buffer#remaining() + */ + public final int remaining() { + return bb.remaining(); + } + + /* + * Status getters + */ + + protected ByteBuffer getBb() { + return bb; + } + + protected boolean isEndianFlip() { + return endianFlip; + } + + public String getByteArray() { + String ret = ""; + if (this.bb == null) + return ret; + byte[] bbyte = bb.array(); + if (bbyte == null) + return ret; + for (int i=0;i>> 8 * (7 - i)); + } + + for (int i = 8; i < 16; i++) { + buffer[i] = (byte) (lsb >>> 8 * (7 - i)); + } + + this.bb.put(buffer); + } + + /** + * @return + * @see java.nio.Buffer#hasRemaining() + */ + public final boolean hasRemaining() { + return bb.hasRemaining(); + } + + /** + * @return + * @see java.nio.Buffer#limit() + */ + public final int limit() { + return bb.limit(); + } + + /** + * @return + * @see java.nio.Buffer#position() + */ + public final int position() { + return bb.position(); + } + + /** + * @return + * @see java.nio.Buffer#remaining() + */ + public final int remaining() { + return bb.remaining(); + } + + /* + * Getters + */ + + public synchronized ByteBuffer getBb() { + return this.bb; + } +} diff --git a/src/engine/net/CheckNetMsgFactoryJob.java b/src/engine/net/CheckNetMsgFactoryJob.java new file mode 100644 index 00000000..4f67417a --- /dev/null +++ b/src/engine/net/CheckNetMsgFactoryJob.java @@ -0,0 +1,53 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.net; + +import engine.gameManager.ConfigManager; +import engine.job.AbstractJob; +import engine.net.client.msg.ClientNetMsg; +import org.pmw.tinylog.Logger; + +public class CheckNetMsgFactoryJob extends AbstractJob { + + private final AbstractConnection conn; + + public CheckNetMsgFactoryJob(AbstractConnection conn) { + super(); + this.conn = conn; + } + + @Override + protected void doJob() { + NetMsgFactory factory = conn.getFactory(); + + // Make any/all msg possible + factory.parseBuffer(); + + // get and route. + AbstractNetMsg msg = factory.getMsg(); + while (msg != null) { + + // Conditionally check to see if origin is set. + if (msg.getOrigin() == null) { + Logger.warn(msg.getClass().getSimpleName() + " had a NULL for its 'origin'."); + msg.setOrigin(this.conn); + } + + if (msg instanceof engine.net.client.msg.ClientNetMsg) { + ConfigManager.handler.handleClientMsg((ClientNetMsg) msg); + + } else { + Logger.error("Unrouteable message of type '" + msg.getClass().getSimpleName() + '\''); + } + + msg = factory.getMsg(); + } + } + +} diff --git a/src/engine/net/ConnectionMonitorJob.java b/src/engine/net/ConnectionMonitorJob.java new file mode 100644 index 00000000..06668bc6 --- /dev/null +++ b/src/engine/net/ConnectionMonitorJob.java @@ -0,0 +1,44 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net; + +import engine.job.AbstractJob; +import engine.job.JobScheduler; +import engine.server.MBServerStatics; + +public class ConnectionMonitorJob extends AbstractJob { + + private final AbstractConnectionManager connMan; + private byte cnt; + + public ConnectionMonitorJob(AbstractConnectionManager connMan, byte cnt) { + super(); + this.connMan = connMan; + this.cnt = cnt; + this.cnt++; + } + + @Override + protected void doJob() { + + if (this.cnt >= 5) { + this.connMan.auditSocketChannelToConnectionMap(); + this.cnt = 0; + } else + this.connMan.auditSocketChannelToConnectionMap(); + + // Self Sustain + ConnectionMonitorJob cmj = new ConnectionMonitorJob(this.connMan, cnt); + JobScheduler.getInstance().scheduleJob(cmj, MBServerStatics.TIMEOUT_CHECKS_TIMER_MS); + + this.setCompletionStatus(JobCompletionStatus.SUCCESS); + } + +} diff --git a/src/engine/net/Dispatch.java b/src/engine/net/Dispatch.java new file mode 100644 index 00000000..d095f08b --- /dev/null +++ b/src/engine/net/Dispatch.java @@ -0,0 +1,74 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.net; + +import engine.objects.PlayerCharacter; + +import java.util.concurrent.ConcurrentLinkedQueue; + +import static engine.net.MessageDispatcher.itemPoolSize; + +/** + * Data class holds a message and a distribution list + */ + +public class Dispatch { + + private static final ConcurrentLinkedQueue dispatchPool = new ConcurrentLinkedQueue<>(); + + public PlayerCharacter player; + public AbstractNetMsg msg; + + public Dispatch(PlayerCharacter player, AbstractNetMsg msg) { + this.player = player; + this.msg = msg; +} + + public void reset() { + this.player = null; + this.msg = null; + } + + public static Dispatch borrow(PlayerCharacter player, AbstractNetMsg msg) { + + Dispatch dispatch; + + dispatch = dispatchPool.poll(); + + if (dispatch == null) { + dispatch = new Dispatch(player, msg); + } else { + dispatch.player = player; + dispatch.msg = msg; + itemPoolSize.decrement(); + } + + return dispatch; + } + + public void release() { + this.reset(); + dispatchPool.add(this); + itemPoolSize.increment(); + } +} diff --git a/src/engine/net/DispatchMessage.java b/src/engine/net/DispatchMessage.java new file mode 100644 index 00000000..626aac47 --- /dev/null +++ b/src/engine/net/DispatchMessage.java @@ -0,0 +1,312 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net; + +import engine.Enum.DispatchChannel; +import engine.Enum.GameObjectType; +import engine.InterestManagement.WorldGrid; +import engine.gameManager.SessionManager; +import engine.math.Vector3fImmutable; +import engine.net.client.ClientConnection; +import engine.objects.AbstractWorldObject; +import engine.objects.Item; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.util.HashSet; + +import static engine.net.MessageDispatcher.dispatchCount; +import static engine.net.MessageDispatcher.maxRecipients; + +/* + * Dispatch Message is the main interface to Magicbane's threaded + * asynch message delivery system. + */ + +public class DispatchMessage { + + public static void startMessagePump() { + + Thread messageDispatcher; + messageDispatcher = new Thread(new MessageDispatcher()); + + messageDispatcher.setName("MessageDispatcher"); + messageDispatcher.start(); + } + + + public static void sendToAllInRange(AbstractWorldObject obj, + AbstractNetMsg msg){ + + if (obj == null) + return; + + if (obj.getObjectType() == GameObjectType.PlayerCharacter || obj.getObjectType() == GameObjectType.Mob || obj.getObjectType() == GameObjectType.NPC || obj.getObjectType() == GameObjectType.Corpse) + dispatchMsgToInterestArea(obj, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + else + dispatchMsgToInterestArea(obj ,msg, DispatchChannel.PRIMARY, MBServerStatics.STRUCTURE_LOAD_RANGE, false, false); + + } + + // Dispatches a message to a playercharacter's interest area + // Method includes handling of exclusion rules for visibility, self, etc. + + public static void dispatchMsgToInterestArea(AbstractWorldObject sourceObject, AbstractNetMsg msg, DispatchChannel dispatchChannel, int interestRange, boolean sendToSelf, boolean useIgnore) { + + Dispatch messageDispatch; + HashSet gridList; + PlayerCharacter gridPlayer; + AbstractWorldObject dispatchSource; + PlayerCharacter sourcePlayer = null; + long recipientCount = 0; + + if (sourceObject == null) + return; + + // If the source of the message is a structure, item or player + // setup our method variables accordingly. + + switch (sourceObject.getObjectType()) { + case Item: + dispatchSource = (AbstractWorldObject) ((Item) sourceObject).getOwner(); + break; + case PlayerCharacter: + dispatchSource = sourceObject; + sourcePlayer = (PlayerCharacter)sourceObject; + if (sourcePlayer.getClientConnection() != null && sendToSelf){ + Dispatch dispatch = Dispatch.borrow(sourcePlayer, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + } + + + break; + default: + dispatchSource = sourceObject; + } + + gridList = WorldGrid.getObjectsInRangePartial(dispatchSource.getLoc(), interestRange, MBServerStatics.MASK_PLAYER); + + for (AbstractWorldObject gridObject : gridList) { + + gridPlayer = (PlayerCharacter)gridObject; + + // Apply filter options if source of dispatch is a player + + if ((dispatchSource.getObjectType() == GameObjectType.PlayerCharacter) && + (sourcePlayer != null)) { + + if (gridPlayer.getObjectUUID() == sourcePlayer.getObjectUUID()) + continue; + + if ((useIgnore == true) && (gridPlayer.isIgnoringPlayer(sourcePlayer) == true)) + continue; + + if(gridPlayer.canSee(sourcePlayer) == false) + continue; + } + + messageDispatch = Dispatch.borrow(gridPlayer, msg); + MessageDispatcher.send(messageDispatch, dispatchChannel); + recipientCount++; + } + + // Update metrics + + if (recipientCount > maxRecipients[dispatchChannel.getChannelID()]) + maxRecipients[dispatchChannel.getChannelID()] = recipientCount; + + dispatchCount[dispatchChannel.getChannelID()].increment(); + } + + public static void dispatchMsgToInterestArea(Vector3fImmutable targetLoc,AbstractWorldObject sourceObject, AbstractNetMsg msg, DispatchChannel dispatchChannel, int interestRange, boolean sendToSelf, boolean useIgnore) { + + Dispatch messageDispatch; + HashSet gridList; + PlayerCharacter gridPlayer; + AbstractWorldObject dispatchSource; + PlayerCharacter sourcePlayer = null; + long recipientCount = 0; + + if (sourceObject == null) + return; + + // If the source of the message is a structure, item or player + // setup our method variables accordingly. + + switch (sourceObject.getObjectType()) { + case Item: + dispatchSource = (AbstractWorldObject) ((Item) sourceObject).getOwner(); + break; + case PlayerCharacter: + dispatchSource = sourceObject; + sourcePlayer = (PlayerCharacter)sourceObject; + + if (sourcePlayer.getClientConnection() != null && sendToSelf){ + Dispatch dispatch = Dispatch.borrow(sourcePlayer, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + } + + + break; + default: + dispatchSource = sourceObject; + } + + gridList = WorldGrid.getObjectsInRangePartial(targetLoc, interestRange, MBServerStatics.MASK_PLAYER); + + for (AbstractWorldObject gridObject : gridList) { + + gridPlayer = (PlayerCharacter)gridObject; + + // Apply filter options if source of dispatch is a player + + if ((dispatchSource.getObjectType() == GameObjectType.PlayerCharacter) && + (sourcePlayer != null)) { + + if (gridPlayer.getObjectUUID() == sourcePlayer.getObjectUUID()) + continue; + + if ((useIgnore == true) && (gridPlayer.isIgnoringPlayer(sourcePlayer) == true)) + continue; + + if(gridPlayer.canSee(sourcePlayer) == false) + continue; + } + + messageDispatch = Dispatch.borrow(gridPlayer, msg); + MessageDispatcher.send(messageDispatch, dispatchChannel); + recipientCount++; + } + + // Update metrics + + if (recipientCount > maxRecipients[dispatchChannel.getChannelID()]) + maxRecipients[dispatchChannel.getChannelID()] = recipientCount; + + dispatchCount[dispatchChannel.getChannelID()].increment(); + } + + // Sends a message to all players in the game + + public static void dispatchMsgToAll(AbstractNetMsg msg) { + + Dispatch messageDispatch; + long recipientCount = 0; + + // Send message to nobody? No thanks! + + if (SessionManager.getAllActivePlayerCharacters().isEmpty()) + return; + + // Messages to all we will default to the secondary dispatch + // delivery channel. They are generally large, or inconsequential. + + for (PlayerCharacter player : SessionManager.getAllActivePlayerCharacters()) { + messageDispatch = Dispatch.borrow(player, msg); + MessageDispatcher.send(messageDispatch, DispatchChannel.SECONDARY); + recipientCount++; + } + + // Update metrics + + if (recipientCount > maxRecipients[DispatchChannel.SECONDARY.getChannelID()]) + maxRecipients[DispatchChannel.SECONDARY.getChannelID()] = recipientCount; + + dispatchCount[DispatchChannel.SECONDARY.getChannelID()].increment(); + + } + + public static void dispatchMsgToAll(PlayerCharacter source, AbstractNetMsg msg, boolean ignore) { + + Dispatch messageDispatch; + long recipientCount = 0; + + // Send message to nobody? No thanks! + + if (SessionManager.getAllActivePlayerCharacters().isEmpty()) + return; + + // Messages to all we will default to the secondary dispatch + // delivery channel. They are generally large, or inconsequential. + + for (PlayerCharacter player : SessionManager.getAllActivePlayerCharacters()) { + + if (ignore && player.isIgnoringPlayer(source)) + continue; + + messageDispatch = Dispatch.borrow(player, msg); + MessageDispatcher.send(messageDispatch, DispatchChannel.SECONDARY); + recipientCount++; + } + + // Update metrics + + if (recipientCount > maxRecipients[DispatchChannel.SECONDARY.getChannelID()]) + maxRecipients[DispatchChannel.SECONDARY.getChannelID()] = recipientCount; + + dispatchCount[DispatchChannel.SECONDARY.getChannelID()].increment(); + + } + + // Sends a message to an arbitrary distribution list + + public static void dispatchMsgDispatch(Dispatch messageDispatch, DispatchChannel dispatchChannel) { + + if (messageDispatch == null){ + Logger.info("DISPATCH Null for DispatchMessage!"); + return; + } + + // No need to serialize an empty list + + if (messageDispatch.player == null){ + Logger.info("Player Null for Dispatch!"); + messageDispatch.release(); + return; + } + + + MessageDispatcher.send(messageDispatch, dispatchChannel); + + dispatchCount[dispatchChannel.getChannelID()].increment(); + + } + + protected static void serializeDispatch(Dispatch messageDispatch) { + ClientConnection connection; + + if (messageDispatch.player == null){ + Logger.info("Player null in serializeDispatch"); + messageDispatch.release(); + return; + } + + connection = messageDispatch.player.getClientConnection(); + + if ((connection == null) || (connection.isConnected() == false)) { + messageDispatch.release(); + return; + } + + if (messageDispatch.msg == null) { + Logger.error("null message sent to " + messageDispatch.player.getName()); + messageDispatch.release(); + return; + } + + if (!connection.sendMsg(messageDispatch.msg)) + Logger.error(messageDispatch.msg.getProtocolMsg() + " failed sending to " + messageDispatch.player.getName()); + + messageDispatch.release(); + } + + +} diff --git a/src/engine/net/ItemProductionManager.java b/src/engine/net/ItemProductionManager.java new file mode 100644 index 00000000..adf379d2 --- /dev/null +++ b/src/engine/net/ItemProductionManager.java @@ -0,0 +1,155 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net; + +import engine.Enum.DispatchChannel; +import engine.objects.ProducedItem; +import org.pmw.tinylog.Logger; + +import java.util.HashSet; +import java.util.concurrent.DelayQueue; +import java.util.concurrent.atomic.LongAdder; + +/** + * Thread blocks until MagicBane dispatch messages are + * enqueued then processes them in FIFO order. The collection + * is thread safe. + * + * Any large messages not time sensitive such as load object + * sent to more than a single individual should be spawned + * individually on a DispatchMessageThread. + */ + + + +public enum ItemProductionManager implements Runnable { + + ITEMPRODUCTIONMANAGER; + + + // Instance variables + + private ItemQueue itemQueue; + + private long nextFailedItemAudit; + + // Class variables + + @SuppressWarnings("unchecked") // Cannot have arrays of generics in java. + private static final DelayQueue producedQueue = new DelayQueue<>(); + + + // Performance metrics + + public static volatile long[] messageCount = new long[DispatchChannel.values().length]; + public static LongAdder[] dispatchCount = new LongAdder[DispatchChannel.values().length]; + public static volatile long[] maxRecipients = new long[DispatchChannel.values().length]; + public static LongAdder dispatchPoolSize = new LongAdder(); + + public static HashSet FailedItems = new HashSet<>(); + + public Thread itemProductionThread = null; + + // Thread constructor + + + public void startMessagePump() { + + itemProductionThread = new Thread(this); + itemProductionThread.setName("ItemProductionManager"); + + } + + public void initialize(){ + itemProductionThread.start(); + } + + + public static void send(ItemQueue item) { + + // Don't queue up empty dispatches! + + if (item == null) + return; + + producedQueue.add(item); + + } + + @Override + public void run() { + + + while (true) { + try { + + this.itemQueue = producedQueue.take(); + + if (this.itemQueue == null){ + return; + } + + if (this.itemQueue != null) { + if (this.itemQueue.item == null){ + this.itemQueue.release(); + return; + } + + + boolean created = this.itemQueue.item.finishProduction(); + + if (!created) + FailedItems.add(this.itemQueue.item); + + this.itemQueue.release(); + + + } + + + } catch (Exception e) { + Logger.error(e); + } + + } + } + + public static String getNetstatString() { + + String outString = null; + String newLine = System.getProperty("line.separator"); + outString = "[LUA_NETSTA()]" + newLine; + outString += "poolSize: " + dispatchPoolSize.longValue() + '\n'; + + for (DispatchChannel dispatchChannel : DispatchChannel.values()) { + + outString += "Channel: " + dispatchChannel.name() + '\n'; + outString += "Dispatches: " + dispatchCount[dispatchChannel.getChannelID()].longValue()+ '\n'; + outString += "Messages: " + messageCount[dispatchChannel.getChannelID()] + '\n'; + outString += "maxRecipients: " + maxRecipients[dispatchChannel.getChannelID()] + '\n'; + } + return outString; + } + + // For Debugging: + //Logger.error("MessageDispatcher", messageDispatch.msg.getOpcodeAsString() + " sent to " + messageDispatch.playerList.size() + " players"); + } diff --git a/src/engine/net/ItemQueue.java b/src/engine/net/ItemQueue.java new file mode 100644 index 00000000..ee0b160f --- /dev/null +++ b/src/engine/net/ItemQueue.java @@ -0,0 +1,99 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.net; + +import engine.objects.ProducedItem; + +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Delayed; +import java.util.concurrent.TimeUnit; + +import static engine.net.MessageDispatcher.itemPoolSize; + +/** + * Data class holds a message and a distribution list + */ + +public class ItemQueue implements Delayed { + + private static final ConcurrentLinkedQueue itemPool = new ConcurrentLinkedQueue<>(); + + public ProducedItem item; + public long delayTime; + + + public ItemQueue(ProducedItem item, long delayTime) { + this.item = item; + this.delayTime = System.currentTimeMillis() + delayTime; + +} + + public void reset() { + this.item = null; + this.delayTime = 0; + } + + public static ItemQueue borrow(ProducedItem item, long delayTime) { + + ItemQueue itemQueue; + + itemQueue = itemPool.poll(); + + if (itemQueue == null) { + itemQueue = new ItemQueue(item, delayTime); + } else { + itemQueue.item = item; + itemQueue.delayTime = System.currentTimeMillis() + delayTime; + itemPoolSize.decrement(); + } + + return itemQueue; + } + + public void release() { + this.reset(); + itemPool.add(this); + itemPoolSize.increment(); + } + + @Override + public int compareTo(Delayed another) { + ItemQueue anotherTask = (ItemQueue) another; + + if (this.delayTime < anotherTask.delayTime) { + return -1; + } + + if (this.delayTime > anotherTask.delayTime) { + return 1; + } + + return 0; + } + + @Override + public long getDelay(TimeUnit unit) { + long difference = delayTime - System.currentTimeMillis(); + return unit.convert(difference, TimeUnit.MILLISECONDS); + } +} diff --git a/src/engine/net/MessageDispatcher.java b/src/engine/net/MessageDispatcher.java new file mode 100644 index 00000000..123a3730 --- /dev/null +++ b/src/engine/net/MessageDispatcher.java @@ -0,0 +1,145 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net; + +import engine.Enum.DispatchChannel; +import org.pmw.tinylog.Logger; + +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.LongAdder; +import java.util.regex.Pattern; + +/** + * Thread blocks until MagicBane dispatch messages are + * enqueued then processes them in FIFO order. The collection + * is thread safe. + * + * Any large messages not time sensitive such as load object + * sent to more than a single individual should be spawned + * individually on a DispatchMessageThread. + */ + +public class MessageDispatcher implements Runnable { + + // Instance variables + + private Dispatch messageDispatch; + private final Pattern filterPattern; // Unused, but just in case + + // Class variables + + @SuppressWarnings("unchecked") // Cannot have arrays of generics in java. + private static final ConcurrentLinkedQueue[] _messageQueue = new ConcurrentLinkedQueue[DispatchChannel.values().length]; + + private static final LinkedBlockingQueue _blockingQueue = new LinkedBlockingQueue<>(); + + // Performance metrics + + public static volatile long[] messageCount = new long[DispatchChannel.values().length]; + public static LongAdder[] dispatchCount = new LongAdder[DispatchChannel.values().length]; + public static volatile long[] maxRecipients = new long[DispatchChannel.values().length]; + public static LongAdder itemPoolSize = new LongAdder(); + + // Thread constructor + + public MessageDispatcher() { + + // Create new FIFO queues for this network thread + + for (DispatchChannel dispatchChannel : DispatchChannel.values()) { + _messageQueue[dispatchChannel.getChannelID()] = new ConcurrentLinkedQueue<>(); + dispatchCount[dispatchChannel.getChannelID()] = new LongAdder(); + } + + filterPattern = Pattern.compile("[^\\p{ASCII}]"); + Logger.info( " Dispatcher thread has started!"); + + } + + public static void send(Dispatch messageDispatch, DispatchChannel dispatchChannel) { + + // Don't queue up empty dispatches! + + if (messageDispatch.player == null) + return; + + _messageQueue[dispatchChannel.getChannelID()].add(messageDispatch); + _blockingQueue.add(true); + + // Update performance metrics + + messageCount[dispatchChannel.getChannelID()]++; + + } + + @Override + public void run() { + + boolean shouldBlock; + + while (true) { + try { + + shouldBlock = true; + + for (DispatchChannel dispatchChannel : DispatchChannel.values()) { + + this.messageDispatch = _messageQueue[dispatchChannel.getChannelID()].poll(); + + if (this.messageDispatch != null) { + DispatchMessage.serializeDispatch(this.messageDispatch); + shouldBlock = false; + } + + } + + if (shouldBlock == true) + shouldBlock = _blockingQueue.take(); + + } catch (Exception e) { + Logger.error(e); + } + + } + } + + public static String getNetstatString() { + + String outString = null; + String newLine = System.getProperty("line.separator"); + outString = "[LUA_NETSTA()]" + newLine; + outString += "poolSize: " + itemPoolSize.longValue() + '\n'; + + for (DispatchChannel dispatchChannel : DispatchChannel.values()) { + + outString += "Channel: " + dispatchChannel.name() + '\n'; + outString += "Dispatches: " + dispatchCount[dispatchChannel.getChannelID()].longValue()+ '\n'; + outString += "Messages: " + messageCount[dispatchChannel.getChannelID()] + '\n'; + outString += "maxRecipients: " + maxRecipients[dispatchChannel.getChannelID()] + '\n'; + } + return outString; + } + + // For Debugging: + //Logger.error("MessageDispatcher", messageDispatch.msg.getOpcodeAsString() + " sent to " + messageDispatch.playerList.size() + " players"); + } diff --git a/src/engine/net/NetMsgFactory.java b/src/engine/net/NetMsgFactory.java new file mode 100644 index 00000000..7d205f3a --- /dev/null +++ b/src/engine/net/NetMsgFactory.java @@ -0,0 +1,425 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net; + +import engine.exception.FactoryBuildException; +import engine.gameManager.ChatManager; +import engine.net.client.ClientConnection; +import engine.net.client.Protocol; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; +import org.joda.time.DateTime; +import org.pmw.tinylog.Logger; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; + +public class NetMsgFactory { + + // NetMsg Opcode to Constructor List + private static final HashMap netMsgDefinitions = new HashMap<>(); + + // Standardize the error strings + private static String ALL_GOOD_JUST_NOT_ENOUGH_BYTES = "Not enough Bytes"; + private static String DESERIALIZATION_FAILURE = "Deserialization Failure"; + private static String UNIMPLEMENTED_OPCODE = "Unimplemented Opcode"; + private static String UNKNOWN_OPCODE = "Unknown Opcode"; + + protected ByteBuffer internalBuffer; + private final ArrayList msgOutbox; + + private boolean enableFloodControl; + private boolean bypassFloodControl; // temp bypass + private boolean floodControlTripped; + + private static final int FLOOD_CONTROL_TRIP_SETPOINT = 1000; + private int badOpcodeCount; + private final AbstractConnection owner; + private int lastMsgPosition = 0; + + public NetMsgFactory(AbstractConnection origin, boolean enableFloodControl) { + this.internalBuffer = Network.byteBufferPool.getBuffer(18); //256k buffer + + this.bypassFloodControl = false; + this.msgOutbox = new ArrayList<>(); + this.enableFloodControl = enableFloodControl; + this.floodControlTripped = false; + this.owner = origin; + } + + public NetMsgFactory(AbstractConnection origin) { + this(origin, true); + } + + public final void addData(byte[] ba) { + // Dont use prefab BB's here, since sizeof(ba) is unknown. + ByteBuffer bb = ByteBuffer.wrap(ba); + bb.position(bb.capacity()); + this.addData(bb); + } + + public final void addData(ByteBuffer newData) { + synchronized (this.internalBuffer) { + + int newCapacity = this.internalBuffer.position() + newData.position(); + + if (newCapacity >= this.internalBuffer.capacity()) { + //Resize!!!! + Logger.warn( + "Bytebuffer is being be Resized."); + + //Get a newer, bigger BB + ByteBuffer newBB = Network.byteBufferPool.getBufferToFit((int) (newCapacity * 1.5)); + + //Copy old data in + this.internalBuffer.flip(); + newBB.put(this.internalBuffer); + + //Get a handle on old BB + ByteBuffer oldBB = this.internalBuffer; + + //install new BB + this.internalBuffer = newBB; + + //Return old BB + Network.byteBufferPool.putBuffer(oldBB); + } + + synchronized (newData) { + // Copy over the data. + newData.flip(); + + try { + this.internalBuffer.put(newData); + } catch (Exception e) { + Logger.error( e.toString()); + // TODO figure out how to handle this error. + } + } + } + } + + public void parseBuffer() { + // Check flood control first + if (this.floodControlTripped) + // this.conn.disconnect(); + return; + + // MBServer.jobMan.submitJob(new ParseBufferJob(this)); + this._parseBuffer(); + } + + /** + * This function makes a copy of the current internal byte buffer and loads + * the copy into a ByteBufferReader. It is copied so that the Factory can + * continue to accumulate data on the internal buffer from the + * socketChannels. The ByteBufferReader is then used in an attempt to build + * an AbstractNetMsg subclass based on protocolMsg. If a message is successfully + * built, the bytes used are removed from the Factory's internal byte + * buffer. + * + * @return + * @throws Exception + */ + protected void _parseBuffer() { + synchronized (this.internalBuffer) { + while (this.internalBuffer.position() > 0) { + // Check flood control first + if (this.floodControlTripped) + break; + + ByteBufferReader reader = null; + + // Check to see if the minimum amount of data is here: + if (this.internalBuffer.position() < 4) + // nothing wrong, just not enough info yet. + break; + + // copy internal buffer into a reader + reader = new ByteBufferReader(this.internalBuffer, false); + + // Reset the limit to the capacity + this.internalBuffer.limit(this.internalBuffer.capacity()); + + try { + AbstractNetMsg msg = this.tryBuild(owner, reader); + + // error, null messages are being returned on unhandled + // opcodes + // for some reason + if (msg == null) + throw new FactoryBuildException(UNIMPLEMENTED_OPCODE); + + + + if (owner.getClass().equals(ClientConnection.class)){ + ClientConnection client = (ClientConnection)owner; + client.setLastOpcode(msg.getProtocolMsg().opcode); + } + + + + + // Logger.debug("Adding a " + msg.getSimpleClassName() + // + " to the outbox."); + this.addMsgToOutBox(msg); + + this.dropLeadingBytesFromBuffer(reader.position()); + this.bypassFloodControl = false; + + } catch (FactoryBuildException e) { + String error = e.getMessage(); + int readerPos = reader.position(); + + if (error.equals(ALL_GOOD_JUST_NOT_ENOUGH_BYTES)){ + break; + } + // no worries, just break. + + else if (error.equals(DESERIALIZATION_FAILURE)) { + // Lop readerPos bytes off the buffer. + this.dropLeadingBytesFromBuffer(readerPos); + + // Lets bypass flood control for now. + this.bypassFloodControl = true; + continue; + + } else if (error.equals(UNIMPLEMENTED_OPCODE)) { + + if (owner.lastProtocol != null && owner.lastProtocol.constructor == null){ + this.dropLeadingBytesFromBuffer(readerPos); + this.bypassFloodControl = true; + continue; + } + + // Lop readerPos bytes off the buffer. + if (reader.position() >= 4) + reader.position(reader.position() - 4); + int newPosition = Protocol.FindNextValidOpcode(reader); + this.dropLeadingBytesFromBuffer(newPosition); + // Lets bypass flood control for now. + this.bypassFloodControl = true; + + continue; + + } else if (error.equals(UNKNOWN_OPCODE)) { + + if (owner.lastProtocol != null && owner.lastProtocol.constructor == null){ + this.dropLeadingBytesFromBuffer(readerPos); + this.bypassFloodControl = true; + continue; + } + // We don't know what this is or how long, so dump the + // first + // byte and try again + if (reader.position() >= 4) + reader.position(reader.position() - 4); + int newPosition = Protocol.FindNextValidOpcode(reader); + this.dropLeadingBytesFromBuffer(newPosition); + // Lets bypass flood control for now. + this.bypassFloodControl = true; + + continue; + } + } catch (Exception e) { + // TODO FIX THIS!!!! +// Logger.error( e); + + }// end catch + + } // end while loop + } + }// end fn + + public AbstractNetMsg tryBuild(AbstractConnection origin, + ByteBufferReader reader) throws FactoryBuildException { + try { + + // Get the protocolMsg + int opcode = reader.getInt(); + // String ocHex = StringUtils.toHexString(protocolMsg); + + if (MBServerStatics.PRINT_INCOMING_OPCODES) + try { + Logger.info( "Incoming protocolMsg: " + + Protocol.getByOpcode(opcode).name() + " " + Integer.toHexString(opcode) + ", size: " + reader.getBb().limit() + "; " + getByteArray(reader)); + } catch (Exception e) { + Logger.error( e); + } + + return NetMsgFactory.getNewInstanceOf(opcode, origin, reader); + + } catch (BufferUnderflowException e) { + // This is okay. it indicates that we recognized the protocolMsg, but + // there isn't enough information in + // the reader to complete the NetMsg deserialization + throw new FactoryBuildException(ALL_GOOD_JUST_NOT_ENOUGH_BYTES); + + } + } + + public static String getByteArray(ByteBufferReader reader) { + String ret = ""; + if (reader == null) + return ret; + + ByteBuffer bb = reader.getBb(); + if (bb == null) + return ret; + + int length = bb.limit(); // - bb.position(); + ByteBuffer temp = bb.duplicate(); + temp.position(bb.limit()); + temp.flip(); + for (int i = 0; i < length; i++) { + ret += Integer.toString((temp.get() & 0xff) + 0x100, 16).substring(1).toUpperCase(); + } + return ret; + } + + private void incrBadOpcodeCount() { + // keeping this a nested if for Troubleshooting/clarity + if (this.enableFloodControl == true) + if (this.bypassFloodControl == false) { + ++this.badOpcodeCount; + + + + if (this.badOpcodeCount >= FLOOD_CONTROL_TRIP_SETPOINT){ + if (this.owner != null){ + if (this.owner instanceof ClientConnection){ + ClientConnection client = (ClientConnection) this.owner; + if (client.getPlayerCharacter() != null){ + ChatManager.chatSystemError(client.getPlayerCharacter(),"TRIPPED Flood Control! PLEASE RELOG!"); + Logger.info( client.getPlayerCharacter().getName() + " Tripped Flood Control!" + this.badOpcodeCount); + } + + } + } + this.floodControlTripped = true; + }else{ + if (this.owner != null){ + if (this.owner instanceof ClientConnection){ + ClientConnection client = (ClientConnection) this.owner; + if (client.getPlayerCharacter() != null){ + ChatManager.chatSystemError(client.getPlayerCharacter(),"Client sending bad messages. bad message Count " + this.badOpcodeCount); + Logger.info( client.getPlayerCharacter().getName() + " has been caught sending bad opcodes. Bad Opcode Count " + this.badOpcodeCount); + } + + } + + + } + } + + } + } + + protected final void dropLeadingBytesFromBuffer(int numberOfBytes) { + this.internalBuffer.limit(this.internalBuffer.position()); + this.internalBuffer.position(numberOfBytes); + this.internalBuffer.compact(); // Compact + } + + protected boolean addMsgToOutBox(AbstractNetMsg msg) { + synchronized (this.msgOutbox) { + return msgOutbox.add(msg); + } + } + + public AbstractNetMsg getMsg() { + synchronized (this.msgOutbox) { + if (this.msgOutbox.isEmpty()) + return null; + return msgOutbox.remove(0); + } + } + + public boolean hasMsg() { + synchronized (this.msgOutbox) { + return !msgOutbox.isEmpty(); + } + } + + public boolean hasData() { + synchronized (this.internalBuffer) { + return (this.internalBuffer.position() != 0); + } + } + + @SuppressWarnings("unchecked") + private static AbstractNetMsg getNewInstanceOf(int opcode, + AbstractConnection origin, ByteBufferReader reader) { + try { + + Protocol protocolMsg = Protocol.getByOpcode(opcode); + + if (protocolMsg == Protocol.NONE){ + + String errorString = DateTime.now().toString() + origin.lastProtocol.name(); + + int errorCode = errorString.hashCode(); + + + if (origin instanceof ClientConnection){ + PlayerCharacter player = ((ClientConnection)origin).getPlayerCharacter(); + if (player != null){ +// if (MBServerStatics.worldServerName.equals("Grief")) + Logger.error("Invalid protocol msg for player " + player.getFirstName() + " : " + opcode + " lastopcode: " + origin.lastProtocol.name() + " Error Code : " + errorCode); + }else + Logger.error("Invalid protocol msg : " + opcode + " lastopcode: " + origin.lastProtocol.name() + " Error Code : " + errorCode); + + } + + return null; + } + origin.lastProtocol = protocolMsg; + + if (protocolMsg.constructor == null){ + return null; + } + + + + Constructor constructor = protocolMsg.constructor; + + if (constructor == null) + return null; + + Object[] myArgs = new Object[2]; + myArgs[0] = origin; + myArgs[1] = reader; + + Object object = constructor.newInstance(myArgs); + + if (object instanceof engine.net.AbstractNetMsg) + return (AbstractNetMsg) object; + + } catch (IllegalAccessException | IllegalArgumentException | InstantiationException | ExceptionInInitializerError e) { + Logger.error( e); + + } catch (InvocationTargetException e) { + if (e.getCause() != null + && e.getCause().getClass() == BufferUnderflowException.class) + throw new BufferUnderflowException(); + Logger.error(e); + } + return null; + } + + public ByteBuffer getInternalBuffer() { + return internalBuffer; + } + +} diff --git a/src/engine/net/NetMsgHandler.java b/src/engine/net/NetMsgHandler.java new file mode 100644 index 00000000..b208ffd3 --- /dev/null +++ b/src/engine/net/NetMsgHandler.java @@ -0,0 +1,17 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net; + +import engine.net.client.msg.ClientNetMsg; + +public interface NetMsgHandler { + + public abstract boolean handleClientMsg(ClientNetMsg msg); +} diff --git a/src/engine/net/NetMsgStat.java b/src/engine/net/NetMsgStat.java new file mode 100644 index 00000000..f54e3b29 --- /dev/null +++ b/src/engine/net/NetMsgStat.java @@ -0,0 +1,77 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net; + +import engine.net.client.Protocol; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +public class NetMsgStat { + + private final Protocol protocolMsg; + private final AtomicLong total = new AtomicLong(); + private final AtomicLong count = new AtomicLong(); + private final AtomicInteger average = new AtomicInteger(); + private final AtomicInteger max = new AtomicInteger(); + private final AtomicInteger countUnderAverage = new AtomicInteger(); + private final AtomicInteger countOverAverage = new AtomicInteger(); + private final AtomicInteger countOverMax = new AtomicInteger(); + + public NetMsgStat(Protocol protocolMsg, int startSize) { + + if (startSize < 10) + startSize = 10; + + if (startSize > 30) + startSize = 30; + + this.protocolMsg = protocolMsg; + this.total.set(startSize); + this.count.set(1L); + this.average.set(10); + this.max.set(startSize); + this.countUnderAverage.set(0); + this.countOverAverage.set(0); + this.countOverMax.set(0); + } + + public void updateStat(int i) { + this.total.addAndGet(i); + this.count.incrementAndGet(); + + int avg = (int) (this.total.get() / this.count.get()); + if (avg < 0) + avg = 0; + else if (avg > 30) + avg = 30; + else + this.average.set(avg); + + if (this.max.get() < i) + this.max.set(i); + + if (i <= avg) + this.countUnderAverage.incrementAndGet(); + else if (i < this.max.get()) + this.countOverAverage.incrementAndGet(); + else + this.countOverMax.incrementAndGet(); + } + + public Protocol getOpcode() { + return this.protocolMsg; + } + + public int getMax() { + return this.max.get(); + } + +} diff --git a/src/engine/net/Network.java b/src/engine/net/Network.java new file mode 100644 index 00000000..7bef5e73 --- /dev/null +++ b/src/engine/net/Network.java @@ -0,0 +1,60 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net; + +import engine.pooling.MultisizeByteBufferPool; + +import java.nio.ByteBuffer; + +public class Network { + + public static final int INITIAL_SOCKET_BUFFER_SIZE = 128 * 1024; + public static final int INITIAL_BYTEBUFFER_POOL_SIZE = 256; + + public static final MultisizeByteBufferPool byteBufferPool = new MultisizeByteBufferPool(); + + public static void init() { + //Force a few to be created. + + //Small (2^10-15) + for (int a = 10; a < 16; ++a) { + for (int i = 0; i < 50; ++i) { + byteBufferPool.putBuffer(ByteBuffer.allocateDirect(MultisizeByteBufferPool.powersOfTwo[a])); + } + } + + //standard size (2^16) + for (int i = 0; i < 100; ++i) { + byteBufferPool.putBuffer(ByteBuffer.allocateDirect(MultisizeByteBufferPool.powersOfTwo[16])); + } + + //Large (2^17) + for (int i = 0; i < 50; ++i) { + byteBufferPool.putBuffer(ByteBuffer.allocateDirect(MultisizeByteBufferPool.powersOfTwo[17])); + } + + // NetMsgFactory size (2^18) + for (int i = 0; i < 64; ++i) { + byteBufferPool.putBuffer(ByteBuffer + .allocateDirect(MultisizeByteBufferPool.powersOfTwo[18])); + } + + //Very Large (2^19) + for (int i = 0; i < 25; ++i) { + byteBufferPool.putBuffer(ByteBuffer.allocateDirect(MultisizeByteBufferPool.powersOfTwo[19])); + } + + //Very Large (2^20) + for (int i = 0; i < 10; ++i) { + byteBufferPool.putBuffer(ByteBuffer.allocateDirect(MultisizeByteBufferPool.powersOfTwo[20])); + } + } + +} diff --git a/src/engine/net/client/ClientAuthenticator.java b/src/engine/net/client/ClientAuthenticator.java new file mode 100644 index 00000000..dd36f503 --- /dev/null +++ b/src/engine/net/client/ClientAuthenticator.java @@ -0,0 +1,381 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client; + +import engine.net.AbstractConnection; +import engine.server.MBServerStatics; +import engine.util.ByteUtils; +import org.pmw.tinylog.Logger; + +import javax.crypto.Cipher; +import javax.crypto.KeyAgreement; +import javax.crypto.ShortBufferException; +import javax.crypto.interfaces.DHPublicKey; +import javax.crypto.spec.DHParameterSpec; +import javax.crypto.spec.DHPublicKeySpec; +import javax.crypto.spec.SecretKeySpec; +import java.io.IOException; +import java.math.BigInteger; +import java.nio.BufferOverflowException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.security.*; + +public class ClientAuthenticator { + + private final AbstractConnection origin; + + private ByteBuffer buffer = ByteBuffer.allocate(100); + private byte[] secretKeyBytes = new byte[16]; + private SecretKeySpec BFKey; + + private Cipher cipher; + private byte[] iVecEnc = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + private byte[] iVecDec = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + private int iVecEncOffset = 0; + private int iVecDecOffset = 0; + private int totalRead = 0; + private KeyFactory keyFactory; + private DHParameterSpec dhParamSpec; + private KeyPairGenerator keyPairGen; + private KeyAgreement keyAgree; + private boolean initialized = false; + private boolean keyInit = false; + private byte[] secretKey; + private byte[] serverPublicKey; + + public ClientAuthenticator(AbstractConnection origin) { + super(); + this.origin = origin; + try { + // init the resuable Crypto Stuff. + this.keyFactory = KeyFactory.getInstance("DH"); + this.dhParamSpec = new DHParameterSpec(ClientAuthenticator.P, ClientAuthenticator.G); + + this.keyPairGen = KeyPairGenerator.getInstance("DH"); + this.keyPairGen.initialize(this.dhParamSpec); + this.keyAgree = KeyAgreement.getInstance("DH"); + + } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { + Logger.error("NoSuchAlgorithmException " + e.getMessage()); + this.keyInit = false; + return; + } + + this.keyInit = true; + } + + private void calcKeys(AbstractConnection origin, byte[] clientPublicKeyBytes) { + + try { + // get the forwarded client public key, in byte[] form. + + // Convert client public key to a BigInteger + BigInteger clientPublicKeyBI = new BigInteger(1, clientPublicKeyBytes); + + // convert the client's Public Key to a DHPublicKey object + DHPublicKeySpec dhKeySpec = new DHPublicKeySpec(clientPublicKeyBI, ClientAuthenticator.P, ClientAuthenticator.G); + DHPublicKey clientPublicKey = (DHPublicKey) this.keyFactory.generatePublic(dhKeySpec); + + // Now calculate the server's PublicKey + byte[] serverPublicKeyBytes = new byte[96]; + boolean invalid = true; + + int tryCnt = 1; + while (invalid) { + KeyPair keyPair = keyPairGen.generateKeyPair(); + this.keyAgree = KeyAgreement.getInstance("DH"); + this.keyAgree.init(keyPair.getPrivate()); + DHPublicKey serverPublicKey = (DHPublicKey) keyPair.getPublic(); + + String hex = serverPublicKey.getY().toString(16); + + if (hex.length() == 192) { + invalid = false; + serverPublicKeyBytes = ByteUtils.stringHexToByteArray(hex); + } + if (tryCnt >= 5) + // Give java 4 tries to get a Public key of valid length. + throw new Exception("Not able to generate a valid length public key"); + ++tryCnt; + } + + // Calculate shared DH Secret Key + keyAgree.doPhase(clientPublicKey, true); + this.secretKey = keyAgree.generateSecret(); + this.serverPublicKey = serverPublicKeyBytes; + + // Now we have the server's publicKey and the common secretKey + } catch (Exception e) { + origin.disconnect(); + Logger.error(e); + } + + } + + public synchronized int initialize(AbstractConnection origin) { + long startTime = System.currentTimeMillis(); + int read = -1; + + // Read the data from connections socket channel + try { + read = origin.getSocketChannel().read(this.buffer); + } catch (IOException e) { + if (e.getLocalizedMessage() != null && !e.getLocalizedMessage().equals(MBServerStatics.EXISTING_CONNECTION_CLOSED) && !e.getLocalizedMessage().equals(MBServerStatics.RESET_BY_PEER)) + Logger.error(e); + origin.disconnect(); + return 0; + } + + if (read == -1) { + Logger.info("EOF on Socket Channel, Disconnecting " + origin.getLocalAddressAndPortAsString()); + origin.disconnect(); + return read; + } + + this.totalRead += read; + + if (this.totalRead > 100) + Logger.error( "Possible Spam warning: " + + origin.getSocketChannel().socket().toString()); + + // Not all arrived yet, so wait for more + if (this.totalRead < 100) + return read; + + this.buffer.flip(); + this.buffer.getInt(); // get the length first & throw away value. + + byte[] PeerPubKeyEnc = new byte[96]; + this.buffer.get(PeerPubKeyEnc); + + this.calcKeys(origin, PeerPubKeyEnc); + + try { + byte[] sharedSecret = this.secretKey; + byte[] PubKeyEnc = this.serverPublicKey; + + // Cut DH SecretKey down to 16 bytes + System.arraycopy(sharedSecret, 0, secretKeyBytes, 0, 16); + + // Calculate Blowfish Secret Key and make ciphers streams. + this.BFKey = new SecretKeySpec(this.secretKeyBytes, "Blowfish"); + + // Initialize cipher and Ivecs. + // Ivecs must be run through the cipher once + // to prep the cipher for cfb mode. + this.cipher = Cipher.getInstance("Blowfish/ECB/NoPadding"); + this.cipher.init(Cipher.ENCRYPT_MODE, this.BFKey); + this.cipher.update(this.iVecEnc, 0, 8, this.iVecEnc, 0); + this.cipher.update(this.iVecDec, 0, 8, this.iVecDec, 0); + + // Send public key to peer + byte[] pubKeyLen = {0x00, 0x00, 0x00, 0x60}; // hex for 96 + ByteBuffer bb = ByteBuffer.wrap(pubKeyLen); + bb.position(bb.limit()); + origin.sendBB(bb); + bb = ByteBuffer.wrap(PubKeyEnc); + bb.position(bb.limit()); + origin.sendBB(bb); + } catch (Exception e) { + Logger.error(e); + origin.disconnect(); + return read; + } + /* + * //Send Secret Key to Login Server if (PortalServer.ServerType == + * "Login") { byte[] keyinfo = new byte[20]; keyinfo[0] = 0x11; + * keyinfo[1] = 0x11; keyinfo[2] = 0x11; keyinfo[3] = 0x11; for (int i = + * 0; i < 16; i++) keyinfo[i + 4] = ShortSharedSecret[i]; + * PortalPair.HandleOutput(keyinfo, 20); } + */ + this.initialized = true; + +// long endTime = System.currentTimeMillis(); +// +// long time = endTime - startTime; +// // Logger.debug("", "Authenticator took " + time + " ms to initialize."); + + return read; + } + + public synchronized void encrypt(final ByteBuffer dataIn, final ByteBuffer dataOut) { + try { + // Assume that if position != 0 that we need to flip. + if (dataIn.position() != 0) + dataIn.flip(); + + int count = dataIn.limit(); + + //Line up the iVecEncOffset.. fall through is intentional + if (iVecEncOffset != 0) + if ((this.iVecEncOffset + dataIn.limit()) < 8) { + //This handles cases where the net msg + offset won't reach 8 bytes total + //prevents BufferUnderflowException in small net messages. - + int newEncOffset = this.iVecEncOffset + dataIn.limit(); + for (int i = this.iVecEncOffset; i < newEncOffset; i++) { + this.iVecEnc[i] = (byte) (dataIn.get() ^ this.iVecEnc[i]); + dataOut.put(this.iVecEnc[i]); + } + this.iVecEncOffset = newEncOffset; + return; + } else + switch (iVecEncOffset) { + case 1: + this.iVecEnc[1] = (byte) (dataIn.get() ^ this.iVecEnc[1]); + dataOut.put(this.iVecEnc[1]); + case 2: + this.iVecEnc[2] = (byte) (dataIn.get() ^ this.iVecEnc[2]); + dataOut.put(this.iVecEnc[2]); + case 3: + this.iVecEnc[3] = (byte) (dataIn.get() ^ this.iVecEnc[3]); + dataOut.put(this.iVecEnc[3]); + case 4: + this.iVecEnc[4] = (byte) (dataIn.get() ^ this.iVecEnc[4]); + dataOut.put(this.iVecEnc[4]); + case 5: + this.iVecEnc[5] = (byte) (dataIn.get() ^ this.iVecEnc[5]); + dataOut.put(this.iVecEnc[5]); + case 6: + this.iVecEnc[6] = (byte) (dataIn.get() ^ this.iVecEnc[6]); + dataOut.put(this.iVecEnc[6]); + case 7: + this.iVecEnc[7] = (byte) (dataIn.get() ^ this.iVecEnc[7]); + dataOut.put(this.iVecEnc[7]); + count -= (8 - iVecEncOffset); + this.iVecEncOffset = 0; + this.cipher.update(this.iVecEnc, 0, 8, this.iVecEnc, 0); + } + + //Main loop - unrolled x8 + int loopCount = (count) >> 3; + for (int i = 0; i < loopCount; i++) { + + this.iVecEnc[0] = (byte) (dataIn.get() ^ this.iVecEnc[0]); + this.iVecEnc[1] = (byte) (dataIn.get() ^ this.iVecEnc[1]); + this.iVecEnc[2] = (byte) (dataIn.get() ^ this.iVecEnc[2]); + this.iVecEnc[3] = (byte) (dataIn.get() ^ this.iVecEnc[3]); + this.iVecEnc[4] = (byte) (dataIn.get() ^ this.iVecEnc[4]); + this.iVecEnc[5] = (byte) (dataIn.get() ^ this.iVecEnc[5]); + this.iVecEnc[6] = (byte) (dataIn.get() ^ this.iVecEnc[6]); + this.iVecEnc[7] = (byte) (dataIn.get() ^ this.iVecEnc[7]); + + dataOut.put(this.iVecEnc[0]); + dataOut.put(this.iVecEnc[1]); + dataOut.put(this.iVecEnc[2]); + dataOut.put(this.iVecEnc[3]); + dataOut.put(this.iVecEnc[4]); + dataOut.put(this.iVecEnc[5]); + dataOut.put(this.iVecEnc[6]); + dataOut.put(this.iVecEnc[7]); + this.cipher.update(this.iVecEnc, 0, 8, this.iVecEnc, 0); + } + + //Resync the iVecEncOffset to handle the remainder.. + this.iVecEncOffset = count % 8; + for (int i = 0; i < iVecEncOffset; i++) { + this.iVecEnc[i] = (byte) (dataIn.get() ^ this.iVecEnc[i]); + dataOut.put(this.iVecEnc[i]); + } + } catch (BufferUnderflowException e) { + if (dataIn != null && dataOut != null) + Logger.warn("Encrypt Error: (in)" + dataIn.toString() + " :: (out)" + dataOut.toString()); + Logger.error("ClientAuth.encrypt() -> Underflow" + e); + } catch (BufferOverflowException e) { + if (dataIn != null && dataOut != null) + Logger.warn("Encrypt Error: (in)" + dataIn.toString() + " :: (out)" + dataOut.toString()); + Logger.error("ClientAuth.encrypt() -> Overflow" + e); + } catch (Exception e) { + Logger.error("ClientAuth.encrypt()" + e); + } + } + + public synchronized void decrypt(ByteBuffer dataIn, ByteBuffer dataOut) { + try { // get lock + synchronized (dataIn) { // TODO is this lock needed? + + // Assume that if position != 0 that we need to flip. + if (dataIn.position() != 0) + dataIn.flip(); + + byte encryptedByte; + byte decryptedByte; + + for (int i = 0; i < dataIn.limit(); ++i) { + + // Get byte out of ByteBuffer + encryptedByte = dataIn.get(); + + // XOR it against the iVEC + decryptedByte = (byte) (encryptedByte ^ this.iVecDec[this.iVecDecOffset]); + + // put the decrypted byte into the outgoing ByteBuffer + dataOut.put(decryptedByte); + + // store the encrypted byte back into the iVEC + this.iVecDec[this.iVecDecOffset] = encryptedByte; + + // Increment ivecOffset + this.iVecDecOffset++; + + // Check to see if iVecOffset is at MAX. If so, reset + if (this.iVecDecOffset > 7) { + try { + this.cipher.update(this.iVecDec, 0, 8, + this.iVecDec, 0); + } catch (ShortBufferException e) { + // suck up this error + Logger.error(e); + } + this.iVecDecOffset = 0; + } + } + + + } + } catch (Exception e) { + Logger.error("ClientAuth.decrypt()" + e); + } + } + + private void initiateKey(byte[] clientPublicKeyBytes) { + + } + + /** + * @return the secretKeyBytes + */ + public byte[] getSecretKeyBytes() { + return secretKeyBytes; + } + + /** + * @return the initialized + */ + public boolean initialized() { + return initialized; + } + + private static final byte P_Bytes[] = {(byte) 0xFB, (byte) 0x46, (byte) 0x56, (byte) 0xB4, (byte) 0xBE, (byte) 0x81, (byte) 0xA4, + (byte) 0x2C, (byte) 0x37, (byte) 0xC4, (byte) 0xA2, (byte) 0x61, (byte) 0x4A, (byte) 0xAC, (byte) 0x65, (byte) 0x90, + (byte) 0x31, (byte) 0xB6, (byte) 0x83, (byte) 0x26, (byte) 0x63, (byte) 0x94, (byte) 0x08, (byte) 0x95, (byte) 0x56, + (byte) 0x8D, (byte) 0x5E, (byte) 0xBF, (byte) 0x94, (byte) 0x10, (byte) 0x5A, (byte) 0x37, (byte) 0xB6, (byte) 0x82, + (byte) 0x1A, (byte) 0x75, (byte) 0x2B, (byte) 0xF1, (byte) 0x94, (byte) 0xB7, (byte) 0x7E, (byte) 0x56, (byte) 0xC6, + (byte) 0xD1, (byte) 0xF5, (byte) 0x18, (byte) 0xE1, (byte) 0xA5, (byte) 0x13, (byte) 0x9E, (byte) 0xC1, (byte) 0x85, + (byte) 0x98, (byte) 0xB7, (byte) 0x32, (byte) 0xDB, (byte) 0x38, (byte) 0x09, (byte) 0x1A, (byte) 0xF8, (byte) 0x5C, + (byte) 0xDA, (byte) 0x4F, (byte) 0x9F, (byte) 0x67, (byte) 0x93, (byte) 0x72, (byte) 0x8F, (byte) 0x75, (byte) 0x4F, + (byte) 0x0B, (byte) 0xBD, (byte) 0x69, (byte) 0x61, (byte) 0x97, (byte) 0x1F, (byte) 0xEE, (byte) 0xFB, (byte) 0x5B, + (byte) 0xB0, (byte) 0x85, (byte) 0xC4, (byte) 0x27, (byte) 0x7E, (byte) 0x41, (byte) 0x42, (byte) 0xC2, (byte) 0xF1, + (byte) 0xDA, (byte) 0x64, (byte) 0x8F, (byte) 0x4E, (byte) 0x28, (byte) 0xFD, (byte) 0x2A, (byte) 0x63}; + + private static final BigInteger P = new BigInteger(1, P_Bytes); + private static final BigInteger G = BigInteger.valueOf(5); + +} diff --git a/src/engine/net/client/ClientConnection.java b/src/engine/net/client/ClientConnection.java new file mode 100644 index 00000000..0ca889be --- /dev/null +++ b/src/engine/net/client/ClientConnection.java @@ -0,0 +1,354 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.net.client; + +import engine.Enum; +import engine.gameManager.ConfigManager; +import engine.gameManager.SessionManager; +import engine.job.JobScheduler; +import engine.jobs.DisconnectJob; +import engine.net.AbstractConnection; +import engine.net.AbstractNetMsg; +import engine.net.Network; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.login.LoginErrorMsg; +import engine.objects.Account; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; +import engine.session.SessionID; +import org.pmw.tinylog.Logger; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.NotYetConnectedException; +import java.nio.channels.SocketChannel; +import java.util.concurrent.locks.ReentrantLock; + +public class ClientConnection extends AbstractConnection { + + // Enumeration of a message's origin for logging purposes + private enum MessageSource { + + SOURCE_CLIENT, + SOURCE_SERVER + } + + private byte cryptoInitTries = 0; + protected SessionID sessionID = null; + private final ClientAuthenticator crypto; + private final String clientIpAddress; + public String machineID; + public long guildtreespam = 0; + public long ordernpcspam = 0; + public ReentrantLock trainLock = new ReentrantLock(); + public ReentrantLock sellLock = new ReentrantLock(); + public ReentrantLock buyLock = new ReentrantLock(); + + public boolean desyncDebug = false; + + public byte[] lastByteBuffer; + + public ClientConnection(ClientConnectionManager connMan, + SocketChannel sockChan) { + super(connMan, sockChan, true); + this.crypto = new ClientAuthenticator(this); + + this.clientIpAddress = sockChan.socket().getRemoteSocketAddress() + .toString().replace("/", "").split(":")[0]; + } + + @Override + protected boolean _sendMsg(AbstractNetMsg msg) { + try { + msg.setOrigin(this); + ByteBuffer bb = msg.serialize(); + + // Application protocol logging toggled via + // DevCmd: netdebug on|off + + if (MBServerStatics.DEBUG_PROTOCOL) + applicationProtocolLogger(msg, MessageSource.SOURCE_SERVER); + + boolean retval = this.sendBB(bb); + Network.byteBufferPool.putBuffer(bb);//return here. + + return retval; + + } catch (Exception e) { // Catch-all + Logger.error(e); + return false; + } + } + + /** + * Sending a NetMsg to the client involves NOT including a dataLen parameter + * and also involves encrypting the data. + * + */ + @Override + protected boolean _sendBB(ByteBuffer bb) { + boolean useCrypto = this.crypto.initialized(); + boolean retVal; + + // Logger.debug("useCrypto: " + useCrypto + ". bb.cap(): " + + // bb.capacity()); + if (useCrypto == false) + retVal = super._sendBB(bb); + else { + if (bb == null) + Logger.error("Incoming bb is null"); + ByteBuffer encrypted = Network.byteBufferPool.getBufferToFit(bb.capacity()); + if (encrypted == null) + Logger.error("Encrypted bb is null"); + this.crypto.encrypt(bb, encrypted); + retVal = super._sendBB(encrypted); + } + + return retVal; + } + + /** + * Receiving data from a client involves the initial Crypto Key Exchange, + * waiting for a complete NetMsg to arrive using an accumulation factory and + * decrypting the data. + */ + //FIXME the int return value on this means nothing! Clean it up! + @Override + protected int read() { + + if (readLock.tryLock()) + try { + + // First and foremost, check to see if we the Crypto is initted yet + if (!this.crypto.initialized()) + this.crypto.initialize(this); + + if (!this.crypto.initialized()) { + ++this.cryptoInitTries; + if (this.cryptoInitTries >= MBServerStatics.MAX_CRYPTO_INIT_TRIES) { + Logger.info("Failed to initialize after " + + MBServerStatics.MAX_CRYPTO_INIT_TRIES + + " tries. Disconnecting."); + this.disconnect(); + } + return 0; + } + + // check to see if SessionID == null; + if (this.sessionID == null) + this.sessionID = new SessionID(this.crypto.getSecretKeyBytes()); + + // Get ByteBuffers out of pool. + ByteBuffer bb = Network.byteBufferPool.getBuffer(16); + ByteBuffer decrypted = Network.byteBufferPool.getBuffer(16); + // ByteBuffer bb = ByteBuffer.allocate(1024 * 4); + + int totalBytesRead = 0; + int lastRead = 0; + do { + try { + bb.clear(); + decrypted.clear(); + lastRead = this.sockChan.read(bb); + // On EOF on the SocketChannel, disconnect. + if (lastRead <= -1) { + this.disconnect(); + break; + } + + if (lastRead == 0) + continue; + + // ByteBuffer decrypted = ByteBuffer.allocate(lastRead); + this.crypto.decrypt(bb, decrypted); + this.factory.addData(decrypted); + + + this.checkInternalFactory(); + + totalBytesRead += lastRead; + + + } catch (NotYetConnectedException e) { + Logger.error(e.getLocalizedMessage()); + totalBytesRead = -1; // Set error retVal + break; + + } catch (ClosedChannelException e) { + // TODO Should a closed channel be logged or just cleaned up? + // this.logEXCEPTION(e); + this.disconnect(); + totalBytesRead = -1; // Set error retVal + break; + + } catch (IOException e) { + if ( e.getLocalizedMessage() != null && (!e.getLocalizedMessage().equals(MBServerStatics.EXISTING_CONNECTION_CLOSED) && !e.getLocalizedMessage().equals(MBServerStatics.RESET_BY_PEER))){ + Logger.info("Error Reading message opcode " + this.lastOpcode); + Logger.error(e); + } + this.disconnect(); + totalBytesRead = -1; // Set error retVal + break; + + } catch (Exception e){ + Logger.info("Error Reading message opcode " + this.lastOpcode); + Logger.error(e); + totalBytesRead = -1; // Set error retVal + this.disconnect(); + break; + } + } + + while (lastRead > 0); + + Network.byteBufferPool.putBuffer(bb); + Network.byteBufferPool.putBuffer(decrypted); + + return totalBytesRead; + + } finally { + readLock.unlock(); + } + else { + Logger.debug("Another thread already has a read lock! Skipping."); + return 0; + } + } + + @Override + public void disconnect() { + super.disconnect(); + try { + + if (ConfigManager.serverType.equals(Enum.ServerType.WORLDSERVER)) + ConfigManager.worldServer.removeClient(this); + else + ConfigManager.loginServer.removeClient(this); + + // TODO There has to be a more direct way to do this... + SessionManager.remSession( + SessionManager.getSession(sessionID)); + } catch (NullPointerException e) { + Logger + .error( + "Tried to remove improperly initialized session. Skipping." + + e); + } + } + + public void forceDisconnect() { + super.disconnect(); + } + + /* + * Getters n setters + */ + + public SessionID getSessionID() { + return sessionID; + } + + public byte[] getSecretKeyBytes() { + return this.crypto.getSecretKeyBytes(); + } + + /* + * Convenience getters for SessionManager + */ + public Account getAccount() { + return SessionManager.getAccount(this); + } + + public PlayerCharacter getPlayerCharacter() { + return SessionManager.getPlayerCharacter(this); + } + + @Override + public boolean handleClientMsg(ClientNetMsg msg) { + + Protocol protocolMsg = msg.getProtocolMsg(); + + switch (protocolMsg) { + case KEEPALIVESERVERCLIENT: + this.setLastKeepAliveTime(); + break; + // case ClientOpcodes.OpenVault: + // case ClientOpcodes.Random: + // case ClientOpcodes.DoorTryOpen: + // case ClientOpcodes.SetSelectedObect: + // case ClientOpcodes.MoveObjectToContainer: + // case ClientOpcodes.ToggleSitStand: + // case ClientOpcodes.SocialChannel: + // case ClientOpcodes.OpenFriendsCondemnList: + case SELLOBJECT: + this.setLastMsgTime(); + break; + case MOVETOPOINT: + case ARCCOMBATMODEATTACKING: + this.setLastMsgTime(); + break; + default: + this.setLastMsgTime(); + break; + } + + // Application protocol logging toggled via + // DevCmd: netdebug on|off + + if (MBServerStatics.DEBUG_PROTOCOL) + applicationProtocolLogger(msg, MessageSource.SOURCE_CLIENT); + + return ConfigManager.handler.handleClientMsg(msg); // *** Refactor : Null check then call + } + // Method logs detailed information about application + // protocol traffic. Toggled at runtime via the + // DevCmd netdebug on|off + + private void applicationProtocolLogger(AbstractNetMsg msg, MessageSource origin) { + + String outString = ""; + PlayerCharacter tempPlayer = null; + + // Log the protocolMsg + if (origin == MessageSource.SOURCE_CLIENT) + outString = " Incoming protocolMsg: "; + else + outString = " Outgoing protocolMsg: "; + + Logger.info(outString + + Integer.toHexString(msg.getProtocolMsg().opcode) + + '/' + msg.getProtocolMsg()); + + // Dump message contents using reflection + tempPlayer = this.getPlayerCharacter(); + outString = ""; + outString += (tempPlayer == null) ? "PlayerUnknown" : tempPlayer.getFirstName() + ' ' + + msg.toString(); + Logger.info(outString); + } + + public void kickToLogin(int errCode, String message) { + + LoginErrorMsg lom = new LoginErrorMsg(errCode, message); + + if (!sendMsg(lom)) + Logger.error("Failed to send message"); // TODO Do we just accept this failure to send Msg? + + DisconnectJob dj = new DisconnectJob(this); + JobScheduler.getInstance().scheduleJob(dj, 250); + + } + + public final String getClientIpAddress() { + return this.clientIpAddress; + } +} diff --git a/src/engine/net/client/ClientConnectionManager.java b/src/engine/net/client/ClientConnectionManager.java new file mode 100644 index 00000000..0ccf5fb8 --- /dev/null +++ b/src/engine/net/client/ClientConnectionManager.java @@ -0,0 +1,35 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client; + +import engine.net.AbstractConnection; +import engine.net.AbstractConnectionManager; + +import java.io.IOException; +import java.net.InetAddress; +import java.nio.channels.SocketChannel; + +public class ClientConnectionManager extends AbstractConnectionManager { + + public ClientConnectionManager(String threadName, InetAddress hostAddress, int port) + throws IOException { + super(threadName, hostAddress, port); + } + + @Override + protected AbstractConnection getNewIncomingConnection(SocketChannel sockChan) { + return new ClientConnection(this, sockChan); + } + + @Override + protected AbstractConnection getNewOutgoingConnection(SocketChannel sockChan) { + return new ClientConnection(this, sockChan); + } +} diff --git a/src/engine/net/client/ClientMessagePump.java b/src/engine/net/client/ClientMessagePump.java new file mode 100644 index 00000000..07ede49c --- /dev/null +++ b/src/engine/net/client/ClientMessagePump.java @@ -0,0 +1,2340 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.net.client; + +import engine.Enum.*; +import engine.InterestManagement.WorldGrid; +import engine.ai.MobileFSM.STATE; +import engine.exception.MsgSendException; +import engine.gameManager.*; +import engine.job.JobContainer; +import engine.job.JobScheduler; +import engine.jobs.RefreshGroupJob; +import engine.jobs.StuckJob; +import engine.math.Vector3fImmutable; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.NetMsgHandler; +import engine.net.client.handlers.AbstractClientMsgHandler; +import engine.net.client.msg.*; +import engine.net.client.msg.chat.AbstractChatMsg; +import engine.net.client.msg.commands.ClientAdminCommandMsg; +import engine.objects.*; +import engine.server.MBServerStatics; +import engine.server.world.WorldServer; +import engine.session.Session; +import engine.util.StringUtils; +import org.pmw.tinylog.Logger; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; + +import static engine.math.FastMath.sqr; + +/** + * @author: + * @summary: This class is the mainline router for application protocol + * messages received by the client. + */ + +public class ClientMessagePump implements NetMsgHandler { + + // Instance variable declaration + + private final WorldServer server; + + public ClientMessagePump(WorldServer server) { + super(); + this.server = server; + } + + /* + * Incoming client protocol message are processed here + */ + + @Override + public boolean handleClientMsg(ClientNetMsg msg) { + + if (msg == null) { + Logger.error("handleClientMsg", "Recieved null msg. Returning."); + return false; + } + + ClientConnection origin; + Protocol protocolMsg = Protocol.NONE; + Session s; + + try { + + // Try registered opcodes first as we take a hatchet to this GodObject + + AbstractClientMsgHandler msgHandler = msg.getProtocolMsg().handler; + + if (msgHandler != null) + return msgHandler.handleNetMsg(msg); + + // Any remaining opcodes fall through and are routed + // through this ungodly switch of doom. + + origin = (ClientConnection) msg.getOrigin(); + s = SessionManager.getSession(origin); + + protocolMsg = msg.getProtocolMsg(); + + switch (protocolMsg) { + case SETSELECTEDOBECT: + ClientMessagePump.targetObject((TargetObjectMsg) msg, origin); + break; + case CITYDATA: + ClientMessagePump.MapData(s, origin); + break; + + /* + * Chat + */ + + // Simplify by fall through. Route in ChatManager + case CHATSAY: + case CHATSHOUT: + case CHATTELL: + case CHATGUILD: + case CHATGROUP: + case CHATPVP: + case CHATIC: + case CHATCITY: + case CHATINFO: + case SYSTEMBROADCASTCHANNEL: + case CHATCSR: + case SYSTEMCHANNEL: + case GLOBALCHANNELMESSAGE: + case LEADERCHANNELMESSAGE: + ChatManager.handleChatMsg(s, (AbstractChatMsg) msg); + break; + case UPDATESTATE: + UpdateStateMsg rwss = (UpdateStateMsg) msg; + runWalkSitStand(rwss, origin); + break; + case ACTIVATECHARTER: + UseCharterMsg ucm = (UseCharterMsg) msg; + ucm.setUnknown02(1); + ucm.configure(); + Dispatch dispatch = Dispatch.borrow(origin.getPlayerCharacter(), ucm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + break; + case CHECKUNIQUEGUILD: + break; + case CREATEPETITION: + break; + case CANCELGUILDCREATION: + break; + case LEAVEREQUEST: + origin.disconnect(); + break; + case POWER: + PowersManager.usePower((PerformActionMsg) msg, origin, false); + break; + case REQUESTMELEEATTACK: + CombatManager.setAttackTarget((AttackCmdMsg) msg, origin); + break; + case READYTOENTER: + break; + case OPENVAULT: + break; + case WHOREQUEST: + WhoRequest((WhoRequestMsg) msg, origin); + break; + case CLIENTADMINCOMMAND: + ChatManager.HandleClientAdminCmd((ClientAdminCommandMsg) msg, origin); + break; + case SOCIALCHANNEL: + social((SocialMsg) msg, origin); + break; + case COMBATMODE: + CombatManager.toggleCombat((ToggleCombatMsg) msg, origin); + break; + case ARCCOMBATMODEATTACKING: + CombatManager.toggleCombat((SetCombatModeMsg) msg, origin); + break; + case MODIFYGUILDSTATE: + ToggleLfgRecruitingMsg tlrm = (ToggleLfgRecruitingMsg) msg; + toggleLfgRecruiting(tlrm, origin); + break; + case TOGGLESITSTAND: + ToggleSitStandMsg tssm = (ToggleSitStandMsg) msg; + toggleSitStand(tssm, origin); + break; + case GUILDTREESTATUS: + GuildTreeStatusMsg((GuildTreeStatusMsg) msg, origin); + break; + case CUSTOMERPETITION: + Logger.info("CCR Petition received: " + msg.toString()); + // TODO need to send something back to client + // TODO what to do with petition? + break; + case IGNORE: + ((IgnoreMsg) msg).handleRequest(origin); + break; + case UNEQUIP: + TransferItemFromEquipToInventory((TransferItemFromEquipToInventoryMsg) msg, origin); + break; + case EQUIP: + TransferItemFromInventoryToEquip((TransferItemFromInventoryToEquipMsg) msg, origin); + break; + case DELETEOBJECT: + DeleteItem((DeleteItemMsg) msg, origin); + break; + case VIEWRESOURCES: + ViewResourcesMessage((ViewResourcesMessage) msg, origin); + break; + case RAISEATTR: + modifyStat((ModifyStatMsg) msg, origin); + break; + case COSTTOOPENBANK: + ackBankWindowOpened((AckBankWindowOpenedMsg) msg, origin); + break; + case RESETAFTERDEATH: + respawn((RespawnMsg) msg, origin); + break; + case REQUESTCONTENTS: + lootWindowRequest((LootWindowRequestMsg) msg, origin); + break; + case MOVEOBJECTTOCONTAINER: + loot((LootMsg) msg, origin); + break; + case SHOWCOMBATINFO: + show((ShowMsg) msg, origin); + break; + case TRANSFERITEMTOBANK: + transferItemFromInventoryToBank((TransferItemFromInventoryToBankMsg) msg, origin); + break; + case TRANSFERITEMFROMBANK: + transferItemFromBankToInventory((TransferItemFromBankToInventoryMsg) msg, origin); + break; + case TRANSFERITEMFROMVAULTTOINVENTORY: + transferItemFromVaultToInventory((TransferItemFromVaultToInventoryMsg) msg, origin); + break; + case ITEMTOVAULT: + transferItemFromInventoryToVault((TransferItemFromInventoryToVaultMsg) msg, origin); + break; + case TRANSFERGOLDFROMVAULTTOINVENTORY: + transferGoldFromVaultToInventory((TransferGoldFromVaultToInventoryMsg) msg, origin); + break; + case GOLDTOVAULT: + transferGoldFromInventoryToVault((TransferGoldFromInventoryToVaultMsg) msg, origin); + break; + case REQUESTTOTRADE: + TradeManager.tradeRequest((TradeRequestMsg) msg, origin); + break; + case REQUESTTRADEOK: + TradeManager.acceptTradeRequest((AcceptTradeRequestMsg) msg, origin); + break; + case REQUESTTRADECANCEL: + TradeManager.rejectTradeRequest((RejectTradeRequestMsg) msg, origin); + break; + case TRADEADDOBJECT: + TradeManager.addItemToTradeWindow((AddItemToTradeWindowMsg) msg, origin); + break; + case TRADEADDGOLD: + TradeManager.addGoldToTradeWindow((AddGoldToTradeWindowMsg) msg, origin); + break; + case TRADECONFIRM: + TradeManager.commitToTrade((CommitToTradeMsg) msg, origin); + break; + case TRADEUNCONFIRM: + TradeManager.uncommitToTrade((UncommitToTradeMsg) msg, origin); + break; + case TRADECLOSE: + TradeManager.closeTradeWindow((CloseTradeWindowMsg) msg, origin); + break; + case ARCREQUESTTRADEBUSY: + TradeManager.invalidTradeRequest((InvalidTradeRequestMsg) msg); + break; + case VENDORDIALOG: + VendorDialogMsg.replyDialog((VendorDialogMsg) msg, origin); + break; + case ARCMINEWINDOWAVAILABLETIME: + MineWindowAvailableTime((ArcMineWindowAvailableTimeMsg) msg, origin); + break; + case ARCMINEWINDOWCHANGE: + MineWindowChange((ArcMineWindowChangeMsg) msg, origin); + break; + case ARCOWNEDMINESLIST: + ListOwnedMines((ArcOwnedMinesListMsg) msg, origin); + break; + case ARCMINECHANGEPRODUCTION: + changeMineProduction((ArcMineChangeProductionMsg) msg, origin); + break; + case SHOPLIST: + openBuyFromNPCWindow((BuyFromNPCWindowMsg) msg, origin); + break; + case BUYFROMNPC: + buyFromNPC((BuyFromNPCMsg) msg, origin); + break; + case SHOPINFO: + openSellToNPCWindow((SellToNPCWindowMsg) msg, origin); + break; + case SELLOBJECT: + sellToNPC((SellToNPCMsg) msg, origin); + break; + case REPAIROBJECT: + Repair((RepairMsg) msg, origin); + break; + case TRAINERLIST: + WorldServer.trainerInfo((TrainerInfoMsg) msg, origin); + break; + case ARCUNTRAINLIST: + WorldServer.refinerScreen((RefinerScreenMsg) msg, origin); + break; + case TRAINSKILL: + TrainMsg.train((TrainMsg) msg, origin); + break; + case ARCUNTRAINABILITY: + RefineMsg.refine((RefineMsg) msg, origin); + break; + case POWERTARGNAME: + PowersManager.summon((SendSummonsRequestMsg) msg, origin); + break; + case ARCSUMMON: + PowersManager.recvSummon((RecvSummonsRequestMsg) msg, origin); + break; + case ARCTRACKINGLIST: + PowersManager.trackWindow((TrackWindowMsg) msg, origin); + break; + case STUCK: + stuck(origin); + break; + case RANDOM: + ClientMessagePump.randomRoll((RandomMsg) msg, origin); + break; + case ARCPETATTACK: + petAttack((PetAttackMsg) msg, origin); + break; + case ARCPETCMD: + petCmd((PetCmdMsg) msg, origin); + break; + case MANAGENPC: + ManageNPCCmd((ManageNPCMsg) msg, origin); + break; + case ARCPROMPTRECALL: + HandlePromptRecall((PromptRecallMsg) msg, origin); + break; + case CHANNELMUTE: + break; + case KEEPALIVESERVERCLIENT: + break; + case UNKNOWN: + break; + + case CONFIRMPROMOTE: + break; + + default: + String ocHex = StringUtils.toHexString(protocolMsg.opcode); + Logger.error("Cannot handle Opcode: " + ocHex + " " + protocolMsg.name()); + return false; + + } + + } catch (MsgSendException | SQLException e) { + Logger.error("handler for " + protocolMsg + " failed: " + e); + return false; + } + + return true; + } + + // *** Refactor need to figure this out. + // Commented out for some reson or another. + + //TODO what is this used for? + private void ManageNPCCmd(ManageNPCMsg msg, ClientConnection origin) { + + } + + private static void MapData(Session s, ClientConnection origin) { + + Dispatch dispatch; +try{ + + + if (s == null || origin == null) + return; + + PlayerCharacter pc = s.getPlayerCharacter(); + + if (pc == null) + return; +boolean updateMine = false; +boolean updateCity = false; + +//do not update Cities and mines everytime you open map. only update them to client when something's changed. + long lastRefresh = pc.getTimeStamp("mineupdate"); + if (lastRefresh <= Mine.getLastChange()){ + pc.setTimeStamp("mineupdate", System.currentTimeMillis()); + updateMine = true; + } + long lastCityRefresh = pc.getTimeStamp("cityUpdate"); + if (lastCityRefresh <= City.lastCityUpdate){ + pc.setTimeStamp("cityUpdate", System.currentTimeMillis()); + updateCity = true; + } + + + + WorldObjectMsg wom = new WorldObjectMsg(s, false); + wom.updateMines(true); + wom.updateCities(updateCity); + dispatch = Dispatch.borrow(pc, wom); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + // } + + lastRefresh = pc.getTimeStamp("hotzoneupdate"); + if (lastRefresh <= WorldServer.getLastHZChange()) { + Zone hotzone = ZoneManager.getHotZone(); + if (hotzone != null) { + HotzoneChangeMsg hcm = new HotzoneChangeMsg(hotzone.getObjectType().ordinal(), hotzone.getObjectUUID()); + dispatch = Dispatch.borrow(pc, hcm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + pc.setTimeStamp("hotzoneupdate", System.currentTimeMillis() - 100); + } + } + + WorldRealmMsg wrm = new WorldRealmMsg(); + dispatch = Dispatch.borrow(pc, wrm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); +}catch(Exception e){ + e.printStackTrace(); +} + } + + private static void WhoRequest(WhoRequestMsg msg, ClientConnection origin) { + + // Handle /who request + PlayerCharacter pc = origin.getPlayerCharacter(); + + if (pc == null) + return; + + if (pc.getTimeStamp("WHO") > System.currentTimeMillis()) { + ErrorPopupMsg.sendErrorMsg(pc, "Who too fast! Please wait 3 seconds."); + return; + } + + WhoResponseMsg.HandleResponse(msg.getSet(), msg.getFilterType(), msg.getFilter(), origin); + pc.getTimestamps().put("WHO", System.currentTimeMillis() + 3000); + } + + private static void runWalkSitStand(UpdateStateMsg msg, ClientConnection origin) throws MsgSendException { + PlayerCharacter pc = SessionManager.getPlayerCharacter(origin); + if (pc == null) + return; + + pc.update(); + if (msg.getSpeed() == 2) + pc.setWalkMode(false); + else + pc.setWalkMode(true); + DispatchMessage.dispatchMsgToInterestArea(pc, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + } + + private static void toggleLfgRecruiting(ToggleLfgRecruitingMsg msg, ClientConnection origin) throws MsgSendException { + PlayerCharacter pc = SessionManager.getPlayerCharacter(origin); + if (pc == null) + return; + int num = msg.toggleLfgRecruiting(); + if (num == 1) + pc.toggleLFGroup(); + else if (num == 2) + pc.toggleLFGuild(); + else if (num == 3) + pc.toggleRecruiting(); + UpdateStateMsg rwss = new UpdateStateMsg(); + rwss.setPlayer(pc); + DispatchMessage.dispatchMsgToInterestArea(pc, rwss, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + } + + private static void toggleSitStand(ToggleSitStandMsg msg, ClientConnection origin) throws MsgSendException { + PlayerCharacter pc = SessionManager.getPlayerCharacter(origin); + if (pc == null) + return; + + pc.update(); + + pc.setSit(msg.toggleSitStand()); + + // cancel effects that break on sit + if (pc.isSit()) { + pc.setCombat(false); + pc.cancelOnSit(); + } + + UpdateStateMsg rwss = new UpdateStateMsg(); + if (pc.isSit()) { + pc.setCombat(false); + rwss.setAware(1); + } + rwss.setPlayer(pc); + + DispatchMessage.dispatchMsgToInterestArea(pc, rwss, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + } + + private static void targetObject(TargetObjectMsg msg, ClientConnection origin) { + PlayerCharacter pc = SessionManager.getPlayerCharacter(origin); + if (pc == null) + return; + + // TODO improve this later. hacky way to make sure player ingame is + // active. + + if (!pc.isActive()) + pc.setActive(true); + + pc.setLastTarget(GameObjectType.values()[msg.getTargetType()], msg.getTargetID()); + } + + private static void social(SocialMsg msg, ClientConnection origin) throws MsgSendException { + PlayerCharacter pc = SessionManager.getPlayerCharacter(origin); + if (pc == null) + return; + DispatchMessage.dispatchMsgToInterestArea(pc, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, true); + } + + private static void TransferItemFromEquipToInventory(TransferItemFromEquipToInventoryMsg msg, ClientConnection origin) { + PlayerCharacter pc = origin.getPlayerCharacter(); + if (pc == null) + return; + + CharacterItemManager itemManager = pc.getCharItemManager(); + if (itemManager == null) + return; + + int slot = msg.getSlotNumber(); + + Item i = itemManager.getItemFromEquipped(slot); + if (i == null) + return; + + if (!itemManager.doesCharOwnThisItem(i.getObjectUUID())) + return; + + //dupe check + if (!i.validForEquip(origin, pc, itemManager)) + return; + + if (i.containerType == ItemContainerType.EQUIPPED) + itemManager.moveItemToInventory(i); + + int ItemType = i.getObjectType().ordinal(); + int ItemID = i.getObjectUUID(); + for (String name : i.getEffects().keySet()) { + Effect eff = i.getEffects().get(name); + if (eff == null) + return; + ApplyEffectMsg pum = new ApplyEffectMsg(); + pum.setEffectID(eff.getEffectToken()); + pum.setSourceType(pc.getObjectType().ordinal()); + pum.setSourceID(pc.getObjectUUID()); + pum.setTargetType(pc.getObjectType().ordinal()); + pum.setTargetID(pc.getObjectUUID()); + pum.setNumTrains(eff.getTrains()); + pum.setUnknown05(1); + pum.setUnknown02(2); + pum.setUnknown06((byte) 1); + pum.setEffectSourceType(ItemType); + pum.setEffectSourceID(ItemID); + pum.setDuration(-1); + + + DispatchMessage.dispatchMsgToInterestArea(pc, pum, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);; + + } + // Update player formulas + pc.applyBonuses(); + DispatchMessage.dispatchMsgToInterestArea(pc, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); + + } + + //call this if the transfer fails server side to kick the item back to inventory from equip + private void forceTransferFromInventoryToEquip(TransferItemFromEquipToInventoryMsg msg, ClientConnection origin, String reason) { + //TODO add this later + //PATCHED CODEZZ + } + + private static void TransferItemFromInventoryToEquip(TransferItemFromInventoryToEquipMsg msg, ClientConnection origin) { + PlayerCharacter pc = origin.getPlayerCharacter(); + if (pc == null) + return; + + CharacterItemManager itemManager = pc.getCharItemManager(); + if (itemManager == null) { + forceTransferFromEquipToInventory(msg, origin, "Can't find your item manager"); + return; + } + + int uuid = msg.getUUID(); + int slot = msg.getSlotNumber(); + //System.out.println("loading to slot: " + slot); + + Item i = itemManager.getItemByUUID(uuid); + + if (i == null) { + forceTransferFromEquipToInventory(msg, origin, "Item not found in your item manager"); + return; + } + + if (!itemManager.doesCharOwnThisItem(i.getObjectUUID())) { + forceTransferFromEquipToInventory(msg, origin, "You do not own this item"); + return; + } + + //dupe check + if (!i.validForInventory(origin, pc, itemManager)) + return; + + if (i.containerType == ItemContainerType.INVENTORY) { + if (!itemManager.equipItem(i, (byte) slot)) { + forceTransferFromEquipToInventory(msg, origin, "Failed to transfer item."); + return; + } + } + else { + forceTransferFromEquipToInventory(msg, origin, "This item is not in your inventory"); + return; + } + + // Update player formulas + pc.applyBonuses(); + DispatchMessage.dispatchMsgToInterestArea(pc, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); + + + for (String name : i.getEffects().keySet()) { + Effect eff = i.getEffects().get(name); + if (eff == null) + return; + + ApplyEffectMsg pum = new ApplyEffectMsg(); + pum.setEffectID(eff.getEffectToken()); + pum.setSourceType(pc.getObjectType().ordinal()); + pum.setSourceID(pc.getObjectUUID()); + pum.setTargetType(pc.getObjectType().ordinal()); + pum.setTargetID(pc.getObjectUUID()); + pum.setNumTrains(eff.getTrains()); + pum.setUnknown05(1); + pum.setUnknown06((byte) 1); + pum.setEffectSourceType(i.getObjectType().ordinal()); + pum.setEffectSourceID(i.getObjectUUID()); + pum.setDuration(-1); + + DispatchMessage.dispatchMsgToInterestArea(pc, pum, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false);; + } + } + + //call this if the transfer fails server side to kick the item back to inventory from equip + private static void forceTransferFromEquipToInventory(TransferItemFromInventoryToEquipMsg msg, ClientConnection origin, String reason) { + + PlayerCharacter pc = origin.getPlayerCharacter(); + + if (pc == null) + return; + + TransferItemFromEquipToInventoryMsg back = new TransferItemFromEquipToInventoryMsg(pc, msg.getSlotNumber()); + Dispatch dispatch = Dispatch.borrow(pc, back); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + ChatManager.chatInfoError(pc, "Can't equip item: " + reason); + } + + public static Boolean NPCVaultBankRangeCheck(PlayerCharacter pc, ClientConnection origin, String bankorvault) { + + if (pc == null) + return false; + + NPC npc = pc.getLastNPCDialog(); + + if (npc == null) + return false; + + // System.out.println(npc.getContract().getName()); + // last npc must be either a banker or vault keeper + + if (bankorvault.equals("vault")) { + if (npc.getContract().getContractID() != 861) + return false; + } + else + // assuming banker + + if (!npc.getContract().getName().equals("Bursar")) + return false; + + if (pc.getLoc().distanceSquared2D(npc.getLoc()) > MBServerStatics.NPC_TALK_RANGE * MBServerStatics.NPC_TALK_RANGE) { + ErrorPopupMsg.sendErrorPopup(pc, 14); + return false; + } else + return true; + + } + + private static void transferItemFromInventoryToBank(TransferItemFromInventoryToBankMsg msg, ClientConnection origin) { + + PlayerCharacter player = origin.getPlayerCharacter(); + Dispatch dispatch; + + if (player == null) + return; + + if (!NPCVaultBankRangeCheck(player, origin, "bank")) + return; + + CharacterItemManager itemManager = player.getCharItemManager(); + + if (itemManager == null) + return; + + if (itemManager.getBankWeight() > 500) { + ErrorPopupMsg.sendErrorPopup(player, 21); + return; + } + + int uuid = msg.getUUID(); + + Item item = itemManager.getItemByUUID(uuid); + + if (item == null) + return; + + //dupe check WTF CHECK BUT NO LOGGING? + + if (!item.validForInventory(origin, player, itemManager)) + return; + + if (item.containerType == ItemContainerType.INVENTORY && itemManager.isBankOpen()) + if (item.getItemBase().getType().equals(engine.Enum.ItemType.GOLD)) { + if (!itemManager.moveGoldToBank(item, msg.getNumItems())) + return; + UpdateGoldMsg goldMes = new UpdateGoldMsg(player); + goldMes.configure(); + + dispatch = Dispatch.borrow(player, goldMes); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + } + else { + + if (!itemManager.hasRoomBank(item.getItemBase().getWeight())) + return; + + if (!itemManager.moveItemToBank(item)) + return; + + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + } + } + + private static void transferItemFromBankToInventory(TransferItemFromBankToInventoryMsg msg, ClientConnection origin) { + + PlayerCharacter player = origin.getPlayerCharacter(); + Dispatch dispatch; + + if (player == null) + return; + + if (!NPCVaultBankRangeCheck(player, origin, "bank")) + return; + + CharacterItemManager itemManager = player.getCharItemManager(); + + if (itemManager == null) + return; + + int uuid = msg.getUUID(); + + Item item = itemManager.getItemByUUID(uuid); + + if (item == null) + return; + + //dupe check + // WTF Checking but not logging? + + if (!item.validForBank(origin, player, itemManager)) + return; + + if (item.containerType == ItemContainerType.BANK && itemManager.isBankOpen() == false) + return; + + if (item.getItemBase().getType().equals(engine.Enum.ItemType.GOLD)) { + + if (!itemManager.moveGoldToInventory(item, msg.getNumItems())) + return; + + UpdateGoldMsg goldMes = new UpdateGoldMsg(player); + goldMes.configure(); + + dispatch = Dispatch.borrow(player, goldMes); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + return; + } + + // Not gold, process update here + + if (!itemManager.hasRoomInventory(item.getItemBase().getWeight())) + return; + + if (itemManager.moveItemToInventory(item) == false) + return; + + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + } + + private static void transferItemFromVaultToInventory(TransferItemFromVaultToInventoryMsg msg, ClientConnection origin) { + + PlayerCharacter player = origin.getPlayerCharacter(); + Dispatch dispatch; + + if (player == null) + return; + + if (player.getAccount() == null) + return; + player.getAccount().transferItemFromVaultToInventory(msg, origin); + + + } + + //call this if the transfer fails server side to kick the item back to inventory from vault + public static void forceTransferFromInventoryToVault(TransferItemFromVaultToInventoryMsg msg, ClientConnection origin, String reason) { + + PlayerCharacter player = origin.getPlayerCharacter(); + Dispatch dispatch; + + if (player == null) + return; + + TransferItemFromInventoryToVaultMsg back = new TransferItemFromInventoryToVaultMsg(msg); + dispatch = Dispatch.borrow(player, back); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + ChatManager.chatInfoError(player, "Can't transfer to inventory: " + reason); + } + + private static void transferItemFromInventoryToVault(TransferItemFromInventoryToVaultMsg msg, ClientConnection origin) { + + PlayerCharacter player = origin.getPlayerCharacter(); + Dispatch dispatch; + + if (player == null) + return; + + if (player.getAccount() == null) + return; + player.getAccount().transferItemFromInventoryToVault(msg, origin); + + } + + //call this if the transfer fails server side to kick the item back to vault from inventory + public static void forceTransferFromVaultToInventory(TransferItemFromInventoryToVaultMsg msg, ClientConnection origin, String reason) { + + PlayerCharacter player = origin.getPlayerCharacter(); + Dispatch dispatch; + + if (player == null) + return; + + TransferItemFromVaultToInventoryMsg back = new TransferItemFromVaultToInventoryMsg(msg); + dispatch = Dispatch.borrow(player, back); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + ChatManager.chatInfoError(player, "Can't transfer to vault: " + reason); + } + + private static void transferGoldFromVaultToInventory(TransferGoldFromVaultToInventoryMsg msg, ClientConnection origin) { + + PlayerCharacter player = origin.getPlayerCharacter(); + + + if (player == null) + return; + + Account account = player.getAccount(); + + if (account == null) + return; + + account.transferGoldFromVaultToInventory(msg, origin); + } + + private static void transferGoldFromInventoryToVault(TransferGoldFromInventoryToVaultMsg msg, ClientConnection origin) { + + PlayerCharacter player = origin.getPlayerCharacter(); + Dispatch dispatch; + + if (player == null) + return; + + Account account = player.getAccount(); + + if (account == null) + return; + + account.transferGoldFromInventoryToVault(msg, origin); + + } + + private static void DeleteItem(DeleteItemMsg msg, ClientConnection origin) { + + CharacterItemManager itemManager = origin.getPlayerCharacter().getCharItemManager(); + int uuid = msg.getUUID(); + + + PlayerCharacter sourcePlayer = origin.getPlayerCharacter(); + + if (sourcePlayer == null) + return; + + if (!sourcePlayer.isAlive()) + return; + + Item i = Item.getFromCache(msg.getUUID()); + + if (i == null) + return; + + if (!itemManager.doesCharOwnThisItem(i.getObjectUUID())) + return; + + if (!itemManager.inventoryContains(i)) + return; + + if (i.isCanDestroy()) + if (itemManager.delete(i) == true) { + Dispatch dispatch = Dispatch.borrow(sourcePlayer, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + } + + } + + private static void ackBankWindowOpened(AckBankWindowOpenedMsg msg, ClientConnection origin) { + // According to the Wiki, the client should not send this message. + // Log the instance to investigate, and modify Wiki accordingly. + Logger.error( msg.toString()); + } + + private static void modifyStat(ModifyStatMsg msg, ClientConnection origin) { + + PlayerCharacter pc = SessionManager.getPlayerCharacter(origin); + + if (pc == null) + return; + + int type = msg.getType(); + + switch (type) { + case MBServerStatics.STAT_STR_ID: + pc.addStr(msg.getAmount()); + break; + case MBServerStatics.STAT_DEX_ID: + pc.addDex(msg.getAmount()); + break; + case MBServerStatics.STAT_CON_ID: + pc.addCon(msg.getAmount()); + break; + case MBServerStatics.STAT_INT_ID: + pc.addInt(msg.getAmount()); + break; + case MBServerStatics.STAT_SPI_ID: + pc.addSpi(msg.getAmount()); + break; + } + } + + + + // called when player clicks respawn button + private static void respawn(RespawnMsg msg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter sourcePlayer = SessionManager.getPlayerCharacter(origin); + + if (sourcePlayer == null) + return; + + if (msg.getObjectType() != sourcePlayer.getObjectType().ordinal() || msg.getObjectID() != sourcePlayer.getObjectUUID()) { + Logger.error( "Player " + sourcePlayer.getObjectUUID() + " respawning character of id " + msg.getObjectType() + ' ' + + msg.getObjectID()); + return; + } + + if (sourcePlayer.isAlive()) { + Logger.error( "Player " + sourcePlayer.getObjectUUID() + " respawning while alive"); + return; + } + // ResetAfterDeath player + sourcePlayer.respawnLock.writeLock().lock(); + try{ + sourcePlayer.respawn(true, false, true); + + }catch(Exception e){ + Logger.error(e); + }finally{ + sourcePlayer.respawnLock.writeLock().unlock(); + + } + // Echo ResetAfterDeath message back + msg.setPlayerHealth(sourcePlayer.getHealth()); + // TODO calculate any experience loss before this point + msg.setPlayerExp(sourcePlayer.getExp() + sourcePlayer.getOverFlowEXP()); + Dispatch dispatch = Dispatch.borrow(sourcePlayer, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + + MoveToPointMsg moveMsg = new MoveToPointMsg(); + moveMsg.setPlayer(sourcePlayer); + moveMsg.setStartCoord(sourcePlayer.getLoc()); + moveMsg.setEndCoord(sourcePlayer.getLoc()); + moveMsg.setInBuilding(-1); + moveMsg.setUnknown01(-1); + + dispatch = Dispatch.borrow(sourcePlayer, moveMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + + MovementManager.sendRWSSMsg(sourcePlayer); + + // refresh the whole group with what just happened + JobScheduler.getInstance().scheduleJob(new RefreshGroupJob(sourcePlayer), MBServerStatics.LOAD_OBJECT_DELAY); + } + + private static void lootWindowRequest(LootWindowRequestMsg msg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter pc = SessionManager.getPlayerCharacter(origin); + + if (pc == null) + return; + + if (!pc.isAlive()) + return; + + if (msg.getSourceType() != pc.getObjectType().ordinal() || msg.getSourceID() != pc.getObjectUUID()) { + Logger.error("Player " + pc.getObjectUUID() + " looting from character of id " + + msg.getSourceType() + ' ' + msg.getSourceID()); + return; + } + + if (pc.getAltitude() > 0) + return; + if (!pc.isAlive()) { + return; + } + + + LootWindowResponseMsg lwrm = null; + GameObjectType targetType = GameObjectType.values()[msg.getTargetType()]; + AbstractCharacter characterTarget = null; + Corpse corpseTarget = null; + + switch (targetType) { + case PlayerCharacter: + + characterTarget = PlayerCharacter.getFromCache(msg.getTargetID()); + if (characterTarget == null) + return; + if (characterTarget.isAlive()) + return; + if (pc.getLoc().distanceSquared2D(characterTarget.getLoc()) > sqr(MBServerStatics.LOOT_RANGE)){ + ErrorPopupMsg.sendErrorMsg(pc, "You are too far away to loot this corpse."); + + Logger.info(pc.getFirstName() + " tried looting at " + pc.getLoc().distance2D(characterTarget.getLoc()) + " distance." ); + return; + } + lwrm = new LootWindowResponseMsg(characterTarget.getObjectType().ordinal(), characterTarget.getObjectUUID(), characterTarget.getInventory(true)); + break; + case NPC: + characterTarget = NPC.getFromCache(msg.getTargetID()); + if (characterTarget == null) + return; + break; + case Mob: + characterTarget = Mob.getFromCache(msg.getTargetID()); + if ((characterTarget == null) || characterTarget.isAlive()) { + return; + } + + if (pc.getLoc().distanceSquared2D(characterTarget.getLoc()) > sqr(MBServerStatics.LOOT_RANGE)){ + ErrorPopupMsg.sendErrorMsg(pc, "You are too far away to loot this corpse."); + + Logger.info(pc.getFirstName() + " tried looting at " + pc.getLoc().distance2D(characterTarget.getLoc()) + " distance." ); + + if (!((Mob)characterTarget).isLootSync()){ + + ((Mob)characterTarget).setLootSync(true); + WorldGrid.updateObject(characterTarget, pc); + } + + + return; + } + + lwrm = new LootWindowResponseMsg(characterTarget.getObjectType().ordinal(), characterTarget.getObjectUUID(), characterTarget.getInventory()); + break; + case Corpse: + corpseTarget = Corpse.getCorpse(msg.getTargetID()); + + if ((corpseTarget == null)) { + return; + } + + if (pc.getLoc().distanceSquared(corpseTarget.getLoc()) > sqr(MBServerStatics.LOOT_RANGE)){ + ErrorPopupMsg.sendErrorMsg(pc, "You are too far away to loot this corpse."); + + Logger.info(pc.getFirstName() + " tried looting at " + pc.getLoc().distance2D(characterTarget.getLoc()) + " distance." ); + return; + } + lwrm = new LootWindowResponseMsg(corpseTarget.getObjectType().ordinal(), msg.getTargetID(), corpseTarget.getInventory()); + break; + } + + if (lwrm == null) + return; + + DispatchMessage.dispatchMsgToInterestArea(pc, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); + Dispatch dispatch = Dispatch.borrow(pc, lwrm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + + } + + private static void loot(LootMsg msg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter player = SessionManager.getPlayerCharacter(origin); + if (player == null) + return; + + if (!player.isAlive()) + return; + + Item item = msg.getItem(); + + if (item == null) + return; + + if (item.lootLock.tryLock()) { + try { + Item itemRet = null; + // get current owner + int targetType = msg.getTargetType(); + int targetID = msg.getTargetID(); + + if (targetType == GameObjectType.PlayerCharacter.ordinal() || targetType == GameObjectType.Mob.ordinal() || targetType == GameObjectType.Corpse.ordinal()) { + } + else { //needed for getting contracts for some reason + targetType = msg.getSourceID2(); + targetID = msg.getUnknown01(); + } + + //can't loot while flying + if (player.getAltitude() > 0) + return; + + AbstractCharacter tar = null; + Corpse corpse = null; + + if (targetType == GameObjectType.PlayerCharacter.ordinal() || targetType == GameObjectType.Mob.ordinal()) { + + if (targetType == GameObjectType.PlayerCharacter.ordinal()) { + tar = PlayerCharacter.getFromCache(targetID); + + if (tar == null) + return; + + if (player.getObjectUUID() != tar.getObjectUUID() && ((PlayerCharacter) tar).isInSafeZone()) + return; + + } + + else if (targetType == GameObjectType.NPC.ordinal()) + tar = NPC.getFromCache(targetID); + else if (targetType == GameObjectType.Mob.ordinal()) + tar = Mob.getFromCache(targetID); + if (tar == null) + return; + + if (tar.equals(player)){ + ErrorPopupMsg.sendErrorMsg(player, "Cannot loot this item."); + return; + } + + + if (player.getLoc().distanceSquared2D(tar.getLoc()) > sqr(MBServerStatics.LOOT_RANGE)){ + ErrorPopupMsg.sendErrorMsg(player, "You are too far away to loot this corpse."); + + Logger.info( player.getFirstName() + " tried looting at " + player.getLoc().distance2D(tar.getLoc()) + " distance." ); + return; + } + + //can't loot from someone who is alive. + if (AbstractWorldObject.IsAbstractCharacter(tar)) { + if (tar.isAlive()) + return; + // Logger.error("WorldServer.loot", "Looting from live player"); + } + + if (!GroupManager.goldSplit(player, item, origin, tar)) { + + if (tar.getCharItemManager() != null) { + + itemRet = tar.getCharItemManager().lootItemFromMe(item, player, origin); + + //Take equipment off mob + if (tar.getObjectType() == GameObjectType.Mob && itemRet != null){ + Mob mobTarget = (Mob)tar; + if (mobTarget.getFidalityID() != 0){ + if (item != null && item.getObjectType() == GameObjectType.MobLoot){ + int fidelityEquipID = ((MobLoot)item).getFidelityEquipID(); + + if (fidelityEquipID != 0){ + for (MobEquipment equip: mobTarget.getEquip().values()){ + if (equip.getObjectUUID() == fidelityEquipID){ + TransferItemFromEquipToInventoryMsg back = new TransferItemFromEquipToInventoryMsg(mobTarget, equip.getSlot()); + + DispatchMessage.dispatchMsgToInterestArea(mobTarget, back, DispatchChannel.SECONDARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); + + LootMsg lootMsg = new LootMsg(0,0,tar.getObjectType().ordinal(), tar.getObjectUUID(), equip); + Dispatch dispatch = Dispatch.borrow(player, lootMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + break; + } + } + } + + + } + } + + + } + } + + } + else { + + } + + } + else if (targetType == GameObjectType.Corpse.ordinal()) { + corpse = Corpse.getCorpse(targetID); + if (corpse == null) + return; + + if (player.getLoc().distanceSquared2D(corpse.getLoc()) > sqr(MBServerStatics.LOOT_RANGE)){ + ErrorPopupMsg.sendErrorMsg(player, "You are too far away to loot this corpse."); + + Logger.info( player.getFirstName() + " tried looting at " + player.getLoc().distance2D(corpse.getLoc()) + " distance." ); + return; + } + + + //can't loot other players in safe zone. + if (corpse.getBelongsToType() == GameObjectType.PlayerCharacter.ordinal()){ + + if (player.getObjectUUID() == corpse.getBelongsToID()) + itemRet = corpse.lootItem(item, player); + else if (!GroupManager.goldSplit(player, item, origin, corpse)) { + itemRet = corpse.lootItem(item, player); + + } + + if (itemRet == null) + return; + + + if (item.getItemBase().getType().equals(engine.Enum.ItemType.GOLD)) { + // this is done to prevent the temporary goldItem item + // (from the mob) from appearing in player's inventory. + // It also updates the goldItem quantity display + UpdateGoldMsg updateTargetGold = null; + + + if (corpse != null) + updateTargetGold = new UpdateGoldMsg(corpse); + + updateTargetGold.configure(); + DispatchMessage.dispatchMsgToInterestArea(corpse, updateTargetGold, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); + + UpdateGoldMsg ugm = new UpdateGoldMsg(player); + ugm.configure(); + Dispatch dispatch = Dispatch.borrow(player, ugm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + // respond back loot message. Try sending to everyone. + + } + else { + + DispatchMessage.dispatchMsgToInterestArea(corpse, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, true); + + + //player.getCharItemManager().updateInventory(); + } + + //TODO send group loot message if player is grouped and visible + Group group = GroupManager.getGroup(player); + + if (group != null && group.getSplitGold() && (item.getItemBase().getType().equals(engine.Enum.ItemType.GOLD) == false)) { + String name = item.getName(); + String text = player.getFirstName() + " has looted " + name + '.'; + ChatManager.chatGroupInfoCanSee(player, text); + } + + return; + } + + + + } + else + return; + + + if (itemRet == null) { + return; + } + + if (item.getItemBase().getType().equals(engine.Enum.ItemType.GOLD)) { + // this is done to prevent the temporary goldItem item + // (from the mob) from appearing in player's inventory. + // It also updates the goldItem quantity display + UpdateGoldMsg updateTargetGold = null; + + if (tar != null) + updateTargetGold = new UpdateGoldMsg(tar); + else if (corpse != null) + updateTargetGold = new UpdateGoldMsg(corpse); + + updateTargetGold.configure(); + DispatchMessage.dispatchMsgToInterestArea(tar, updateTargetGold, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + + UpdateGoldMsg ugm = new UpdateGoldMsg(player); + ugm.configure(); + Dispatch dispatch = Dispatch.borrow(player, ugm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + // respond back loot message. Try sending to everyone. + + } + else { + msg.setSourceType1(0); + msg.setSourceType2(0); + msg.setSourceID1(0); + msg.setSourceID2(0); + Dispatch dispatch = Dispatch.borrow(player, msg); + //DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + DispatchMessage.dispatchMsgToInterestArea(tar, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, true); + LootMsg newItemMsg = new LootMsg(GameObjectType.PlayerCharacter.ordinal(), player.getObjectUUID(),0,0, itemRet); + dispatch = Dispatch.borrow(player, newItemMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + + //player.getCharItemManager().updateInventory(); + } + + //TODO send group loot message if player is grouped and visible + Group group = GroupManager.getGroup(player); + + if (group != null && group.getSplitGold() && (item.getItemBase().getType().equals(engine.Enum.ItemType.GOLD) == false)) { + String name = item.getName(); + String text = player.getFirstName() + " has looted " + name + '.'; + ChatManager.chatGroupInfoCanSee(player, text); + } + } catch (Exception e) { + Logger.info( e.getMessage()); + } finally { + item.lootLock.unlock(); + } + } + + + } + + //returns true if looted item is goldItem and is split. Otherwise returns false + + // called when player types /show + private static void show(ShowMsg msg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter pc = SessionManager.getPlayerCharacter(origin); + + if (pc == null) + return; + + int targetType = msg.getTargetType(); + AbstractCharacter tar = null; + + if (targetType == GameObjectType.PlayerCharacter.ordinal()) + tar = PlayerCharacter.getFromCache(msg.getTargetID()); + else if (targetType == GameObjectType.NPC.ordinal()) + tar = NPC.getFromCache(msg.getTargetID()); + else if (targetType == GameObjectType.Mob.ordinal()) + tar = Mob.getFromCache(msg.getTargetID()); + + if (tar == null || !tar.isAlive() || !tar.isActive()) + return; + + msg.setUnknown01(pc.getLoc()); + msg.setUnknown02(pc.getLoc()); + msg.setRange01(pc.getRange()); + msg.setUnknown03(tar.getLoc()); + msg.setUnknown04(tar.getLoc()); + msg.setRange01(tar.getRange()); + + Dispatch dispatch = Dispatch.borrow(pc, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + } + + private static void ViewResourcesMessage(ViewResourcesMessage msg, ClientConnection origin) throws SQLException { + + PlayerCharacter player = SessionManager.getPlayerCharacter(origin); + + if (player == null) + return; + + Guild guild = player.getGuild(); + City city = guild.getOwnedCity(); + + if (city == null) + return; + + Building warehouse = BuildingManager.getBuilding(city.getWarehouseBuildingID()); + + if (warehouse == null) + return; + + ViewResourcesMessage vrm = new ViewResourcesMessage(player); + vrm.setWarehouseBuilding(warehouse); + vrm.setGuild(player.getGuild()); + vrm.configure(); + + Dispatch dispatch = Dispatch.borrow(player, vrm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + } + + private static void MineWindowAvailableTime(ArcMineWindowAvailableTimeMsg msg, ClientConnection origin) { + Building tol = BuildingManager.getBuildingFromCache(msg.getBuildingUUID()); + Dispatch dispatch; + + if (tol == null) + return; + + if (tol.getBlueprintUUID() == 0) + return; + + if (tol.getBlueprint().getBuildingGroup() != BuildingGroup.TOL) + return; + + PlayerCharacter pc = SessionManager.getPlayerCharacter(origin); + + if (pc == null) + return; + + if (!Guild.sameGuild(tol.getGuild(), pc.getGuild())) + return; //must be same guild + + if (GuildStatusController.isInnerCouncil(pc.getGuildStatus()) == false) // is this only GL? + return; + + ArcMineWindowAvailableTimeMsg amwat = new ArcMineWindowAvailableTimeMsg(tol, 10); + amwat.configure(); + dispatch = Dispatch.borrow(pc, amwat); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + } + + private static void MineWindowChange(ArcMineWindowChangeMsg msg, ClientConnection origin) { + + Building tol = (Building) BuildingManager.getBuildingFromCache(msg.getBuildingID()); + //hodge podge sanity check to make sure they dont set it before early window and is not set at late window. + if (msg.getTime() < MBServerStatics.MINE_EARLY_WINDOW && msg.getTime() != MBServerStatics.MINE_LATE_WINDOW) + return; //invalid mine time, must be in range + if (tol == null) + return; + + if (tol.getBlueprintUUID() == 0) + return; + + if (tol.getBlueprint().getBuildingGroup() != BuildingGroup.TOL) + return; + + PlayerCharacter pc = SessionManager.getPlayerCharacter(origin); + if (pc == null) + return; + + Guild tolG = tol.getGuild(); + if (tolG == null) + return; + if (!Guild.sameGuild(tolG, pc.getGuild())) + return; //must be same guild + if (GuildStatusController.isInnerCouncil(pc.getGuildStatus()) == false) // is this only GL? + return; + + if (!DbManager.GuildQueries.UPDATE_MINETIME(tolG.getObjectUUID(), msg.getTime())) { + Logger.error("MineWindowChange", "Failed to update mine time for guild " + tolG.getObjectUUID()); + ChatManager.chatGuildError(pc, "Failed to update the mine time"); + return; + } + tolG.setMineTime(msg.getTime()); + ChatManager.chatGuildInfo(pc, "Mine time updated."); + } + + private static void ListOwnedMines(ArcOwnedMinesListMsg msg, ClientConnection origin) { + + PlayerCharacter pc = SessionManager.getPlayerCharacter(origin); + + if (pc == null) + return; + //TODO verify this against the warehouse? + + if (GuildStatusController.isInnerCouncil(pc.getGuildStatus()) == false)// is this only GL? + return; + + msg.setMineList(Mine.getMinesForGuild(pc.getGuild().getObjectUUID())); + Dispatch dispatch = Dispatch.borrow(pc, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + } + + private static void changeMineProduction(ArcMineChangeProductionMsg msg, ClientConnection origin) { + + PlayerCharacter sourcePlayer = SessionManager.getPlayerCharacter(origin); + + if (sourcePlayer == null) + return; + + //TODO verify this against the warehouse? + + if (GuildStatusController.isInnerCouncil(sourcePlayer.getGuildStatus()) == false) // is this only GL? + return; + + Mine mine = Mine.getMine(msg.getMineID()); + + if (mine == null) + return; + + //make sure mine belongs to guild + if (mine.getOwningGuild() == null || mine.getOwningGuild().getObjectUUID() != sourcePlayer.getGuild().getObjectUUID()) + return; + + //make sure valid resource + Resource r = Resource.resourceByHash.get(msg.getResourceHash()); + + if (r == null) + return; + + //update resource + mine.changeProductionType(r); + Mine.setLastChange(System.currentTimeMillis()); + Dispatch dispatch = Dispatch.borrow(sourcePlayer, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + } + + private static void randomRoll(RandomMsg msg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter source = origin.getPlayerCharacter(); + + if (source == null || !source.isAlive()) + return; + + //2 second cooldown on random rolls + long lastRandom = source.getTimeStamp("RandomRoll"); + + if (System.currentTimeMillis() - lastRandom < 2000) + return; + source.setTimeStamp("RandomRoll", System.currentTimeMillis()); + + //handle random roll + int max = msg.getMax(); + + if (max > 0) + msg.setRoll(ThreadLocalRandom.current().nextInt(max) + 1); + else if (max < 0) { + max = 1 - max; + msg.setRoll((ThreadLocalRandom.current().nextInt(max) - max) + 1); + } + + msg.setSourceType(source.getObjectType().ordinal()); + msg.setSourceID(source.getObjectUUID()); + + //send to all in range + DispatchMessage.dispatchMsgToInterestArea(source, msg, DispatchChannel.SECONDARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, true); + } + + private static void stuck(ClientConnection origin) { + + PlayerCharacter sourcePlayer = origin.getPlayerCharacter(); + + if (sourcePlayer == null) + return; + + if (sourcePlayer.getTimers().containsKey("Stuck")) + return; + + StuckJob sj = new StuckJob(sourcePlayer); + JobContainer jc = JobScheduler.getInstance().scheduleJob(sj, 10000); // Convert + ConcurrentHashMap timers = sourcePlayer.getTimers(); + + if (timers != null) { + if (timers.containsKey("Stuck")) { + timers.get("Stuck").cancelJob(); + timers.remove("Stuck"); + } + timers.put("Stuck", jc); + } + } + + private static void GuildTreeStatusMsg(GuildTreeStatusMsg msg, ClientConnection origin) throws SQLException { + + PlayerCharacter player = SessionManager.getPlayerCharacter(origin); + Dispatch dispatch; + + if (player == null) + return; + + if (origin.guildtreespam > System.currentTimeMillis()) { + return; + } + origin.guildtreespam = System.currentTimeMillis() + 5000; + + Building b = BuildingManager.getBuildingFromCache(msg.getTargetID()); + if (b == null) + return; + + GuildTreeStatusMsg gtsm = new GuildTreeStatusMsg(b, player); + gtsm.configure(); + + dispatch = Dispatch.borrow(player, gtsm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + } + + + private static void openSellToNPCWindow(SellToNPCWindowMsg msg, ClientConnection origin) { + + PlayerCharacter sourcePlayer = SessionManager.getPlayerCharacter(origin); + Dispatch dispatch; + + if (sourcePlayer == null) + return; + + NPC npc = NPC.getFromCache(msg.getNPCID()); + + if (npc == null) + return; + + // test within talking range + + if (sourcePlayer.getLoc().distanceSquared2D(npc.getLoc()) > MBServerStatics.NPC_TALK_RANGE * MBServerStatics.NPC_TALK_RANGE) { + ErrorPopupMsg.sendErrorPopup(sourcePlayer, 14); + return; + } + + Contract con = npc.getContract(); + + if (con == null) + return; + float bargain = sourcePlayer.getBargain(); + + float profit = npc.getBuyPercent(sourcePlayer) + bargain; + + if (profit > 1) + profit = 1; + + msg.setupOutput(); + + msg.setUnknown05(profit); + msg.setUnknown06(500000); //TODO set goldItem on npc later + msg.setItemType(con.getBuyItemType()); + msg.setSkillTokens(con.getBuySkillToken()); + msg.setUnknownArray(con.getBuyUnknownToken()); + + dispatch = Dispatch.borrow(sourcePlayer, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + } + + private static void sellToNPC(SellToNPCMsg msg, ClientConnection origin) { + + PlayerCharacter player = SessionManager.getPlayerCharacter(origin); + Dispatch dispatch; + + if (player == null) + return; + + CharacterItemManager itemMan = player.getCharItemManager(); + + if (itemMan == null) + return; + + NPC npc = NPC.getFromCache(msg.getNPCID()); + + if (npc == null) + return; + + Item gold = itemMan.getGoldInventory(); + + if (gold == null) + return; + + if (origin.sellLock.tryLock()) { + try { + Item sell; + int cost = 0; + + + if (npc.getCharItemManager().getInventoryCount() > 150) { + if (npc.getParentZone() != null && npc.getParentZone().getPlayerCityUUID() == 0) { + ArrayList inv = npc.getInventory(); + for (int i = 0; i < 20; i++) { + try { + Item toRemove = inv.get(i); + if (toRemove != null) + npc.getCharItemManager().delete(toRemove); + } catch (Exception e) { + break; + } + + } + } + + } + + // Early exit sanity check + + if (msg.getItemType() == GameObjectType.Item.ordinal() == false) + return; + + sell = Item.getFromCache(msg.getItemID()); + + if (sell == null) + return; + + //get item to sell + + ItemBase ib = sell.getItemBase(); + + if (ib == null) + return; + + if (npc.getParentZone() != null && npc.getParentZone().getPlayerCityUUID() != 0) + if (!npc.getCharItemManager().hasRoomInventory(ib.getWeight())){ + + ErrorPopupMsg.sendErrorPopup(player, 21); + return; + } + + if (!sell.validForInventory(origin, player, itemMan)) + return; + + //get goldItem cost to sell + + + cost = sell.getBaseValue(); + + if (sell.isID()) + cost = sell.getMagicValue(); + + float bargain = player.getBargain(); + + float profit = npc.getBuyPercent(player) + bargain; + + if (profit > 1) + profit = 1; + + + + cost *= profit; + + if (gold.getNumOfItems() + cost > 10000000) { + return; + } + + if (gold.getNumOfItems() + cost < 0) + return; + + //TODO make sure npc can buy item type + //test room available for item on npc + + // if (!npc.isStatic() && npc.getCharItemManager().getInventoryCount() > 150) { + // // chatMan.chatSystemInfo(pc, "This vendor's inventory is full"); + // return; + // } + + //make sure item is in player inventory + + Building building = (!npc.isStatic()) ? npc.getBuilding() : null; + + if (building != null && building.getProtectionState().equals(ProtectionState.NPC)) + building = null; + if (npc.getParentZone().getPlayerCityUUID() == 0) + building = null; + + //make sure npc can afford item + + if (building != null && !building.hasFunds(cost)){ + ErrorPopupMsg.sendErrorPopup(player, 17); + return; + } + if (building != null && (building.getStrongboxValue() - cost) < 0){ + ErrorPopupMsg.sendErrorPopup(player, 17); + return; + } + + //TODO transfer item and goldItem transfer should be handled together incase failure + //transfer the item + + if (!itemMan.sellToNPC(sell, npc)) + return; + + if (!itemMan.sellToNPC(building, cost, sell)) + return; + + //handle goldItem transfer + + if (sell == null) + return; + + // ***REFACTOR: SellToNpc sends this message, is this a duplicate? + + //update player's goldItem count + UpdateGoldMsg ugm = new UpdateGoldMsg(player); + ugm.configure(); + + dispatch = Dispatch.borrow(player, ugm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + //send the sell message back to update player + msg.setItemType(sell.getObjectType().ordinal()); + msg.setItemID(sell.getObjectUUID()); + msg.setUnknown01(cost); //not sure if this is correct + + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + } finally { + origin.sellLock.unlock(); + } + } + else { + ErrorPopupMsg.sendErrorPopup(player, 12); + } + } + + private static void openBuyFromNPCWindow(BuyFromNPCWindowMsg msg, ClientConnection origin) { + + PlayerCharacter sourcePlayer = SessionManager.getPlayerCharacter(origin); + Dispatch dispatch; + + if (sourcePlayer == null) + return; + + NPC npc = NPC.getFromCache(msg.getNpcID()); + + if (npc == null) + return; + + // test within talking range + + if (sourcePlayer.getLoc().distanceSquared2D(npc.getLoc()) > MBServerStatics.NPC_TALK_RANGE * MBServerStatics.NPC_TALK_RANGE) { + ErrorPopupMsg.sendErrorPopup(sourcePlayer, 14); + return; + } + + dispatch = Dispatch.borrow(sourcePlayer, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + } + + private static void buyFromNPC(BuyFromNPCMsg msg, ClientConnection origin) { + + PlayerCharacter sourcePlayer = SessionManager.getPlayerCharacter(origin); + + if (sourcePlayer == null) + return; + + if (origin.buyLock.tryLock()) { + + try { + CharacterItemManager itemMan = sourcePlayer.getCharItemManager(); + + if (itemMan == null) + return; + + NPC npc = NPC.getFromCache(msg.getNPCID()); + + if (npc == null) + return; + + Item gold = itemMan.getGoldInventory(); + + if (gold == null) + return; + + Item buy = null; + + if (msg.getItemType() == GameObjectType.MobEquipment.ordinal()) { + ArrayList sellInventory = npc.getContract().getSellInventory(); + if (sellInventory == null) + return; + for (MobEquipment me : sellInventory) { + if (me.getObjectUUID() == msg.getItemID()) { + ItemBase ib = me.getItemBase(); + if (ib == null) + return; + + //test room available for item + if (!itemMan.hasRoomInventory(ib.getWeight())) + return; + + int cost = me.getMagicValue(); + + float bargain = sourcePlayer.getBargain(); + + float profit = npc.getSellPercent(sourcePlayer) - bargain; + + if (profit < 1) + profit = 1; + + + cost *= profit; + + + + + + + if (gold.getNumOfItems() - cost < 0) { + //dont' have enough goldItem exit! + // chatMan.chatSystemInfo(pc, "" + "You dont have enough gold."); + return; + } + + Building b = (!npc.isStatic()) ? npc.getBuilding() : null; + + if (b != null && b.getProtectionState().equals(ProtectionState.NPC)) + b = null; + int buildingDeposit = cost - me.getMagicValue(); + if (b != null && (b.getStrongboxValue() + buildingDeposit) > b.getMaxGold()) { + ErrorPopupMsg.sendErrorPopup(sourcePlayer, 206); + return; + } + + if (!itemMan.buyFromNPC(b, cost, buildingDeposit)) { + // chatMan.chatSystemInfo(pc, "" + "You Failed to buy the item."); + return; + } + + buy = Item.createItemForPlayer(sourcePlayer, ib); + + if (buy != null) { + me.transferEnchants(buy); + itemMan.addItemToInventory(buy); + //itemMan.updateInventory(); + } + } + } + } + else if (msg.getItemType() == GameObjectType.Item.ordinal()) { + + CharacterItemManager npcCim = npc.getCharItemManager(); + + if (npcCim == null) + return; + + buy = Item.getFromCache(msg.getItemID()); + + if (buy == null) + return; + + ItemBase ib = buy.getItemBase(); + + if (ib == null) + return; + + if (!npcCim.inventoryContains(buy)) + return; + + //test room available for item + if (!itemMan.hasRoomInventory(ib.getWeight())) + return; + + //TODO test cost and subtract goldItem + + //TODO CHnage this if we ever put NPc city npcs in buildings. + int cost = buy.getBaseValue(); + + if (buy.isID() || buy.isCustomValue()) + cost = buy.getMagicValue(); + + float bargain = sourcePlayer.getBargain(); + + float profit = npc.getSellPercent(sourcePlayer) - bargain; + + if (profit < 1) + profit = 1; + + if (!buy.isCustomValue()) + cost *= profit; + else + cost = buy.getValue(); + + + + if (gold.getNumOfItems() - cost < 0) { + ErrorPopupMsg.sendErrorPopup(sourcePlayer, 128); // Insufficient Gold + return; + } + + Building b = (!npc.isStatic()) ? npc.getBuilding() : null; + + if (b != null) + if (b.getProtectionState().equals(ProtectionState.NPC)) + b = null; + + int buildingDeposit = cost; + + if (b != null && (b.getStrongboxValue() + buildingDeposit) > b.getMaxGold()) { + ErrorPopupMsg.sendErrorPopup(sourcePlayer, 206); + return; + } + + if (!itemMan.buyFromNPC(b, cost, buildingDeposit)) { + ErrorPopupMsg.sendErrorPopup(sourcePlayer, 110); + return; + } + + if (buy != null) + itemMan.buyFromNPC(buy, npc); + + }else if (msg.getItemType() == GameObjectType.MobLoot.ordinal()) { + + CharacterItemManager npcCim = npc.getCharItemManager(); + + if (npcCim == null) + return; + + buy = MobLoot.getFromCache(msg.getItemID()); + + if (buy == null) + return; + + ItemBase ib = buy.getItemBase(); + + if (ib == null) + return; + + if (!npcCim.inventoryContains(buy)) + return; + + //test room available for item + if (!itemMan.hasRoomInventory(ib.getWeight())) + return; + + //TODO test cost and subtract goldItem + + //TODO CHnage this if we ever put NPc city npcs in buildings. + + int cost = buy.getMagicValue(); + cost *= npc.getSellPercent(sourcePlayer); + + + if (gold.getNumOfItems() - cost < 0) { + ErrorPopupMsg.sendErrorPopup(sourcePlayer, 128); // Insufficient Gold + return; + } + + Building b = (!npc.isStatic()) ? npc.getBuilding() : null; + + if (b != null && b.getProtectionState().equals(ProtectionState.NPC)) + b = null; + int buildingDeposit = cost; + + if (b != null && (b.getStrongboxValue() + buildingDeposit) > b.getMaxGold()) { + ErrorPopupMsg.sendErrorPopup(sourcePlayer, 206); + return; + } + + if (!itemMan.buyFromNPC(b, cost, buildingDeposit)) + return; + + if (buy != null) + itemMan.buyFromNPC(buy, npc); + + } + else + return; + + if (buy != null) { + + msg.setItem(buy); + //send the buy message back to update player + // msg.setItemType(buy.getObjectType().ordinal()); + // msg.setItemID(buy.getObjectUUID()); + Dispatch dispatch = Dispatch.borrow(sourcePlayer, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + itemMan.updateInventory(); + } + + } finally { + origin.buyLock.unlock(); + } + } + else { + ErrorPopupMsg.sendErrorPopup(origin.getPlayerCharacter(), 12); // All production slots taken + } + + } + + //Handle RepairObject Window and RepairObject Requests + + private static void Repair(RepairMsg msg, ClientConnection origin) { + + PlayerCharacter player = SessionManager.getPlayerCharacter(origin); + Dispatch dispatch; + + if (player == null) + return; + + NPC npc = NPC.getFromCache(msg.getNPCID()); + + if (npc == null) + return; + + if (msg.getMsgType() == 1) { //Open RepairObject Window + + if (player.getLoc().distanceSquared2D(npc.getLoc()) > MBServerStatics.NPC_TALK_RANGE * MBServerStatics.NPC_TALK_RANGE) { + ErrorPopupMsg.sendErrorPopup(player, 14); + return; + } + + //send open repair window response + msg.setRepairWindowAck(npc); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + } + else if (msg.getMsgType() == 0) { //Request RepairObject + + CharacterItemManager itemMan = player.getCharItemManager(); + + if (itemMan == null) + return; + + Item gold = itemMan.getGoldInventory(); + + if (gold == null) + return; + + Item toRepair = Item.getFromCache(msg.getItemID()); + + if (toRepair == null) + return; + + if (toRepair.getItemBase().isGlass()) + return; + + //make sure item is in player's inventory or equipment + if (!itemMan.inventoryContains(toRepair) && !itemMan.equippedContains(toRepair)) + return; + + //make sure item is damaged and not destroyed + short dur = toRepair.getDurabilityCurrent(); + short max = toRepair.getDurabilityMax(); + + if (dur >= max || dur < 1) + return; + + //TODO get cost to repair + int cost = (int) ((max - dur) * 80.1); + Building b = (!npc.isStatic()) ? npc.getBuilding() : null; + + if (b != null) + if (b.getProtectionState().equals(ProtectionState.NPC)) + b = null; + + + if (b != null && (b.getStrongboxValue() + cost) > b.getMaxGold()) { + ErrorPopupMsg.sendErrorPopup(player, 206); + return; + } + + if (player.getCharItemManager().getGoldInventory().getNumOfItems() - cost < 0) + return; + + if (player.getCharItemManager().getGoldInventory().getNumOfItems() - cost > MBServerStatics.PLAYER_GOLD_LIMIT) + return; + + if (!itemMan.buyFromNPC(b, cost, cost)) { + ErrorPopupMsg.sendErrorPopup(player, 128); + return; + } + + //update player's goldItem count + UpdateGoldMsg ugm = new UpdateGoldMsg(player); + ugm.configure(); + dispatch = Dispatch.borrow(player, ugm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + //update durability to database + if (!DbManager.ItemQueries.SET_DURABILITY(toRepair, max)) + return; + + //repair the item + toRepair.setDurabilityCurrent(max); + + //send repair msg + msg.setupRepairAck(max - dur); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + } + } + + protected static void petAttack(PetAttackMsg msg, ClientConnection conn) throws MsgSendException { + + PlayerCharacter pc = SessionManager.getPlayerCharacter(conn); + + if (pc == null) + return; + + Mob pet = pc.getPet(); + + if (pet == null) + return; + + if (!pet.isAlive()) + return; + + if ((pc.inSafeZone()) + && (msg.getTargetType() == GameObjectType.PlayerCharacter.ordinal())) + return; + + CombatManager.setAttackTarget(msg, conn); + + if (pet.getCombatTarget() == null) + return; + pet.setState(STATE.Attack); + } + + protected static void petCmd(PetCmdMsg msg, ClientConnection conn) throws MsgSendException { + + PlayerCharacter pc = SessionManager.getPlayerCharacter(conn); + + if (pc == null) + return; + + Mob pet = pc.getPet(); + + if (pet == null) + return; + + if (!pet.isAlive()) + return; + + if (pet.getState() == STATE.Disabled) + return; + + int type = msg.getType(); + + if (type == 1) { //stop attack + pet.setCombatTarget(null); + pc.setCombat(false); + pet.setState(STATE.Awake); + + } + else if (type == 2) { //dismiss + pet.dismiss(); + pc.dismissPet(); + + if (pet.isAlive()) + WorldGrid.updateObject(pet); + } + else if (type == 3) //toggle assist + pet.toggleAssist(); + else if (type == 5) { //rest + boolean sit = (!(pet.isSit())); + pet.setSit(sit); + + // cancel effects that break on sit + if (pet.isSit()) + pet.cancelOnSit(); + + UpdateStateMsg rwss = new UpdateStateMsg(); + rwss.setPlayer(pet); + DispatchMessage.sendToAllInRange(pet, rwss); + } + } + + protected static void HandlePromptRecall(PromptRecallMsg msg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter player = SessionManager.getPlayerCharacter(origin); + boolean recallAccepted; + + if (player == null) + return; + + boolean confirmed = msg.getConfirmed(); + + if (confirmed == true) { + long timeElapsed = System.currentTimeMillis() - player.getTimeStamp("PromptRecall"); + //send fail message + recallAccepted = timeElapsed < 15000; + } + else + recallAccepted = false; + + if (recallAccepted == true) { + //handle recall + long type = player.getTimeStamp("LastRecallType"); + + if (type == 1) { //recall to bind + player.teleport(player.getBindLoc()); + player.setSafeMode(); + } + else { //recall to rg + float dist = 9999999999f; + Building rg = null; + Vector3fImmutable rgLoc; + + for (Runegate runegate : Runegate.getRunegates()) { + + if ((runegate.getGateType() == RunegateType.OBLIV) || + (runegate.getGateType() == RunegateType.CHAOS)) + continue; + + for (Runegate thisGate : Runegate.getRunegates()) { + + rgLoc = thisGate.getGateType().getGateBuilding().getLoc(); + + float distanceSquaredToRunegate = player.getLoc().distanceSquared2D(rgLoc); + + if (distanceSquaredToRunegate < sqr(dist)) + rg = thisGate.getGateType().getGateBuilding(); + + } + } + //nearest runegate found. teleport characterTarget + + if (rg != null) { + player.teleport(rg.getLoc()); + player.setSafeMode(); + } + } + } + } + +} diff --git a/src/engine/net/client/Protocol.java b/src/engine/net/client/Protocol.java new file mode 100644 index 00000000..d95becd3 --- /dev/null +++ b/src/engine/net/client/Protocol.java @@ -0,0 +1,338 @@ +package engine.net.client; + +/* This class defines Magicbane's application network protocol. +--> Name / Opcode / Message / Handler + */ + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.client.handlers.*; +import engine.net.client.msg.*; +import engine.net.client.msg.chat.*; +import engine.net.client.msg.commands.ClientAdminCommandMsg; +import engine.net.client.msg.group.*; +import engine.net.client.msg.guild.*; +import engine.net.client.msg.login.*; +import org.pmw.tinylog.Logger; + +import java.lang.reflect.Constructor; +import java.util.HashMap; + +public enum Protocol { + + + NONE(0x0, null, null), + ABANDONASSET(0xFDDBB233, AbandonAssetMsg.class, AbandonAssetMsgHandler.class), // AbandonAsset + ACTIVATECHARTER(0x296C0B22, UseCharterMsg.class, null),// Use Guild Charter + ACTIVATENPC(0xC9AAE81E, ActivateNPCMessage.class, ActivateNPCMsgHandler.class), + ACTIVATEPLEDGE(0x5A694DC0, SwearInMsg.class, SwearInHandler.class), // Swear In + ADDFRIEND(0xCFA1C787,AddFriendMessage.class,null), + ALLIANCECHANGE(0x0E7D0B57, AllianceChangeMsg.class, AllianceChangeMsgHandler.class), // Remove From Allies/Enemies List + ALLYENEMYLIST(0xAEA443FD, AllyEnemyListMsg.class, AllyEnemyListMsgHandler.class), + ARCCOMBATMODEATTACKING(0xD8B10579, SetCombatModeMsg.class, null), // Attack From Outside Combat Mode + ARCHOTZONECHANGE(0xDCFF196F, null, null), //change hotzone + ARCIGNORELISTUPDATE(0x4B1B17C2, IgnoreListMsg.class, null), //req/show ignore list + ARCLOGINNOTIFY(0x010FED87, ArcLoginNotifyMsg.class, ArcLoginNotifyMsgHandler.class), //Client Confirms entering world + ARCMINECHANGEPRODUCTION(0x1EAA993F, ArcMineChangeProductionMsg.class, null), + ARCMINETOWERCRESTUPDATE(0x34164D0D, null, null), + ARCMINEWINDOWAVAILABLETIME(0x6C909DE7, ArcMineWindowAvailableTimeMsg.class, null), + ARCMINEWINDOWCHANGE(0x92B2148A, ArcMineWindowChangeMsg.class, null), + ARCOWNEDMINESLIST(0x59184455, ArcOwnedMinesListMsg.class, null), + ARCPETATTACK(0x18CD61AD, PetAttackMsg.class, null), // Pet Attack + ARCPETCMD(0x4E80E001, PetCmdMsg.class, null), // Stop ArcPetAttack, Toggle Assist, Toggle Rest + ARCPOWERPROJECTILE(0xA2312D3B, null, null), + ARCPROMPTRECALL(0xE3196B6E, PromptRecallMsg.class, null), //Recall Prompt + ARCREQUESTTRADEBUSY(0xD4BAB4DF, InvalidTradeRequestMsg.class, null), // Attempt trade with someone who is already trading + ARCSERVERSTATUS(0x87BA4462, null, null), //Update Server Status + ARCSIEGESPIRE(0x36A49BC6, ArcSiegeSpireMsg.class, ArcSiegeSpireMsgHandler.class), // Activate/Deactivate Spires + ARCSUMMON(0xFD816A0A, RecvSummonsRequestMsg.class, null), // Suspect Recv Summons Request + ARCTRACKINGLIST(0xC89CF08B, TrackWindowMsg.class, null), //Request/Send Track window + ARCTRACKOBJECT(0x609B6BA2, TrackArrowMsg.class, null), //Send Track Arrow + ARCUNTRAINABILITY(0x548DBF83, RefineMsg.class, null), //Refine + ARCUNTRAINLIST(0x38879E90, RefinerScreenMsg.class, null), //Refiner screen + ARCVIEWASSETTRANSACTIONS(0xBFA476E4, ArcViewAssetTransactionsMsg.class, ArcViewAssetTransactionsMsgHandler.class), + ASSETSUPPORT(0xc481f89D, AssetSupportMsg.class, AssetSupportMsgHandler.class), + BANISHMEMBER(0x31AA3368, BanishUnbanishMsg.class, BanishUnbanishHandler.class), // Banish/Unbanish + BANKINVENTORY(0x32F3F503, ShowBankInventoryMsg.class, null), // ShowCombatInfo Bank Inventory + BREAKFEALTY(0x479A4C19, BreakFealtyMsg.class, BreakFealtyHandler.class), + BUYFROMNPC(0xA2B8DFA5, BuyFromNPCMsg.class, null), // Buy Item From NPC + CANCELGUILDCREATION(0x385EA922, GuildCreationCloseMsg.class, GuildCreationCloseHandler.class), //Close the window + CHANGEALTITUDE(0x624F08BA, ChangeAltitudeMsg.class, ChangeAltitudeHandler.class), //Change Altitude + CHANGEGUILDLEADER(0xE40BC95D, ChangeGuildLeaderMsg.class, ChangeGuildLeaderHandler.class), + CHANNELMUTE(0xC1BDC53A, ChatFilterMsg.class, ChannelMuteMsgHandler.class), //Chat Channels that are turned on + CHARSELECTSCREEN(0x682C935D, null, null), // Character Selection Screen + CHATCITY(0x9D402901, ChatCityMsg.class, null), // Chat Channel: /City + CHATCSR(0x14EBA1C3, ChatCSRMsg.class, null), //Chat Channel: CSR + CHATGROUP(0xA895B634, ChatGroupMsg.class, null), // Chat Channel: /group + CHATGUILD(0xA9D92ED4, ChatGuildMsg.class, null), // Chat Channel: /guild + CHATIC(0x00A75F35, ChatICMsg.class, null), // Chat Channel: /IC + CHATINFO(0x9D4B61EB, ChatInfoMsg.class, null), // Chat Channel: /Info + CHATPVP(0x14EBA570, ChatPvPMsg.class, null), // Chat Channel: PVP + CHATSAY(0x14EA0393, ChatSayMsg.class, null), // Chat Channel: /say + CHATSHOUT(0xA8D5B560, ChatShoutMsg.class, null), // Chat Channel: /shout + CHATTELL(0x9D4AC896, ChatTellMsg.class, null), // Chat Channel: /tell + CHECKUNIQUEGUILD(0x689097D7, GuildCreationOptionsMsg.class, GuildCreationOptionsHandler.class), // Set Guild Name/Motto in Use Guild Charter + CITYASSET(0x7cae1678, CityAssetMsg.class, null), + CITYCHOICE(0x406610BB, CityChoiceMsg.class, CityChoiceMsgHandler.class), + CITYDATA(0xB8A947D4, WorldObjectMsg.class, null), //Realm Data - Optional(?) + CITYZONE(0x254947F2, CityZoneMsg.class, null), //For Creating City Object Clientside(Terraform)/Rename City. + CLAIMASSET(0x948C62CC, ClaimAssetMsg.class, ClaimAssetMsgHandler.class), // ClaimAsset + CLAIMGUILDTREE(0xFD1C6442, ClaimGuildTreeMsg.class, ClaimGuildTreeMsgHandler.class), + CLIENTADMINCOMMAND(0x624EAB5F, ClientAdminCommandMsg.class, null), //Admin Command + CLIENTUPDATEVAULT( 0x66EDBECD, UpdateVaultMsg.class, null), + COMBATMODE(0xFE4BF353, ToggleCombatMsg.class, null), //Toggle Combat mode + CONFIRMPROMOTE(0x153BB5F9, ConfirmPromoteMsg.class, null), + COSTTOOPENBANK(0x135BE5E8, AckBankWindowOpenedMsg.class, null), // ACK Bank Window Opened + CREATECHAR(0x5D18B5C8, CommitNewCharacterMsg.class, null), // Commit New Character, + CREATEPETITION(0xD489CFED, GuildCreationFinalizeMsg.class, GuildCreationFinalizeHandler.class), //Confirm guild creation + CUSTOMERPETITION(0x7F9D7D6D, PetitionReceivedMsg.class, null), + DELETEOBJECT(0x57F069D8, DeleteItemMsg.class, null), //Delete Item from Inventory + DESTROYBUILDING(0x3CB6FAD3, DestroyBuildingMsg.class, DestroyBuildingHandler.class), // Destroy Building + DISBANDGUILD(0x77AABD64, DisbandGuildMsg.class, DisbandGuildHandler.class), //Disband Guild + DISMISSGUILD(0x8D2D3D61, DismissGuildMsg.class, DismissGuildHandler.class), + DOORTRYOPEN(0xA83DD8C8, DoorTryOpenMsg.class, DoorTryOpenMsgHandler.class), // Open/Close Door + ENTERWORLD(0xB9783F85, RequestEnterWorldMsg.class, RequestEnterWorldHandler.class), // Request Enter World + EQUIP(0x3CB1AF8C, TransferItemFromInventoryToEquipMsg.class, null), // Transfer Item from Inventory to Equip + EXPERIENCE(0xC57802A7, GrantExperienceMsg.class, null), //TODO rename once identified + FORGETOBJECTS(0xE307A0E1, UnloadObjectsMsg.class, null), // Unload Objects + FRIENDACCEPT(0xCA297870,AcceptFriendMsg.class,FriendAcceptHandler.class), + FRIENDDECLINE(0xF08FC279,DeclineFriendMsg.class,FriendDeclineHandler.class), + FURNITURE(0xCE7FA503, FurnitureMsg.class, FurnitureHandler.class), + GAMESERVERIPRESPONSE(0x6C95CF87, GameServerIPResponseMsg.class, null), // Game Server IP Response + GLOBALCHANNELMESSAGE(0x2bf03fd2, null, null), + GOLDTOVAULT(0x3ABAEE49, TransferGoldFromInventoryToVaultMsg.class, null), // Transfer Gold from Inventory to Vault + GROUPDISBAND(0xE2B85AA4, DisbandGroupMsg.class, DisbandGroupHandler.class), //Disband Group + GROUPFOLLOW(0xC61B0476, FormationFollowMsg.class, FormationFollowHandler.class), //Toggle Follow, set Formation + GROUPLEADERAPPOINT(0xEF778DD3, AppointGroupLeaderMsg.class, AppointGroupLeaderHandler.class), //Appoint new group leader + GROUPREMOVE(0x6E50277C, RemoveFromGroupMsg.class, RemoveFromGroupHandler.class), //Remove from Group + GROUPTREASURE(0x01041C66, ToggleGroupSplitMsg.class, ToggleGroupSplitHandler.class), // Toggle Group Split + GUILDMEMBERONLINE(0x7B79EB3A, GuildEnterWorldMsg.class, null), // Send Enter World Message to Guild + GUILDRANKCHANGE(0x0DEFB21F, ChangeRankMsg.class, ChangeRankHandler.class), // Change Rank + GUILDTREESTATUS(0x4B95FB85, GuildTreeStatusMsg.class, null), + HIRELINGSERVICE(0xD3D93322,HirelingServiceMsg.class,HirelingServiceMsgHandler.class), + IGNORE(0xBD8881EE, IgnoreMsg.class, null), //client sent /ignore command + INITIATETRADEHUDS(0x667D29D8, OpenTradeWindowMsg.class, null), // Open Trade Window + INVITEGROUP(0x004A2012, GroupInviteMsg.class, GroupInviteHandler.class), // Send/Receive/Deny Group Invite + INVITEGUILDFEALTY(0x0274D612, InviteToSubMsg.class, InviteToSubHandler.class), // Invite Guild to Swear + INVITETOGUILD(0x6819062A, InviteToGuildMsg.class, InviteToGuildHandler.class), // Invite player to guild, refuse guild invite + ITEMHEALTHUPDATE(0xB635F55E, ItemHealthUpdateMsg.class, null), //Update Durability of item + ITEMPRODUCTION(0x3CCE8E30, ItemProductionMsg.class, ItemProductionMsgHandler.class), + ITEMTOVAULT(0x3ABE4927, TransferItemFromInventoryToVaultMsg.class, null), // Transfer Item to Vault + JOINFORPROVINCE(0x1FB369CD, AcceptSubInviteMsg.class, AcceptSubInviteHandler.class), //Response to invite to swear? + JOINFORSWORN(0xF6A4170F, null, null), + JOINGROUP(0x7EC5E636, GroupInviteResponseMsg.class, GroupInviteResponseHandler.class), // Accept Group Invite + JOINGUILD(0xF0C5F2FF, AcceptInviteToGuildMsg.class, AcceptInviteToGuildHandler.class), // Accept guild invite + KEEPALIVESERVERCLIENT(0x49EE129C, KeepAliveServerClientMsg.class, KeepAliveServerClientHandler.class), // Keep Alive + LEADERBOARD(0x6F0C1386, LeaderboardMessage.class, null), + LEADERCHANNELMESSAGE(0x17b306f9, ChatGlobalMsg.class, null), + LEAVEGROUP(0xD8037303, LeaveGroupMsg.class, LeaveGroupHandler.class), //Leave Group + LEAVEGUILD(0x1801EA32, LeaveGuildMsg.class, LeaveGuildHandler.class), // Leave Guild + LEAVEREQUEST(0xC79D775C, LeaveWorldMsg.class, null), //Client Request Leave World + LEAVEWORLD(0xB801EAEC, null, null), //Response to client for Request Leave World + LOADCHARACTER(0x5756BC53, null, null), // Load Player/NPC/Mob, other then self + LOADSTRUCTURE(0xB8A3A654, LoadStructureMsg.class, null), //Load Buildings and World Detail Objects + LOCKUNLOCKDOOR(0x8D0E8C44, LockUnlockDoorMsg.class, LockUnlockDoorMsgHandler.class), // Lock/Unlock Door + LOGIN(0x3D51E445, ClientLoginInfoMsg.class, null), // Login Information + LOGINFAILED(0x47B867F6, null, null), // Login Error + LOGINTOGAMESERVER(0x77910FDF, LoginToGameServerMsg.class, LoginToGameServerMsgHandler.class), // Login to Game Server + MANAGECITYASSETS(0xCFF01225, ManageCityAssetsMsg.class, ManageCityAssetMsgHandler.class), // Manage city assets + MANAGENPC(0x43A273FA, null, null), // Open Hireling Management Page + MERCHANT(0x3E645EF4, MerchantMsg.class, MerchantMsgHandler.class), // Open Teleport List, Teleport, Open Shrine, Request Boon, open/manage warehouse window + MINIONTRAINING(0xD355F528, MinionTrainingMessage.class, MinionTrainingMsgHandler.class), + MODIFYGUILDSTATE(0x38936FEA, ToggleLfgRecruitingMsg.class, null), //Toggle LFGroup/LFGuild/Recruiting + MOTD(0xEC841E8D, MOTDMsg.class, MOTDEditHandler.class), //Send/Rec Guild/Nation/IC MOTD Message + MOVECORRECTION(0x47FAD1E3, null, null), //Force move to point? + MOVEOBJECTTOCONTAINER(0xD1639F7C, LootMsg.class, null), //Send/Recv MoveObjectToContainer Msg + MOVETOPOINT(0x49EF7241, MoveToPointMsg.class, MoveToPointHandler.class), // Move to point + NAMEVERIFY(0x1B3BF0B1, null, null), // Invalid Name in Character Creation + NEWWORLD(0x982E4A77, WorldDataMsg.class, null), // World Data + OBJECTACTION(0x06855A36, ObjectActionMsg.class, ObjectActionMsgHandler.class), //Use item + OKCOSTTOOPENBANK(0x6F97A502, null, null), + OPENFRIENDSCONDEMNLIST(0x49E5FE4F, OpenFriendsCondemnListMsg.class, OpenFriendsCondemnListMsgHandler.class), // Friends/Con demn/Kill/Death/Heraldry List + OPENVAULT(0xBE048E50, OpenVaultMsg.class, null), // Open Vault Window + ORDERNPC(0x61C707B1, OrderNPCMsg.class, OrderNPCMsgHandler.class), + PASSIVEMESSAGETRIGGER(0x2FF9E2E4, null, null), //PassiveMessageTriggerMsg + PET(0x624F3D8C, PetMsg.class, null), //Summon Pet? + PLACEASSET(0x940962DF, PlaceAssetMsg.class, PlaceAssetMsgHandler.class), + PLAYERDATA(0xB206D352, SendOwnPlayerMsg.class, null), //Enter World, Own Player Data + PLAYERFRIENDS(0xDDEF9E7D, FriendRequestMsg.class, FriendRequestHandler.class), + POWER(0x3C97A459, PerformActionMsg.class, null), // REQ / CMD Perform Action + POWERACTION(0xA0B27EEB, ApplyEffectMsg.class, null), // Apply Effect, add to effects icons + POWERACTIONDD(0xD43052F8, ModifyHealthMsg.class, null), //Modify Health/Mana/Stamina using power + POWERACTIONDDDIE(0xC27D446B, null, null), //Modify Health/Mana/Stamina using power and kill target + POWERTARGNAME(0x5A807CCE, SendSummonsRequestMsg.class, null), // Send Summons Request + RAISEATTR(0x5EEB65E0, ModifyStatMsg.class, null), // Modify Stat + RANDOM(0xAC5D0135, RandomMsg.class, null), //RequestSend random roll + READYTOENTER(0x490E4FE0, EnterWorldReceivedMsg.class, null), //Client Ack Receive Enter World + REALMDATA(0x2399B775, null, null), //Realm Data - Optional(?) + RECOMMENDNATION(0x6D4579E9, RecommendNationMsg.class, RecommendNationMsgHandler.class), // Recommend as Ally/Enemy, error + RECYCLEPOWER(0x24033B67, RecyclePowerMsg.class, null), //Unlock power for reUse + REMOVECHAR(0x5D3F9739, DeleteCharacterMsg.class, null), // Delete Character + REMOVEFRIEND(0xE0D5DB42,RemoveFriendMessage.class,RemoveFriendHandler.class), + REPAIRBUILDING(0xAF8C2560, RepairBuildingMsg.class, RepairBuildingMsgHandler.class), + REPAIROBJECT(0x782219CE, RepairMsg.class, null), //Repair Window Req/Ack, RepairObject item Req/Ack + REQUESTCONTENTS(0xA786B0A2, LootWindowRequestMsg.class, null), // MoveObjectToContainer Window Request + REQUESTGUILDLIST(0x85DCC6D7, ReqGuildListMsg.class, RequestGuildListHandler.class), + REQUESTMELEEATTACK(0x98C71545, AttackCmdMsg.class, null), // Attack + REQUESTMEMBERLIST(0x3235E5EA, GuildControlMsg.class, GuildControlHandler.class), // Part of Promote/Demote, Also Player History + REQUESTTOOPENBANK(0xF26E453F, null, null), // RequestToOpenBankMsg + REQUESTTOTRADE(0x4D84259B, TradeRequestMsg.class, null), // Trade Request + REQUESTTRADECANCEL(0xCB0C5735, RejectTradeRequestMsg.class, null), // Reject RequestToTrade + REQUESTTRADEOK(0xFFD29841, AcceptTradeRequestMsg.class, null), // Accept Trade Request + RESETAFTERDEATH(0xFDCBB98F,RespawnMsg.class, null), //Respawn Request/Response + ROTATEMSG(0x57F2088E, RotateObjectMsg.class, null), + SAFEMODE(0x9CF3922A, SafeModeMsg.class, null), //Tell client they're in safe mode + SCALEOBJECT(0xE2B392D9, null, null), // Adjust scale of object + SELECTCHAR(0x7E6A9338, GameServerIPRequestMsg.class, null), // Game Server IP Request + SELECTCITY(0x7E6BE630, null, null), + SELECTSERVER(0x440D28B7, ServerInfoMsg.class, null), // Server Info Request/Response + SELLOBJECT(0x57111C67, SellToNPCMsg.class, null), //Sell to NPC + SENDCITYENTRY(0xBC3B5E72, null, null), //Send Teleport/Repledge List + SENDGUILDENTRY(0x6D5EF164, null, null), + SENDMEMBERENTRY(0x6949C720, GuildListMsg.class, GuildListHandler.class), // ShowCombatInfo guild members list, I think + SETITEMFLAG(0xE8C1B53B, null, null), + SETMOTD(0xFD21FC7C, MOTDCommitMsg.class, MOTDCommitHandler.class), //Commit Guild/Nation/IC MOTD Message + SETOBJVAL(0x08A50FD1, null, null), + SETRUNE(0x888E7C64, ApplyRuneMsg.class, null), //Apply Promotion, Stat Rune (maybe disc also) + SETSELECTEDOBECT(0x64E10938, TargetObjectMsg.class, null), // Target an object + SHOPINFO(0x267DAB90, SellToNPCWindowMsg.class, null), //open Sell to NPC Window + SHOPLIST(0x682DAB4D, BuyFromNPCWindowMsg.class, null), // Open Buy From NPC Window + SHOWCOMBATINFO(0x9BF1E5EA, ShowMsg.class, null), // Request/Response /show + SHOWVAULTINVENTORY(0xD1FB4842, null, null), // Show Vault Inventory + SOCIALCHANNEL(0x2BF58FA6, SocialMsg.class, null), // Socials + STANDARDALERT(0xFA0A24BB, ErrorPopupMsg.class, null), //Popup messages + STUCK(0x3D04AF3A, StuckCommandMsg.class, null), // /Stuck Command + SWEARINGUILD(0x389B66B1, SwearInGuildMsg.class, SwearInGuildHandler.class), + SYNC(0x49ec109f, null, null), //Client/Server loc sync + SYSTEMBROADCASTCHANNEL(0x2FAD89D1, ChatSystemMsg.class, null), // Chat Channel: System Message + SYSTEMCHANNEL(0x29BB4D66, ChatSystemChannelMsg.class, null), // Chat System Channel + TARGETEDACTION(0xB79BA48F, TargetedActionMsg.class, null), //Message sent for attacks + TAXCITY(0xCD41EAA6, TaxCityMsg.class, TaxCityMsgHandler.class), + TAXRESOURCES(0x4AD458AF, TaxResourcesMsg.class, TaxResourcesMsgHandler.class), + TELEPORT(0x23E726EA, TeleportToPointMsg.class, null), // Teleport to point + TERRITORYCHANGE(0x6B388C8C,TerritoryChangeMessage.class, null), //Hey rich, look what I found? :) + TOGGLESITSTAND(0x624F3C0F, ToggleSitStandMsg.class, null), //Toggle Sit/Stand + TRADEADDGOLD(0x654ACB45, AddGoldToTradeWindowMsg.class, null), // Add Gold to Trade Window + TRADEADDOBJECT(0x55D363E9, AddItemToTradeWindowMsg.class, null), // Add an Item to the Trade Window + TRADECLOSE(0x5008D7FC, CloseTradeWindowMsg.class, null), // Cancel trade/ACK trade complete + TRADECONFIRM(0x6911E65E, CommitToTradeMsg.class, null), // Commit to trade + TRADECONFIRMSTATUS(0x9F85DAFC, null, null), // Other player commit/uncommit/add item + TRADEUNCONFIRM(0xEBE280E0, UncommitToTradeMsg.class, null), // Uncommit to trade + TRAINERLIST(0x41FABA62, TrainerInfoMsg.class, null), //Req/Send Trainer Info/Pricing + TRAINSKILL(0xB0BF68CD, TrainMsg.class, null), //Train skills/powers + TRANSFERASSET(0x3EA1C4C9, TransferAssetMsg.class, TransferAssetMsgHandler.class), // Transfer Building + TRANSFERGOLDFROMVAULTTOINVENTORY(0x011D0123, TransferGoldFromVaultToInventoryMsg.class, null), // Transfer Gold from Vault to Inventory + TRANSFERGOLDTOFROMBUILDING(0x1B1AC8C7, TransferGoldToFromBuildingMsg.class, TransferGoldToFromBuildingMsgHandler.class), // Transfer Gold to/From Building, Transfer Error + TRANSFERITEMFROMBANK(0x9D04977B, TransferItemFromBankToInventoryMsg.class, null), // Transfer Item from Bank to Inventory + TRANSFERITEMFROMVAULTTOINVENTORY(0x0119A64D, TransferItemFromVaultToInventoryMsg.class, null), // Transfer Item from Vault to Inventory + TRANSFERITEMTOBANK(0xD48C46FA, TransferItemFromInventoryToBankMsg.class, null), // Transfer Item from Inventory to Bank + UNEQUIP(0xC6BFB907, TransferItemFromEquipToInventoryMsg.class, null), // Transfer Item from Equip to Inventory + UNKNOWN(0x238C9259, UnknownMsg.class,null), + UPDATECHARORMOB(0xB6D78961, null, null), + UPDATECLIENTALLIANCES(0xF3FEB5D4, null, GuildUnknownHandler.class), //AlliancesMsg + UPDATECLIENTINVENTORIES(0xE66F533D, UpdateInventoryMsg.class, null), //Update player inventory + UPDATEEFFECTS(0xD4675293, null, null), //Update all effects for an item + UPDATEFRIENDSTATUS(0x654E2255, UpdateFriendStatusMessage.class, UpdateFriendStatusHandler.class), + UPDATEGOLDVALUE(0x6915A3FB, null, null), // Update gold in inventory and/or bank + UPDATEGROUP(0x004E6BCE, GroupUpdateMsg.class, GroupUpdateHandler.class), // Update Group Info + UPDATEGUILD(0x001D4DF6, GuildInfoMsg.class, GuildInfoHandler.class), // REQ / CMD Promote/Demote Screen + UPDATEOBJECT(0x1A724739, null, null), + UPDATESTATE(0x001A45FB, UpdateStateMsg.class, null), // REQ / CMD Toggle Run/Walk Sit/Stand :: UpdateStateMessage + UPDATETRADEWINDOW(0x406EBDE6, UpdateTradeWindowMsg.class, null), // Trade Complete + UPGRADEASSET(0x2B85A865, UpgradeAssetMessage.class, UpgradeAssetMsgHandler.class), + VENDORDIALOG(0x98ACD594, VendorDialogMsg.class, null), // Send/Recv Vendor Dialog + VERSIONINFO(0x4B7EE463, VersionInfoMsg.class, null), // Version Information + VIEWRESOURCES(0xCEFD0346, ViewResourcesMessage.class, null), + VISUALUPDATE(0x33402fd2, null, null), + WEIGHTINVENTORY(0xF1B6A85C, LootWindowResponseMsg.class, null), // MoveObjectToContainer Window Response + WHOREQUEST(0xF431CCE9, WhoRequestMsg.class, null), // Request /who + WHORESPONSE(0xD7C36568, WhoResponseMsg.class, null), // Response /who + REQUESTBALLLIST(0xE366FF64,RequestBallListMessage.class,RequestBallListHandler.class), + SENDBALLENTRY(0xAC2B5EDC,SendBallEntryMessage.class,SendBallEntryHandler.class), + UNKNOWN1(-263523523, Unknown1Msg.class,null), + DROPGOLD(1461654160,DropGoldMsg.class,null); + + public int opcode; + private Class message; + private Class handlerClass; + public Constructor constructor; + public AbstractClientMsgHandler handler; + + Protocol(int opcode, Class message, Class handlerClass) { + this.opcode = opcode; + this.message = message; + this.handlerClass = handlerClass; + + // Create reference to message class constructor. + + if (this.message != null) { + Class[] params = {AbstractConnection.class, ByteBufferReader.class}; + + try { + this.constructor = this.message.getConstructor(params); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + } + + // Create instance of message handler for incoming protocol messages + + if (this.handlerClass != null) { + try { + handler = (AbstractClientMsgHandler) handlerClass.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + } + } + } + + private static HashMap _protocolMsgByOpcode = new HashMap<>(); + + public static Protocol getByOpcode(int opcode) { + + Protocol protocol = _protocolMsgByOpcode.get(opcode); + + if (protocol != null) + return protocol; + + return Protocol.NONE; + } + + public static void initProtocolLookup() { + + for (Protocol protocol : Protocol.values()) { + + if (_protocolMsgByOpcode.containsKey(protocol.opcode)){ + Logger.error("Duplicate opcodes for " + protocol.name() + " and " + _protocolMsgByOpcode.get(protocol.opcode).name()); + } + _protocolMsgByOpcode.put(protocol.opcode, protocol); + } + } + + +public static int FindNextValidOpcode(ByteBufferReader reader){ + int startPos = reader.position(); + int bytesLeft = reader.remaining(); + + if (bytesLeft < 4) + return startPos; + int nextPos = startPos; + for (int i = 1; i< bytesLeft; i++ ){ + reader.position(nextPos); + if (reader.remaining() < 4) + return reader.position(); + int newOpcode = reader.getInt(); + + Protocol foundProtocol = Protocol.getByOpcode(newOpcode); + if (foundProtocol.equals(Protocol.NONE)){ + nextPos += 1; + continue; + } + + //found opcode. return position - 4 to rewind back to start of opcode, so we can handle it. + return reader.position() - 4; + } + + return startPos; +} +} diff --git a/src/engine/net/client/handlers/AbandonAssetMsgHandler.java b/src/engine/net/client/handlers/AbandonAssetMsgHandler.java new file mode 100644 index 00000000..111a2319 --- /dev/null +++ b/src/engine/net/client/handlers/AbandonAssetMsgHandler.java @@ -0,0 +1,206 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.BuildingGroup; +import engine.Enum.GuildState; +import engine.exception.MsgSendException; +import engine.gameManager.*; +import engine.net.client.ClientConnection; +import engine.net.client.msg.AbandonAssetMsg; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.objects.*; +import org.pmw.tinylog.Logger; + +/* + * @Author: + * @Summary: Processes application protocol message which processes + * client requests to abandon a building. + */ +public class AbandonAssetMsgHandler extends AbstractClientMsgHandler { + + // Instance variables + + public AbandonAssetMsgHandler() { + super(AbandonAssetMsg.class); + + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + // Member variable declaration + PlayerCharacter player; + Building building; + AbandonAssetMsg msg; + + // Member variable assignment + msg = (AbandonAssetMsg) baseMsg; + + player = origin.getPlayerCharacter(); + building = BuildingManager.getBuildingFromCache(msg.getUUID()); + + // Oops! *** Refactor: Log error + if ((player == null) || (building == null)) + return true; + + // Early exit if object is not owned by the player + if (building.getOwnerUUID() != player.getObjectUUID()) + return true; + + // Cannot abandon a building without a blueprint. + // Players do not own rocks or shrubbery. + if (building.getBlueprintUUID() == 0) + return true; + + // Players cannot abandon shrines + + if ((building.getBlueprint().getBuildingGroup() == BuildingGroup.SHRINE)) { + ErrorPopupMsg.sendErrorMsg(player, "Cannot for to abandon shrine!"); + return true; + } + + if ((building.getBlueprint().getBuildingGroup() == BuildingGroup.MINE)) { + ErrorPopupMsg.sendErrorMsg(player, "Cannot abandon mine!"); + return true; + } + + if (Blueprint.isMeshWallPiece(building.getBlueprintUUID())) { + ErrorPopupMsg.sendErrorMsg(player, "Cannot for to abandon fortress asset!"); + return true; + } + + if ((building.getBlueprint().getBuildingGroup() == BuildingGroup.BARRACK)) { + ErrorPopupMsg.sendErrorMsg(player, "Cannot for to abandon fortress asset!"); + return true; + } + + if ((building.getBlueprint().getBuildingGroup() == BuildingGroup.BULWARK)) { + ErrorPopupMsg.sendErrorMsg(player, "Cannot for to abandon siege asset!"); + return true; + } + + if ((building.getBlueprint().getBuildingGroup() == BuildingGroup.SIEGETENT)) { + ErrorPopupMsg.sendErrorMsg(player, "Cannot for to abandon siege asset!"); + return true; + } + + if ((building.getBlueprint().getBuildingGroup() == BuildingGroup.BANESTONE)) { + ErrorPopupMsg.sendErrorMsg(player, "Cannot for to abandon banestone!"); + return true; + } + + // Trees require special handling beyond an individual building + if ((building.getBlueprint().getBuildingGroup() == BuildingGroup.TOL)) + AbandonAllCityObjects(player, building); + else + AbandonSingleAsset(player, building); + + return true; + } + + private static void AbandonSingleAsset(PlayerCharacter sourcePlayer, + Building targetBuilding) { + + // Transfer the building asset ownership and refresh all clients + + DbManager.BuildingQueries.CLEAR_FRIENDS_LIST(targetBuilding.getObjectUUID()); + targetBuilding.getFriends().clear(); + + // Clear protection status but only if a seige building + + if (targetBuilding.getBlueprint().getBuildingGroup().equals(BuildingGroup.BULWARK) || + targetBuilding.getBlueprint().getBuildingGroup().equals(BuildingGroup.SIEGETENT)) + targetBuilding.setProtectionState(Enum.ProtectionState.NONE); + + DbManager.BuildingQueries.CLEAR_CONDEMNED_LIST(targetBuilding.getObjectUUID()); + targetBuilding.getCondemned().clear(); + targetBuilding.setOwner(null); + targetBuilding.refreshGuild(); + + } + + private void AbandonAllCityObjects(PlayerCharacter sourcePlayer, + Building targetBuilding) { + Guild sourceGuild; + Zone cityZone; + + sourceGuild = sourcePlayer.getGuild(); + + if (sourceGuild == null) + return; + + if (sourceGuild.getSubGuildList().size() > 0) { + ChatManager.chatCityError(sourcePlayer, "You Cannot abandon a nation city."); + return; + } + + + + cityZone = ZoneManager.findSmallestZone(targetBuilding.getLoc()); + + // Can't abandon a tree not within a player city zone + if (cityZone.isPlayerCity() == false) + return; + + if (targetBuilding.getCity() == null) + return; + + if (targetBuilding.getCity().getBane() != null){ + ErrorPopupMsg.sendErrorMsg(sourcePlayer, "Can't abandon Tree while a bane exists."); + return; + } + + if (targetBuilding.getCity().hasBeenTransfered == true) { + ChatManager.chatCityError(sourcePlayer, "City can only be abandoned once per rebooting."); + return; + } + + // Guild no longer owns his tree. + if (!DbManager.GuildQueries.SET_GUILD_OWNED_CITY(sourceGuild.getObjectUUID(), 0)) { + Logger.error("Failed to update Owned City to Database"); + return; + } + + sourceGuild.setCityUUID(0); + sourceGuild.setGuildState(GuildState.Errant); + sourceGuild.setNation(null); + + // Transfer the city assets + TransferCityAssets(sourcePlayer, targetBuilding); + + GuildManager.updateAllGuildTags(sourceGuild); + GuildManager.updateAllGuildBinds(sourceGuild, null); + + } + + private void TransferCityAssets(PlayerCharacter sourcePlayer, + Building cityTOL) { + + Zone cityZone; + + // Build list of buildings within this parent zone + cityZone = ZoneManager.findSmallestZone(cityTOL.getLoc()); + + for (Building cityBuilding : cityZone.zoneBuildingSet) { + + Blueprint cityBlueprint; + cityBlueprint = cityBuilding.getBlueprint(); + + // Buildings without blueprints cannot be abandoned + if (cityBlueprint == null) + continue; + + // Transfer ownership of valid city assets + if ((cityBlueprint.getBuildingGroup() == BuildingGroup.TOL) + || (cityBlueprint.getBuildingGroup() == BuildingGroup.SPIRE) + || (cityBlueprint.getBuildingGroup() == BuildingGroup.BARRACK) + || (cityBlueprint.isWallPiece()) + || (cityBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.SHRINE)) + AbandonSingleAsset(sourcePlayer, cityBuilding); + + } + + } + +} diff --git a/src/engine/net/client/handlers/AbstractClientMsgHandler.java b/src/engine/net/client/handlers/AbstractClientMsgHandler.java new file mode 100644 index 00000000..fdc2153b --- /dev/null +++ b/src/engine/net/client/handlers/AbstractClientMsgHandler.java @@ -0,0 +1,33 @@ +package engine.net.client.handlers; + +import engine.exception.MsgSendException; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; + +/* @Summary: This is the abstract class from which all message handlers + * for mainline application protocol derive. Namely those + * routed and executed via ClientMessageHandler. + */ + +public abstract class AbstractClientMsgHandler { + private final Class handler; + + public AbstractClientMsgHandler(Class handler) { + this.handler = handler; + } + + public boolean handleNetMsg(ClientNetMsg msg) { + + boolean executionSucceded; + + try { + executionSucceded = _handleNetMsg(msg, (ClientConnection) msg.getOrigin()); + } catch (MsgSendException e) { + e.printStackTrace(); + executionSucceded = false; + } + + return executionSucceded; + } + +protected abstract boolean _handleNetMsg(ClientNetMsg msg, ClientConnection origin) throws MsgSendException;} diff --git a/src/engine/net/client/handlers/AcceptInviteToGuildHandler.java b/src/engine/net/client/handlers/AcceptInviteToGuildHandler.java new file mode 100644 index 00000000..ff33c3e9 --- /dev/null +++ b/src/engine/net/client/handlers/AcceptInviteToGuildHandler.java @@ -0,0 +1,122 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.Enum.GuildHistoryType; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.gameManager.GuildManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.guild.AcceptInviteToGuildMsg; +import engine.net.client.msg.guild.GuildInfoMsg; +import engine.objects.Guild; +import engine.objects.GuildHistory; +import engine.objects.PlayerCharacter; +import org.joda.time.DateTime; + +public class AcceptInviteToGuildHandler extends AbstractClientMsgHandler { + + public AcceptInviteToGuildHandler() { + super(AcceptInviteToGuildMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter player; + AcceptInviteToGuildMsg msg; + Guild guild; + + msg = (AcceptInviteToGuildMsg) baseMsg; + + // get PlayerCharacter of person accepting invite + + player = SessionManager.getPlayerCharacter(origin); + + if (player == null) + return true; + + guild = (Guild) DbManager.getObject(GameObjectType.Guild, msg.getGuildUUID()); + + + if (guild == null) + return true; + + if (guild.getGuildType() == null){ + ErrorPopupMsg.sendErrorPopup(player, GuildManager.NO_CHARTER_FOUND); + return true; + } + + // verify they accepted for the correct guild + + if (player.getLastGuildToInvite() != msg.getGuildUUID()) + return true; + + if ( (player.getGuild() != null) && + (player.getGuild().isErrant() == false)) { + ChatManager.chatGuildError(player, + "You already belongs to a guild!"); + return true; + } + + // verify they are acceptable level for guild + + if (player.getLevel() < guild.getRepledgeMin() || player.getLevel() > guild.getRepledgeMax()) + return true; + + // Add player to guild + player.setGuild(guild); + + // Cleanup guild stuff + player.resetGuildStatuses(); + + Dispatch dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + DispatchMessage.sendToAllInRange(player, new GuildInfoMsg(player, guild, 2)); + + player.incVer(); + + //Add to guild History + + if (player.getGuild() != null){ + if (DbManager.GuildQueries.ADD_TO_GUILDHISTORY(player.getGuildUUID(), player, DateTime.now(), GuildHistoryType.JOIN)){ + GuildHistory guildHistory = new GuildHistory(player.getGuildUUID(),player.getGuild().getName(),DateTime.now(), GuildHistoryType.JOIN) ; + player.getGuildHistory().add(guildHistory); + } + } + + // Send guild join message + + ChatManager.chatGuildInfo(player, player.getFirstName() + " has joined the guild"); + return true; + } + +} diff --git a/src/engine/net/client/handlers/AcceptSubInviteHandler.java b/src/engine/net/client/handlers/AcceptSubInviteHandler.java new file mode 100644 index 00000000..38fbb3ef --- /dev/null +++ b/src/engine/net/client/handlers/AcceptSubInviteHandler.java @@ -0,0 +1,106 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.Enum.GuildState; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.guild.AcceptSubInviteMsg; +import engine.objects.Guild; +import engine.objects.GuildStatusController; +import engine.objects.PlayerCharacter; + +import java.util.ArrayList; + +public class AcceptSubInviteHandler extends AbstractClientMsgHandler { + + public AcceptSubInviteHandler() { + super(AcceptSubInviteMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + AcceptSubInviteMsg msg = (AcceptSubInviteMsg) baseMsg; + PlayerCharacter sourcePlayer; + Guild sourceGuild; + Guild targetGuild; + Dispatch dispatch; + + // get PlayerCharacter of person sending sub invite + + sourcePlayer = SessionManager.getPlayerCharacter(origin); + + if (sourcePlayer == null) + return true; + + sourceGuild = sourcePlayer.getGuild(); + targetGuild = (Guild) DbManager.getObject(GameObjectType.Guild, msg.guildUUID()); + + //must be source guild to sub to + + if (targetGuild == null) { + ErrorPopupMsg.sendErrorPopup(sourcePlayer, 45); // Failure to swear guild + return true; + } + if (sourceGuild == null) { + ErrorPopupMsg.sendErrorPopup(sourcePlayer, 45); // Failure to swear guild + return true; + } + + if (sourceGuild.equals(targetGuild)) + return true; + + if (GuildStatusController.isGuildLeader(sourcePlayer.getGuildStatus()) == false) { + ErrorPopupMsg.sendErrorMsg(sourcePlayer, "Only a guild leader can accept fealty!"); + return true; + } + + //source guild is limited to 7 subs + //TODO this should be based on TOL rank + + if (!targetGuild.canSubAGuild(sourceGuild)) { + ErrorPopupMsg.sendErrorPopup(sourcePlayer, 45); // Failure to swear guild + return true; + } + + //all tests passed, let's Handle code + //Update Target Guild State. + + sourceGuild.upgradeGuildState(false); + + //Add sub so GuildMaster can Swear in. + + ArrayList subs = targetGuild.getSubGuildList(); + subs.add(sourceGuild); + + targetGuild.setGuildState(GuildState.Nation); + + + //Let's send the message back. + + msg.setUnknown02(1); + msg.setResponse("Your guild is now a " + sourceGuild.getGuildState().name() + '.'); + dispatch = Dispatch.borrow(sourcePlayer, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + ChatManager.chatSystemInfo(sourcePlayer, "Your guild is now a " + sourceGuild.getGuildState().name() + '.'); + return true; + } +} diff --git a/src/engine/net/client/handlers/ActivateNPCMsgHandler.java b/src/engine/net/client/handlers/ActivateNPCMsgHandler.java new file mode 100644 index 00000000..ad204bfd --- /dev/null +++ b/src/engine/net/client/handlers/ActivateNPCMsgHandler.java @@ -0,0 +1,136 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.ItemType; +import engine.exception.MsgSendException; +import engine.gameManager.*; +import engine.math.Vector3fImmutable; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ActivateNPCMessage; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ManageCityAssetsMsg; +import engine.objects.*; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; + +/* + * @Author: + * @Summary: Processes application protocol message which keeps + * client's tcp connection open. + */ +public class ActivateNPCMsgHandler extends AbstractClientMsgHandler { + + public ActivateNPCMsgHandler() { + super(ActivateNPCMessage.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + ActivateNPCMessage msg; + PlayerCharacter player; + Building building; + Contract contract; + CharacterItemManager itemMan; + Zone zone; + + msg = (ActivateNPCMessage) baseMsg; + player = SessionManager.getPlayerCharacter(origin); + building = BuildingManager.getBuildingFromCache(msg.buildingUUID()); + + if (player == null || building == null) + return false; + + ArrayList ItemLists = new ArrayList<>(); + + // Filter hirelings by slot type + + for (Item hirelings : player.getInventory()) { + if (hirelings.getItemBase().getType().equals(ItemType.CONTRACT)) { + contract = DbManager.ContractQueries.GET_CONTRACT(hirelings.getItemBase().getUUID()); + if (contract == null) + continue; + if (contract.canSlotinBuilding(building)) + ItemLists.add(hirelings); + } + } + + if (msg.getUnknown01() == 1) { + //Request npc list to slot + ActivateNPCMessage anm = new ActivateNPCMessage(); + anm.setSize(ItemLists.size()); + anm.setItemList(ItemLists); + Dispatch dispatch = Dispatch.borrow(player, anm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + if (msg.getUnknown01() == 0) { + + //Slot npc + + if (building.getBlueprintUUID() == 0) { + ChatManager.chatSystemError(player, "Unable to load Blueprint for Building Mesh " + building.getMeshUUID()); + return false; + } + + if (building.getBlueprint().getMaxSlots() == building.getHirelings().size()) + return false; + + Vector3fImmutable NpcLoc = new Vector3fImmutable(building.getLoc()); + + Item contractItem = Item.getFromCache(msg.getUnknown04()); + + if (contractItem == null) + return false; + + if (!player.getCharItemManager().doesCharOwnThisItem(contractItem.getObjectUUID())) { + Logger.error(player.getName() + "has attempted to place Hireling : " + contractItem.getName() + "without a valid contract!"); + return false; + } + + itemMan = player.getCharItemManager(); + + zone = ZoneManager.findSmallestZone(NpcLoc); + + if (zone == null) + return false; + + contract = DbManager.ContractQueries.GET_CONTRACT(contractItem.getItemBase().getUUID()); + + if (contract == null) + return false; + + // Check if contract can be slotted in this building + + if (contract.canSlotinBuilding(building) == false) + return false; + + if (!BuildingManager.addHireling(building, player, NpcLoc, zone, contract, contractItem)) + return false; + + itemMan.delete(contractItem); + itemMan.updateInventory(); + + ManageCityAssetsMsg mca1 = new ManageCityAssetsMsg(player, building); + + mca1.actionType = 3; + + mca1.setTargetType(building.getObjectType().ordinal()); + mca1.setTargetID(building.getObjectUUID()); + mca1.setTargetType3(building.getObjectType().ordinal()); + mca1.setTargetID3(building.getObjectUUID()); + mca1.setAssetName1(building.getName()); + mca1.setUnknown54(1); + Dispatch dispatch = Dispatch.borrow(player, mca1); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + + } + + return true; + } + +} diff --git a/src/engine/net/client/handlers/AllianceChangeMsgHandler.java b/src/engine/net/client/handlers/AllianceChangeMsgHandler.java new file mode 100644 index 00000000..db918d6d --- /dev/null +++ b/src/engine/net/client/handlers/AllianceChangeMsgHandler.java @@ -0,0 +1,161 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.AllianceType; +import engine.exception.MsgSendException; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.AllianceChangeMsg; +import engine.net.client.msg.ClientNetMsg; +import engine.objects.Guild; +import engine.objects.GuildStatusController; +import engine.objects.PlayerCharacter; + +/* + * @Author: + * @Summary: Processes application protocol message which handles + * protecting and unprotecting city assets + */ +public class AllianceChangeMsgHandler extends AbstractClientMsgHandler { + + public AllianceChangeMsgHandler() { + super(AllianceChangeMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + // Member variable declaration + + PlayerCharacter player; + AllianceChangeMsg msg; + + + // Member variable assignment + + msg = (AllianceChangeMsg) baseMsg; + + player = SessionManager.getPlayerCharacter(origin); + + if (player == null) + return true; + + + + Guild toGuild = null; + toGuild = Guild.getGuild(msg.getSourceGuildID()); + if (toGuild.isErrant()) + return true; + + if (player.getGuild().isErrant()) + return true; + + + + switch (msg.getMsgType()){ + case AllianceChangeMsg.MAKE_ALLY: + case 1: //allyfromRecommended + player.getGuild().addGuildToAlliance(msg, AllianceType.Ally, toGuild, player); + break; + case AllianceChangeMsg.MAKE_ENEMY: + case 2: //enemy recommend + player.getGuild().addGuildToAlliance(msg, AllianceType.Enemy, toGuild, player); + break; + case 3: + case 5: + case 7: + player.getGuild().removeGuildFromAllAlliances(toGuild); + break; + + } + msg.setMsgType(AllianceChangeMsg.INFO_SUCCESS); + Dispatch dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + + + + + + + return true; + + } + + private static void MakeEnemy(Guild fromGuild, Guild toGuild, AllianceChangeMsg msg, ClientConnection origin) { + + // Member variable declaration + Dispatch dispatch; + + // Member variable assignment + + if (fromGuild == null) + return; + + if (toGuild == null) + return; + + if (!Guild.sameGuild(origin.getPlayerCharacter().getGuild(), fromGuild)){ + msg.setMsgType(AllianceChangeMsg.ERROR_NOT_SAME_GUILD); + dispatch = Dispatch.borrow(origin.getPlayerCharacter(), msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return; + } + + if (!GuildStatusController.isInnerCouncil(origin.getPlayerCharacter().getGuildStatus()) && !GuildStatusController.isGuildLeader(origin.getPlayerCharacter().getGuildStatus())){ + msg.setMsgType(AllianceChangeMsg.ERROR_NOT_AUTHORIZED); + dispatch = Dispatch.borrow(origin.getPlayerCharacter(), msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return; + } + + + dispatch = Dispatch.borrow(origin.getPlayerCharacter(), msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + + } + + private static void makeAlly(Guild fromGuild, Guild toGuild, AllianceChangeMsg msg, ClientConnection origin) { + + // Member variable declaration + Dispatch dispatch; + + // Member variable assignment + + if (fromGuild == null) + return; + + if (toGuild == null) + return; + + dispatch = Dispatch.borrow(origin.getPlayerCharacter(), msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + + } + + private static void removeFromAlliance(Guild fromGuild, Guild toGuild, AllianceChangeMsg msg, ClientConnection origin) { + + // Member variable declaration + Dispatch dispatch; + + // Member variable assignment + + if (fromGuild == null) + return; + + if (toGuild == null) + return; + + dispatch = Dispatch.borrow(origin.getPlayerCharacter(), msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + + } + + + +} diff --git a/src/engine/net/client/handlers/AllyEnemyListMsgHandler.java b/src/engine/net/client/handlers/AllyEnemyListMsgHandler.java new file mode 100644 index 00000000..b13bde9b --- /dev/null +++ b/src/engine/net/client/handlers/AllyEnemyListMsgHandler.java @@ -0,0 +1,81 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.exception.MsgSendException; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.AllyEnemyListMsg; +import engine.net.client.msg.ClientNetMsg; +import engine.objects.Guild; +import engine.objects.PlayerCharacter; + +/* + * @Author: + * @Summary: Processes application protocol message which handles + * protecting and unprotecting city assets + */ +public class AllyEnemyListMsgHandler extends AbstractClientMsgHandler { + + public AllyEnemyListMsgHandler() { + super(AllyEnemyListMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + // Member variable declaration + + PlayerCharacter player; + AllyEnemyListMsg msg; + + + // Member variable assignment + + msg = (AllyEnemyListMsg) baseMsg; + + player = SessionManager.getPlayerCharacter(origin); + + if (player == null) + return true; + + + AllyEnemyListMsgHandler.showAllyEnemyList(player.getGuild(), Guild.getGuild(msg.getGuildID()), msg, origin); + + + + + // dispatch = Dispatch.borrow(player, baseMsg); + // DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + return true; + + } + + private static void showAllyEnemyList(Guild fromGuild, Guild toGuild, AllyEnemyListMsg msg, ClientConnection origin) { + + // Member variable declaration + Dispatch dispatch; + + // Member variable assignment + + if (fromGuild == null) + return; + + if (toGuild == null) + return; + dispatch = Dispatch.borrow(origin.getPlayerCharacter(), msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + // UpdateClientAlliancesMsg ucam = new UpdateClientAlliancesMsg(); + // + // dispatch = Dispatch.borrow(origin.getPlayerCharacter(), ucam); + // DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + + } + + + +} diff --git a/src/engine/net/client/handlers/AppointGroupLeaderHandler.java b/src/engine/net/client/handlers/AppointGroupLeaderHandler.java new file mode 100644 index 00000000..b7e1f42f --- /dev/null +++ b/src/engine/net/client/handlers/AppointGroupLeaderHandler.java @@ -0,0 +1,85 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.GroupManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.group.AppointGroupLeaderMsg; +import engine.net.client.msg.group.GroupUpdateMsg; +import engine.objects.Group; +import engine.objects.PlayerCharacter; + +public class AppointGroupLeaderHandler extends AbstractClientMsgHandler { + + public AppointGroupLeaderHandler() { + super(AppointGroupLeaderMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + AppointGroupLeaderMsg msg = (AppointGroupLeaderMsg) baseMsg; + + PlayerCharacter source = SessionManager.getPlayerCharacter(origin); + if (source == null) { + return false; + } + Group group = GroupManager.getGroup(source); + if (group == null) { + return false; + } + if (group.getGroupLead() != source) { + return false; + } + PlayerCharacter target = SessionManager.getPlayerCharacterByID(msg.getTargetID()); + if (target == null) { + return false; + } + if (target == source) { // Can't appoint self leader + AppointGroupLeaderMsg reply = new AppointGroupLeaderMsg(); + reply.setResponse(1); + Dispatch dispatch = Dispatch.borrow(origin.getPlayerCharacter(), reply); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + return false; + } + + // Change Group Leader + if (!group.setGroupLead(target.getObjectUUID())) { + return false; // failed to update group leader + } + // Refresh everyones group list + GroupUpdateMsg gim = new GroupUpdateMsg(); + gim.setGroup(group); + gim.setPlayer(target); + gim.setMessageType(2); + gim.addPlayer(source); + + group.sendUpdate(gim); + + // Disable Formation + target.setFollow(false); + gim = new GroupUpdateMsg(); + gim.setGroup(group); + gim.setPlayer(target); + gim.setMessageType(8); + group.sendUpdate(gim); + + String text = target.getFirstName() + " is the new group leader."; + ChatManager.chatGroupInfo(source, text); + + return true; + } + +} diff --git a/src/engine/net/client/handlers/ArcLoginNotifyMsgHandler.java b/src/engine/net/client/handlers/ArcLoginNotifyMsgHandler.java new file mode 100644 index 00000000..dd951737 --- /dev/null +++ b/src/engine/net/client/handlers/ArcLoginNotifyMsgHandler.java @@ -0,0 +1,141 @@ +package engine.net.client.handlers; + +import engine.Enum.DispatchChannel; +import engine.exception.MsgSendException; +import engine.gameManager.*; +import engine.job.JobScheduler; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ArcLoginNotifyMsg; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.HotzoneChangeMsg; +import engine.net.client.msg.PetMsg; +import engine.objects.*; +import engine.server.MBServerStatics; +import engine.session.Session; +import org.pmw.tinylog.Logger; + +public class ArcLoginNotifyMsgHandler extends AbstractClientMsgHandler { + + public ArcLoginNotifyMsgHandler() { + super(ArcLoginNotifyMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter player = SessionManager.getPlayerCharacter(origin); + + if (player == null) { + Logger.error(ConfigManager.MB_WORLD_NAME.getValue()+ ".EnterWorld", "Unable to find player for session"); + origin.kickToLogin(MBServerStatics.LOGINERROR_UNABLE_TO_LOGIN, "Player not found."); + return true; + } + + // cancel logout Timer if exists + if (player.getTimers().containsKey("Logout")) { + + JobScheduler.getInstance().cancelScheduledJob(player.getTimers().get("Logout")); + player.getTimers().remove("Logout"); + } + player.setTimeStamp("logout", 0); + + // refresh group window if still in group for both this player + // and everyone else in the group + + if (GroupManager.getGroup(player) != null) { + GroupManager.RefreshMyGroupList(player, origin); + GroupManager.RefreshOthersGroupList(player); + } + + player.setEnteredWorld(true); + // Set player active + player.resetRegenUpdateTime(); + player.setActive(true); + + //player.sendAllEffects(player.getClientConnection()); + // Send Enter world message to guild + + ChatManager.GuildEnterWorldMsg(player, origin); + + // Send Guild, Nation and IC MOTD + GuildManager.enterWorldMOTD(player); + ChatManager.sendSystemMessage(player, ConfigManager.MB_WORLD_GREETING.getValue()); + + // Set player mask for QT + if (player.getRace() != null && player.getRace().getToken() == -524731385) + player.setObjectTypeMask(MBServerStatics.MASK_PLAYER | MBServerStatics.MASK_UNDEAD); + else + player.setObjectTypeMask(MBServerStatics.MASK_PLAYER); + + // If player not already in world, then set them to bind loc and add + // to world + + if (player.newChar) + player.newChar = false; // TODO Fix safe mode + + // PowersManager.applyPower(player, player, new + // Vector3f(0f, 0f, 0f), -1661758934, 50, false); + + // Add player to the QT for tracking + + player.setLoc(player.getLoc()); + + //send online status to friends. + PlayerFriends.SendFriendsStatus(player, true); + + // Handle too many simultaneous logins from the same forum account by disconnecting the other account(s) + + Account thisAccount = SessionManager.getAccount(player); + int maxAccounts = MBServerStatics.MAX_ACTIVE_GAME_ACCOUNTS_PER_DISCORD_ACCOUNT; + + if (maxAccounts > 0) { + + int count = 1; + for (Account othAccount : SessionManager.getAllActiveAccounts()) { + + if (othAccount.equals(thisAccount)) + continue; + + if (thisAccount.discordAccount.equals(othAccount.discordAccount) == false) + continue; + + count++; + + if (count > maxAccounts) { + Session otherSession = SessionManager.getSession(othAccount); + if (otherSession != null) { + ClientConnection otherConn = otherSession.getConn(); + if (otherConn != null) { + ChatManager.chatSystemInfo(player, "Only 4 accounts may be used simultaneously. Account '" + othAccount.getUname() + "' has been disconnected."); + otherConn.disconnect(); + } + } + } + } + } + + player.setTimeStamp("logout", 0); + + if (player.getPet() != null) { + PetMsg pm = new PetMsg(5, player.getPet()); + Dispatch dispatch = Dispatch.borrow(player, pm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + } + + //Send current hotzone + Zone hotzone = ZoneManager.getHotZone(); + + if (hotzone != null) { + HotzoneChangeMsg hcm = new HotzoneChangeMsg(hotzone.getObjectType().ordinal(), hotzone.getObjectUUID()); + Dispatch dispatch = Dispatch.borrow(player, hcm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + } + + if (player.getGuild() != null && !player.getGuild().isErrant()) { + Guild.UpdateClientAlliancesForPlayer(player); + } + return true; + } +} \ No newline at end of file diff --git a/src/engine/net/client/handlers/ArcSiegeSpireMsgHandler.java b/src/engine/net/client/handlers/ArcSiegeSpireMsgHandler.java new file mode 100644 index 00000000..9c2b7ed0 --- /dev/null +++ b/src/engine/net/client/handlers/ArcSiegeSpireMsgHandler.java @@ -0,0 +1,120 @@ +package engine.net.client.handlers; + +import engine.Enum.GameObjectType; +import engine.exception.MsgSendException; +import engine.gameManager.DbManager; +import engine.gameManager.SessionManager; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ArcSiegeSpireMsg; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.objects.Building; +import engine.objects.GuildStatusController; +import engine.objects.PlayerCharacter; +import org.pmw.tinylog.Logger; + +/* + * @Author: + * @Summary: Processes application protocol message which requests that + * siege spires be toggled on or off. + */ + +public class ArcSiegeSpireMsgHandler extends AbstractClientMsgHandler { + + public ArcSiegeSpireMsgHandler() { + super(ArcSiegeSpireMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter player; + Building spire; + ArcSiegeSpireMsg msg; + + msg = (ArcSiegeSpireMsg) baseMsg; + + player = SessionManager.getPlayerCharacter(origin); + + if (player == null) + return true; + + spire = (Building) DbManager.getObject(GameObjectType.Building, msg.getBuildingUUID()); + + if (spire == null) + return true; + + if (spire.getCity() == null) + return true; + + spire.getCity().transactionLock.writeLock().lock(); + + try{ + + + //can't activate a spire that's not rank 1. + + if (spire.getRank() < 1) + return true; + + // can't activate a spire without a city + + if (spire.getCity() == null) + return true; + + // Must have management authority for the spire + + if ((player.getGuild().equals(spire.getGuild()) == false) + || (GuildStatusController.isInnerCouncil(player.getGuildStatus()) == false) ) + return true; + + // Handle case where spire is sabotaged + + if (spire.getTimeStamp("DISABLED") > System.currentTimeMillis()) { + ErrorPopupMsg.sendErrorPopup(player, 174); //This siege spire cannot be toggled yet. + return true; + } + + // Handle case where spire's toggle delay hasn't yet passed + + if (spire.getTimeStamp("TOGGLEDELAY") > System.currentTimeMillis()) { + ErrorPopupMsg.sendErrorPopup(player, 174); //This siege spire cannot be toggled yet. + return true; + } + + // This protocol message is a toggle. If it's currently active then disable + // the spire. + + if (spire.isSpireIsActive()) { + spire.disableSpire(false); + return true; + } + + // Must be enough gold on the spire to turn it on + + if (!spire.hasFunds(5000)){ + ErrorPopupMsg.sendErrorPopup(player, 127); // Not enough gold in strongbox + return true; + } + + if (spire.getStrongboxValue() < 5000) { + ErrorPopupMsg.sendErrorPopup(player, 127); // Not enough gold in strongbox + return true; + } + + spire.transferGold(-5000,false); + spire.enableSpire(); + + // Spire is now enabled. Reset the toggle delay + + spire.setTimeStamp("TOGGLEDELAY", System.currentTimeMillis() + (long) 10 * 60 * 1000); + }catch(Exception e){ + Logger.error(e); + }finally{ + spire.getCity().transactionLock.writeLock().unlock(); + } + return true; + + } + +} diff --git a/src/engine/net/client/handlers/ArcViewAssetTransactionsMsgHandler.java b/src/engine/net/client/handlers/ArcViewAssetTransactionsMsgHandler.java new file mode 100644 index 00000000..c2d0d952 --- /dev/null +++ b/src/engine/net/client/handlers/ArcViewAssetTransactionsMsgHandler.java @@ -0,0 +1,54 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.exception.MsgSendException; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ArcViewAssetTransactionsMsg; +import engine.net.client.msg.ClientNetMsg; +import engine.objects.PlayerCharacter; +import engine.objects.Warehouse; + +/* + * @Author: + * @Summary: Processes application protocol message which transfers + * gold between a building's strongbox and a player character. + */ + +public class ArcViewAssetTransactionsMsgHandler extends AbstractClientMsgHandler { + + public ArcViewAssetTransactionsMsgHandler() { + super(ArcViewAssetTransactionsMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter player; + ArcViewAssetTransactionsMsg msg; + ArcViewAssetTransactionsMsg newMsg; + player = SessionManager.getPlayerCharacter(origin); + Dispatch dispatch; + + if (player == null) + return true; + + msg = (ArcViewAssetTransactionsMsg) baseMsg; + + Warehouse warehouse = Warehouse.warehouseByBuildingUUID.get(msg.getWarehouseID()); + + if (warehouse == null) + return true; + + newMsg = new ArcViewAssetTransactionsMsg(warehouse,msg); + newMsg.configure(); + + dispatch = Dispatch.borrow(player, newMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + return true; + } + +} diff --git a/src/engine/net/client/handlers/AssetSupportMsgHandler.java b/src/engine/net/client/handlers/AssetSupportMsgHandler.java new file mode 100644 index 00000000..561aaa0f --- /dev/null +++ b/src/engine/net/client/handlers/AssetSupportMsgHandler.java @@ -0,0 +1,240 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.SupportMsgType; +import engine.Enum.TaxType; +import engine.exception.MsgSendException; +import engine.gameManager.BuildingManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.*; +import engine.objects.*; +import org.pmw.tinylog.Logger; + +/* + * @Author: + * @Summary: Processes application protocol message which handles + * protecting and unprotecting city assets + */ +public class AssetSupportMsgHandler extends AbstractClientMsgHandler { + + public AssetSupportMsgHandler() { + super(AssetSupportMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + // Member variable declaration + + PlayerCharacter player; + NPC vendor; + Building targetBuilding; + Dispatch dispatch; + + AssetSupportMsg msg; + CityAssetMsg outMsg; + + // Member variable assignment + + msg = (AssetSupportMsg) baseMsg; + + player = SessionManager.getPlayerCharacter(origin); + + if (player == null) + return true; + + vendor = NPC.getFromCache(msg.getNpcID()); + + if (msg.getMessageType() !=6 && msg.getMessageType() != 7){ + if (vendor == null) + return true; + + vendor.getBuilding(); + + if (vendor.getBuilding() == null) + return true; + } + + + SupportMsgType supportType = SupportMsgType.typeLookup.get(msg.getMessageType()); + + if (supportType == null) { + supportType = Enum.SupportMsgType.NONE; + Logger.error("No enumeration for support type" + msg.getMessageType()); + } + switch (supportType) { + + case PROTECT: + targetBuilding = BuildingManager.getBuildingFromCache(msg.getProtectedBuildingID()); + protectAsset(msg,targetBuilding, vendor, origin); + break; + case UNPROTECT: + targetBuilding = BuildingManager.getBuildingFromCache(msg.getProtectedBuildingID()); + unprotectAsset(targetBuilding, vendor, origin); + break; + case VIEWUNPROTECTED: + outMsg = new CityAssetMsg(); + outMsg.setBuildingID(msg.getBuildingID()); + outMsg.configure(); + + dispatch = Dispatch.borrow(player, outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + break; + + case REMOVETAX: + targetBuilding = BuildingManager.getBuildingFromCache(msg.getBuildingID()); + + if (targetBuilding == null) + return true; + + targetBuilding.removeTaxes(); + unprotectAsset(targetBuilding, null, origin); + + ManageCityAssetsMsg mca = new ManageCityAssetsMsg(origin.getPlayerCharacter(),targetBuilding); + + // Action TYPE + mca.actionType = 3; + mca.setTargetType(targetBuilding.getObjectType().ordinal()); + mca.setTargetID(targetBuilding.getObjectUUID()); + mca.setTargetType3(targetBuilding.getObjectType().ordinal()); + mca.setTargetID3(targetBuilding.getObjectUUID()); + mca.setAssetName1(targetBuilding.getName()); + mca.setUnknown54(1); + dispatch = Dispatch.borrow(player, mca); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return true; + + case ACCEPTTAX: //AcceptTax + + targetBuilding = BuildingManager.getBuildingFromCache(msg.getBuildingID()); + + if (targetBuilding == null) + return true; + + targetBuilding.acceptTaxes(); + + mca = new ManageCityAssetsMsg(origin.getPlayerCharacter(),targetBuilding); + + // Action TYPE + mca.actionType = 3; + mca.setTargetType(targetBuilding.getObjectType().ordinal()); + mca.setTargetID(targetBuilding.getObjectUUID()); + mca.setTargetType3(targetBuilding.getObjectType().ordinal()); + mca.setTargetID3(targetBuilding.getObjectUUID()); + mca.setAssetName1(targetBuilding.getName()); + mca.setUnknown54(1); + + dispatch = Dispatch.borrow(player, mca); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return true; + } + + dispatch = Dispatch.borrow(player, baseMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + return true; + + } + + private static void protectAsset(AssetSupportMsg msg, Building targetBuilding, NPC vendor, ClientConnection origin) { + + // Member variable declaration + + Zone serverZone; + City serverCity; + ManageNPCMsg outMsg; + int protectionSlots; + Dispatch dispatch; + + // Member variable assignment + + if (targetBuilding == null) + return; + + serverZone = vendor.getParentZone(); + + if (serverZone == null) + return; + + serverCity = City.GetCityFromCache(serverZone.getPlayerCityUUID()); + + if (serverCity == null) + return; + + if (!serverCity.isLocationOnCityZone(targetBuilding.getLoc())){ + ErrorPopupMsg.sendErrorMsg(origin.getPlayerCharacter(), "Structura must be on city zone for to protect."); + return; + } + + if ((serverCity.getTOL() == null)|| (serverCity.getTOL().getRank() < 1)) + return; + + if (serverCity.protectionEnforced == false) { + ErrorPopupMsg.sendErrorMsg(origin.getPlayerCharacter(), "Runemaster can not protect structura during bane!"); + return; + } + + if (serverCity.getRuneMaster() == null) { + ErrorPopupMsg.sendErrorMsg(origin.getPlayerCharacter(), "Runemaster is needed for to protect structura!"); + return; + } + + // Enforce runemaster protection limits + + protectionSlots = (2 * serverCity.getRuneMaster().getRank()) + 6; + + if (serverCity.getRuneMaster().getProtectedBuildings().size() >= + protectionSlots) { + ErrorPopupMsg.sendErrorMsg(origin.getPlayerCharacter(), "Runemaster can only protect " + protectionSlots + " structura!"); + return; + } + + if (msg.getWeeklyTax() != 0){ + if (!serverCity.getTOL().addProtectionTax(targetBuilding, origin.getPlayerCharacter(), TaxType.WEEKLY, msg.getWeeklyTax(), msg.getEnforceKOS() == 1 ? true: false)){ + ErrorPopupMsg.sendErrorMsg(origin.getPlayerCharacter(), "Failed to add taxes to building."); + return; + } + targetBuilding.setProtectionState(Enum.ProtectionState.PENDING); + }else if (msg.getProfitTax() != 0){ + if (!serverCity.getTOL().addProtectionTax(targetBuilding, origin.getPlayerCharacter(), TaxType.PROFIT, msg.getProfitTax(), msg.getEnforceKOS() == 1 ? true: false)){ + ErrorPopupMsg.sendErrorMsg(origin.getPlayerCharacter(), "Failed to add taxes to building."); + return; + } + targetBuilding.setProtectionState(Enum.ProtectionState.PENDING); + }else + targetBuilding.setProtectionState(Enum.ProtectionState.CONTRACT); + + + + outMsg = new ManageNPCMsg(vendor); + dispatch = Dispatch.borrow(origin.getPlayerCharacter(), outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + private static void unprotectAsset(Building targetBuilding, NPC vendor, ClientConnection origin) { + + if (targetBuilding == null) + return; + + // Early exit if UUID < the last database derived building UUID. + + if (targetBuilding.getProtectionState() == Enum.ProtectionState.NPC) { + return; + } + + if (targetBuilding.getProtectionState() == engine.Enum.ProtectionState.NONE) + return; + + if (GuildStatusController.isInnerCouncil(origin.getPlayerCharacter().getGuildStatus()) == false) + return; + + targetBuilding.removeTaxes(); + + targetBuilding.setProtectionState(engine.Enum.ProtectionState.NONE); + + } + +} diff --git a/src/engine/net/client/handlers/BanishUnbanishHandler.java b/src/engine/net/client/handlers/BanishUnbanishHandler.java new file mode 100644 index 00000000..7676373c --- /dev/null +++ b/src/engine/net/client/handlers/BanishUnbanishHandler.java @@ -0,0 +1,129 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.GuildHistoryType; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.guild.BanishUnbanishMsg; +import engine.net.client.msg.guild.GuildListMsg; +import engine.objects.Guild; +import engine.objects.GuildHistory; +import engine.objects.GuildStatusController; +import engine.objects.PlayerCharacter; +import org.joda.time.DateTime; + +public class BanishUnbanishHandler extends AbstractClientMsgHandler { + + public BanishUnbanishHandler() { + super(BanishUnbanishMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + BanishUnbanishMsg msg = (BanishUnbanishMsg) baseMsg; + Dispatch dispatch; + + int target = msg.getTarget(); + PlayerCharacter source = origin.getPlayerCharacter(); + + if(source == null || source.getGuild().isErrant() || source.getGuild().getObjectUUID() == 0) + return true; + + if (GuildStatusController.isGuildLeader(source.getGuildStatus()) == false && GuildStatusController.isInnerCouncil(source.getGuildStatus()) == false) + return true; + + if (source.getObjectUUID() == target) { + ErrorPopupMsg.sendErrorPopup(source, 103); // You may not banish this char + return true; + } + + boolean success = false; + Guild guild = source.getGuild(); + PlayerCharacter realizedTarget = PlayerCharacter.getFromCache(target); + + if(realizedTarget != null) { + // Guild leader can't leave guild. must pass GL or disband + if ( GuildStatusController.isGuildLeader(realizedTarget.getGuildStatus()) == false) { + //ICs cannot banish other ICs + if (!(GuildStatusController.isInnerCouncil(realizedTarget.getGuildStatus()) && GuildStatusController.isGuildLeader(source.getGuildStatus()) == false)) { + success = true; + if (msg.getMsgType() == 1){ + if (!DbManager.GuildQueries.ADD_TO_BANISHED_FROM_GUILDLIST(guild.getObjectUUID(), realizedTarget.getObjectUUID())){ + ChatManager.chatGuildError(source, "Failed To unbanish " + realizedTarget.getName()); + return true; + } + guild.getBanishList().remove(realizedTarget); + } + + + else{ + + if (!DbManager.GuildQueries.ADD_TO_BANISHED_FROM_GUILDLIST(guild.getObjectUUID(), realizedTarget.getObjectUUID())){ + ChatManager.chatGuildError(source, "Failed To Banish " + realizedTarget.getName()); + return true; + } + guild.removePlayer(realizedTarget, GuildHistoryType.BANISHED); + guild.getBanishList().add(realizedTarget); //TODO we might encapsulate this a bit better; also not sure that a list of PC objects is really ideal + } + + } + } + } else { + if (guild.getGuildLeaderUUID() != target) { + PlayerCharacter toBanish = PlayerCharacter.getPlayerCharacter(target); + if (toBanish == null) + return true; + //already added previously. + if (SessionManager.getPlayerCharacterByID(toBanish.getObjectUUID()) != null) + return true; + + if(DbManager.GuildQueries.BANISH_FROM_GUILD_OFFLINE(target, GuildStatusController.isGuildLeader(source.getGuildStatus())) != 0) { + + success = true; + + //Set guild history + + if (DbManager.GuildQueries.ADD_TO_GUILDHISTORY(guild.getObjectUUID(), toBanish, DateTime.now(), GuildHistoryType.BANISHED)){ + GuildHistory guildHistory = new GuildHistory(toBanish.getGuildUUID(),toBanish.getGuild().getName(),DateTime.now(), GuildHistoryType.BANISHED) ; + toBanish.getGuildHistory().add(guildHistory); + } + } + } + } + + + if(success) { + //TODO re enable this once we get unbanish working!!!! + //DbManager.GuildQueries.ADD_TO_BANISHED_FROM_GUILDLIST(guild.getobjectUUID(), target); + + // Send left guild message to rest of guild + String targetName = PlayerCharacter.getFirstName(target); + ChatManager.chatGuildInfo(guild, + targetName + " has been banished from " + guild.getName() + '.'); + GuildListMsg guildListMsg = new GuildListMsg(guild); + dispatch = Dispatch.borrow(source, guildListMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } else { + ErrorPopupMsg.sendErrorPopup(source, 103); // You may not banish this char + } + return true; + + } + +} diff --git a/src/engine/net/client/handlers/BreakFealtyHandler.java b/src/engine/net/client/handlers/BreakFealtyHandler.java new file mode 100644 index 00000000..ba91fbf8 --- /dev/null +++ b/src/engine/net/client/handlers/BreakFealtyHandler.java @@ -0,0 +1,193 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.gameManager.GuildManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.guild.BreakFealtyMsg; +import engine.net.client.msg.guild.SendGuildEntryMsg; +import engine.objects.*; +import engine.server.MBServerStatics; +import engine.session.Session; + +import java.util.ArrayList; + +public class BreakFealtyHandler extends AbstractClientMsgHandler { + + public BreakFealtyHandler() { + super(BreakFealtyMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + BreakFealtyMsg bfm; + PlayerCharacter player; + Guild toBreak; + Guild guild; + Dispatch dispatch; + + bfm = (BreakFealtyMsg) baseMsg; + + // get PlayerCharacter of person accepting invite + + player = SessionManager.getPlayerCharacter( + origin); + + if (player == null) + return true; + + toBreak = (Guild) DbManager.getObject(GameObjectType.Guild, bfm.getGuildUUID()); + + if (toBreak == null) { + ErrorPopupMsg.sendErrorMsg(player, "A Serious error has occured. Please post details for to ensure transaction integrity"); + return true; + } + + guild = player.getGuild(); + + if (guild == null) { + ErrorPopupMsg.sendErrorMsg(player, "You do not belong to a guild!"); + return true; + } + + if (toBreak.isNPCGuild()){ + if (GuildStatusController.isGuildLeader(player.getGuildStatus()) == false) { + ErrorPopupMsg.sendErrorMsg(player, "Only guild leader can break fealty!"); + return true; + } + + + + + if (!DbManager.GuildQueries.UPDATE_PARENT(guild.getObjectUUID(),MBServerStatics.worldUUID)) { + ErrorPopupMsg.sendErrorMsg(player, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + return true; + } + + switch (guild.getGuildState()) { + case Sworn: + guild.setNation(null); + GuildManager.updateAllGuildTags(guild); + GuildManager.updateAllGuildBinds(guild, null); + break; + case Province: + guild.setNation(guild); + GuildManager.updateAllGuildTags(guild); + GuildManager.updateAllGuildBinds(guild, guild.getOwnedCity()); + break; + } + + guild.downgradeGuildState(); + + SendGuildEntryMsg msg = new SendGuildEntryMsg(player); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + //Update Map. + + final Session s = SessionManager.getSession(player); + + City.lastCityUpdate = System.currentTimeMillis(); + + + ArrayList guildMembers = SessionManager.getActivePCsInGuildID(guild.getObjectUUID()); + + for (PlayerCharacter member : guildMembers) { + ChatManager.chatGuildInfo(member, guild.getName() + " has broke fealty from " + toBreak.getName() + '!'); + } + + ArrayList breakFealtyMembers = SessionManager.getActivePCsInGuildID(toBreak.getObjectUUID()); + + for (PlayerCharacter member : breakFealtyMembers) { + ChatManager.chatGuildInfo(member, guild.getName() + " has broken fealty from " + toBreak.getName() + '!'); + } + + return true; + + + } + + if (!toBreak.getSubGuildList().contains(guild)) { + ErrorPopupMsg.sendErrorMsg(player, "Failure to break fealty!"); + return true; + } + + if (GuildStatusController.isGuildLeader(player.getGuildStatus()) == false) { + ErrorPopupMsg.sendErrorMsg(player, "Only guild leader can break fealty!"); + return true; + } + + if (Bane.getBaneByAttackerGuild(guild) != null) + { + ErrorPopupMsg.sendErrorMsg(player, "You may break fealty with active bane!"); + return true; + } + + if (!DbManager.GuildQueries.UPDATE_PARENT(guild.getObjectUUID(),MBServerStatics.worldUUID)) { + ErrorPopupMsg.sendErrorMsg(player, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + return true; + } + + switch (guild.getGuildState()) { + case Sworn: + guild.setNation(null); + GuildManager.updateAllGuildTags(guild); + GuildManager.updateAllGuildBinds(guild, null); + break; + case Province: + guild.setNation(guild); + GuildManager.updateAllGuildTags(guild); + GuildManager.updateAllGuildBinds(guild, guild.getOwnedCity()); + break; + } + + guild.downgradeGuildState(); + toBreak.getSubGuildList().remove(guild); + + if (toBreak.getSubGuildList().isEmpty()) + toBreak.downgradeGuildState(); + + SendGuildEntryMsg msg = new SendGuildEntryMsg(player); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + //Update Map. + + final Session s = SessionManager.getSession(player); + + City.lastCityUpdate = System.currentTimeMillis(); + + + ArrayList guildMembers = SessionManager.getActivePCsInGuildID(guild.getObjectUUID()); + + for (PlayerCharacter member : guildMembers) { + ChatManager.chatGuildInfo(member, guild.getName() + " has broke fealty from " + toBreak.getName() + '!'); + } + + ArrayList breakFealtyMembers = SessionManager.getActivePCsInGuildID(toBreak.getObjectUUID()); + + for (PlayerCharacter member : breakFealtyMembers) { + ChatManager.chatGuildInfo(member, guild.getName() + " has broken fealty from " + toBreak.getName() + '!'); + } + + return true; + } +} diff --git a/src/engine/net/client/handlers/ChangeAltitudeHandler.java b/src/engine/net/client/handlers/ChangeAltitudeHandler.java new file mode 100644 index 00000000..b8b09fb4 --- /dev/null +++ b/src/engine/net/client/handlers/ChangeAltitudeHandler.java @@ -0,0 +1,190 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum.DispatchChannel; +import engine.exception.MsgSendException; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ChangeAltitudeMsg; +import engine.net.client.msg.ClientNetMsg; +import engine.objects.AbstractCharacter; +import engine.objects.Building; +import engine.objects.PlayerCharacter; +import engine.objects.Regions; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +public class ChangeAltitudeHandler extends AbstractClientMsgHandler { + + public ChangeAltitudeHandler() { + super(ChangeAltitudeMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, + ClientConnection origin) throws MsgSendException { + + ChangeAltitudeMsg msg = (ChangeAltitudeMsg) baseMsg; + + PlayerCharacter pc = origin.getPlayerCharacter(); + if (pc == null) { + return false; + } + + if (!AbstractCharacter.CanFly(pc)) + return false; + + if (pc.isSwimming()) + return false; + if (pc.getRegion() != null && !pc.getRegion().isOutside()) + return false; + + + + + + + + // Find out if we already have an altitude timer running and if so + // do not process more alt change requests + + if (pc.getTakeOffTime() != 0) + return false; + + + // remove all movement timers and jobs + //TODO: test if they can fly + + float targetAlt; + float amount = msg.getAmountToMove(); + if (amount != 10 && amount != 60) + return false; + if (pc.getAltitude() == 60 && msg.up()) + return true; + if (pc.getAltitude() == 0 && !msg.up()) + return true; + + pc.update(); + pc.stopMovement(pc.getLoc()); + msg.setStartAlt(pc.getAltitude()); + if (msg.up()) { + + pc.landingRegion = null; + if (pc.getAltitude() == 0){ + Regions upRegion = pc.getRegion(); + if (upRegion != null){ + float startAlt = 0; + Building regionBuilding = Regions.GetBuildingForRegion(upRegion); + if (upRegion != null) + startAlt = upRegion.lerpY(pc) - regionBuilding.getLoc().y; + float rounded = startAlt *.10f; + + rounded = ((int)rounded) * 10; + + if (rounded < 0) + rounded = 0; + + + msg.setStartAlt(startAlt); + targetAlt = rounded + amount; + + if (targetAlt > 60) + targetAlt = 60; + + pc.setAltitude(startAlt); + pc.setDesiredAltitude(targetAlt); + }else{ + msg.setStartAlt(pc.getAltitude()); + targetAlt = pc.getAltitude() + amount; + if (targetAlt > 60) + targetAlt = 60; + } + }else{ + msg.setStartAlt(pc.getAltitude()); + + targetAlt = pc.getAltitude() + amount; + if (targetAlt > 60) + targetAlt = 60; + } + + + } else { + msg.setStartAlt(pc.getAltitude()); + targetAlt = pc.getAltitude() - amount; + if (targetAlt < 0) + targetAlt = 0; + } + msg.setTargetAlt(targetAlt); + if (pc.getAltitude() < 1 && targetAlt > pc.getAltitude()) { + // char is on the ground and is about to start flight + if (pc.getStamina() < 10) { + return false; + } + } + + if (MBServerStatics.MOVEMENT_SYNC_DEBUG) { + Logger.info ("Changing Altitude, moving=" + pc.isMoving() + + " Current Loc " + pc.getLoc().getX() + ' ' + pc.getLoc().getZ() + + " End Loc " + pc.getEndLoc().getX() + ' ' + pc.getEndLoc().getZ()); + } + + if (msg.up()){ + pc.update(); + pc.setDesiredAltitude(targetAlt); + pc.setTakeOffTime(System.currentTimeMillis()); + } + + else{ + Regions region = PlayerCharacter.InsideBuildingRegionGoingDown(pc); + + + + if (region != null){ + float landingAltitude = 0; + Building building = Regions.GetBuildingForRegion(region); + if (building != null) + landingAltitude = region.lerpY(pc) - building.getLoc().y; + + if (landingAltitude >= targetAlt){ + pc.landingRegion = region; + pc.landingAltitude = landingAltitude; + pc.setDesiredAltitude(landingAltitude); + }else{ + pc.landingRegion = null; + pc.setDesiredAltitude(targetAlt); + } + + + }else + pc.setDesiredAltitude(targetAlt); + + pc.update(); + + + pc.setTakeOffTime(System.currentTimeMillis()); + } + + + + // Add timer for height change cooldown, this also tells getLoc we are not moving + //MovementManager.addChangeAltitudeTimer(pc, msg.getStartAlt(), msg.getTargetAlt(), (int)((MBServerStatics.HEIGHT_CHANGE_TIMER_MS * amount) + 100 ) ); + + // Add flight timer job to check stam and ground you when you run out + //MovementManager.addFlightTimer(pc, msg, MBServerStatics.FLY_FREQUENCY_MS); + //Send change altitude to all in range + DispatchMessage.dispatchMsgToInterestArea(pc, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + + + + return true; + } + +} diff --git a/src/engine/net/client/handlers/ChangeGuildLeaderHandler.java b/src/engine/net/client/handlers/ChangeGuildLeaderHandler.java new file mode 100644 index 00000000..585f28a5 --- /dev/null +++ b/src/engine/net/client/handlers/ChangeGuildLeaderHandler.java @@ -0,0 +1,147 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ChangeGuildLeaderMsg; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.guild.GuildInfoMsg; +import engine.net.client.msg.guild.GuildListMsg; +import engine.objects.City; +import engine.objects.Guild; +import engine.objects.GuildStatusController; +import engine.objects.PlayerCharacter; + +public class ChangeGuildLeaderHandler extends AbstractClientMsgHandler { + + public ChangeGuildLeaderHandler() { + super(ChangeGuildLeaderMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + ChangeGuildLeaderMsg msg; + PlayerCharacter sourcePlayer; + PlayerCharacter targetPlayer; + City city; + + msg = (ChangeGuildLeaderMsg) baseMsg; + sourcePlayer = origin.getPlayerCharacter(); + if (sourcePlayer == null) + return true; + if (GuildStatusController.isGuildLeader(sourcePlayer.getGuildStatus()) == false) + return true; + + + + Guild glGuild = sourcePlayer.getGuild(); + + if (glGuild == null) + return true; + + if (!glGuild.isGuildLeader(sourcePlayer.getObjectUUID())) + return true; + targetPlayer = (PlayerCharacter) DbManager.getObject(GameObjectType.PlayerCharacter, msg.getTargetID()); + + + if (targetPlayer == null) + return true; + + if (GuildStatusController.isGuildLeader(targetPlayer.getGuildStatus())) + return true; + + if (!Guild.sameGuild(sourcePlayer.getGuild(),targetPlayer.getGuild())) + return false; + + + //updateSource will generate a new promote/demote screen for sourcePlayer + //updateTarget will sync guild info for the target and all players in range + + + + + String targetName = null; + boolean isMale = true; + boolean updateTarget; + + Enum.GuildType t = Enum.GuildType.getGuildTypeFromInt(sourcePlayer.getGuild().getCharter()); + + + if (!DbManager.GuildQueries.SET_GUILD_LEADER(targetPlayer.getObjectUUID(), glGuild.getObjectUUID())){ + ChatManager.chatGuildError(sourcePlayer, "Failed to change guild leader!"); + return false; + } + + glGuild.setGuildLeader(targetPlayer); + + + + if (glGuild.getOwnedCity() != null){ + city = glGuild.getOwnedCity(); + if (!city.transferGuildLeader(targetPlayer)){ + ChatManager.chatGuildError(sourcePlayer, "Failed to Transfer City Objects. Contact CCR!"); + return false; + } + + } + + + + targetPlayer.setGuildLeader(true); + targetPlayer.setInnerCouncil(true); + targetPlayer.setFullMember(true); + targetPlayer.incVer(); + targetName = targetPlayer.getFirstName(); + updateTarget = true; + + + ChatManager.chatGuildInfo(sourcePlayer.getGuild(), + targetName + " has been promoted to " + + "guild leader!"); + + //These values record a change, not the new value... + + + + //updateOldGuildLeader + sourcePlayer.setInnerCouncil(true); + sourcePlayer.setFullMember(true); + sourcePlayer.setGuildLeader(false); + sourcePlayer.incVer(); + + GuildInfoMsg guildInfoMsg = new GuildInfoMsg(sourcePlayer, sourcePlayer.getGuild(), 2); + Dispatch dispatch = Dispatch.borrow(sourcePlayer, guildInfoMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + + GuildListMsg guildListMsg = new GuildListMsg(sourcePlayer.getGuild()); + + dispatch = Dispatch.borrow(sourcePlayer, guildListMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + + + + if (targetPlayer != null) { + if (updateTarget) + DispatchMessage.sendToAllInRange(targetPlayer, new GuildInfoMsg(targetPlayer, targetPlayer.getGuild(), 2)); + } + + return true; + + } + +} diff --git a/src/engine/net/client/handlers/ChangeRankHandler.java b/src/engine/net/client/handlers/ChangeRankHandler.java new file mode 100644 index 00000000..cc0de5ca --- /dev/null +++ b/src/engine/net/client/handlers/ChangeRankHandler.java @@ -0,0 +1,176 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.guild.ChangeRankMsg; +import engine.net.client.msg.guild.GuildInfoMsg; +import engine.net.client.msg.guild.GuildListMsg; +import engine.objects.GuildStatusController; +import engine.objects.PlayerCharacter; + +public class ChangeRankHandler extends AbstractClientMsgHandler { + + public ChangeRankHandler() { + super(ChangeRankMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + ChangeRankMsg msg; + PlayerCharacter sourcePlayer; + PlayerCharacter targetPlayer; + + msg = (ChangeRankMsg) baseMsg; + sourcePlayer = origin.getPlayerCharacter(); + + targetPlayer = (PlayerCharacter) DbManager.getObject(GameObjectType.PlayerCharacter, msg.getPlayerUUID()); + + if (msg.getPlayerUUID() == 0) + targetPlayer = sourcePlayer; + + //updateSource will generate a new promote/demote screen for sourcePlayer + //updateTarget will sync guild info for the target and all players in range + + boolean updateSource = false, updateTarget = false; + + if (sourcePlayer == null ||GuildStatusController.isInnerCouncil(sourcePlayer.getGuildStatus()) == false) + return true; + + if (targetPlayer != null && (targetPlayer.getGuild().equals(sourcePlayer.getGuild()) == false)) + return true; + + String targetName = null; + boolean isMale; + + if (msg.getPreviousRank() != msg.getNewRank()) { + Enum.GuildType t = Enum.GuildType.getGuildTypeFromInt(sourcePlayer.getGuild().getCharter()); + + if (targetPlayer != null) { + targetPlayer.setGuildTitle(msg.getNewRank()); + + targetName = targetPlayer.getFirstName(); + isMale = targetPlayer.getRace().getRaceType().getCharacterSex().equals(Enum.CharacterSex.MALE); + } else { + DbManager.GuildQueries.UPDATE_GUILD_RANK_OFFLINE(msg.getPlayerUUID(), msg.getNewRank(), sourcePlayer.getGuild().getObjectUUID()); + + targetName = PlayerCharacter.getFirstName(msg.getPlayerUUID()); + isMale = true; + } + + ChatManager.chatGuildInfo(sourcePlayer.getGuild(), + targetName + " has been " + + ((msg.getNewRank() > msg.getPreviousRank()) ? "pro" : "de") + "moted to " + + t.getRankForGender(msg.getNewRank(), isMale) + '!'); + updateSource = true; + } + + //These values record a change, not the new value... + boolean icUpdate = false, recruitUpdate = false, taxUpdate = false; + + //Handle the offline case.. + if (targetPlayer == null) { + int updateMask = DbManager.GuildQueries.UPDATE_GUILD_STATUS_OFFLINE(msg.getPlayerUUID(), + msg.getIc() > 0, + msg.getRec() > 0, + msg.getTax() > 0, + sourcePlayer.getGuild().getObjectUUID()); + + //These values come from the updateIsStatusOffline function + icUpdate = (updateMask & 4) > 0; + recruitUpdate = (updateMask & 2) > 0; + taxUpdate = (updateMask & 1) > 0; + + if (targetName == null && updateMask > 0) + targetName = PlayerCharacter.getFirstName(msg.getPlayerUUID()); + } else { + icUpdate = (GuildStatusController.isInnerCouncil(targetPlayer.getGuildStatus()) != (msg.getIc() > 0)) && GuildStatusController.isGuildLeader(sourcePlayer.getGuildStatus()); + recruitUpdate = (GuildStatusController.isRecruiter(targetPlayer.getGuildStatus()) != (msg.getRec() > 0)) && GuildStatusController.isGuildLeader(sourcePlayer.getGuildStatus()); + taxUpdate = (GuildStatusController.isTaxCollector(targetPlayer.getGuildStatus()) != (msg.getTax() > 0)) && GuildStatusController.isGuildLeader(sourcePlayer.getGuildStatus()); + + //This logic branch only executes if targetPlayer has passed a null check... + if (icUpdate){ + targetPlayer.setInnerCouncil(msg.getIc() > 0); + targetPlayer.setFullMember(true); + targetPlayer.incVer(); + } + + if (recruitUpdate) + targetPlayer.setRecruiter(msg.getRec() > 0); + + if (taxUpdate) + targetPlayer.setTaxCollector(msg.getTax() > 0); + + if (targetName == null) + targetName = targetPlayer.getFirstName(); + } + + if (icUpdate) { + ChatManager.chatGuildInfo(sourcePlayer.getGuild(), + (msg.getIc() > 0) + ? targetName + " has been appointed to the inner council." + : targetName + " is no longer a member of the inner council."); + + updateSource = true; + updateTarget = true; + } + + if (recruitUpdate) { + updateSource = true; + updateTarget = true; + } + + if (taxUpdate) { + updateSource = true; + updateTarget = true; + } + + if (targetPlayer != null) { + targetPlayer.incVer(); + if (updateTarget) + DispatchMessage.sendToAllInRange(targetPlayer, new GuildInfoMsg(targetPlayer, targetPlayer.getGuild(), 2)); + } + + if (updateSource) { + + Dispatch dispatch = Dispatch.borrow(sourcePlayer, new GuildInfoMsg(sourcePlayer, sourcePlayer.getGuild(), 2)); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + + dispatch = Dispatch.borrow(sourcePlayer, new GuildListMsg(sourcePlayer.getGuild())); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + + } + + return true; + + } + +} diff --git a/src/engine/net/client/handlers/ChannelMuteMsgHandler.java b/src/engine/net/client/handlers/ChannelMuteMsgHandler.java new file mode 100644 index 00000000..ba6480d8 --- /dev/null +++ b/src/engine/net/client/handlers/ChannelMuteMsgHandler.java @@ -0,0 +1,123 @@ +package engine.net.client.handlers; + +import engine.Enum.BuildingGroup; +import engine.Enum.GuildState; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.gameManager.GuildManager; +import engine.gameManager.ZoneManager; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ChatFilterMsg; +import engine.net.client.msg.ClientNetMsg; +import engine.objects.*; +import org.pmw.tinylog.Logger; + +/* + * @Author: + * @Summary: Processes application protocol message which processes + * client requests to abandon a building. + */ +public class ChannelMuteMsgHandler extends AbstractClientMsgHandler { + + // Instance variables + + public ChannelMuteMsgHandler() { + super(ChatFilterMsg.class); + + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + return true; + } + + private static void AbandonSingleAsset(PlayerCharacter sourcePlayer, + Building targetBuilding) { + + // Transfer the building asset ownership and refresh all clients + + DbManager.BuildingQueries.CLEAR_FRIENDS_LIST(targetBuilding.getObjectUUID()); + targetBuilding.getFriends().clear(); + + DbManager.BuildingQueries.CLEAR_CONDEMNED_LIST(targetBuilding.getObjectUUID()); + targetBuilding.getCondemned().clear(); + targetBuilding.setOwner(null); + targetBuilding.refreshGuild(); + } + + private void AbandonAllCityObjects(PlayerCharacter sourcePlayer, + Building targetBuilding) { + Guild sourceGuild; + Zone cityZone; + + sourceGuild = sourcePlayer.getGuild(); + + if (sourceGuild == null) + return; + + if (sourceGuild.getSubGuildList().size() > 0) { + ChatManager.chatCityError(sourcePlayer, "You Cannot abandon a nation city."); + return; + } + + cityZone = ZoneManager.findSmallestZone(targetBuilding.getLoc()); + + // Can't abandon a tree not within a player city zone + if (cityZone.isPlayerCity() == false) + return; + + if (targetBuilding.getCity().hasBeenTransfered == true) { + ChatManager.chatCityError(sourcePlayer, "City can only be abandoned once per rebooting."); + return; + } + + // Guild no longer owns his tree. + if (!DbManager.GuildQueries.SET_GUILD_OWNED_CITY(sourceGuild.getObjectUUID(), 0)) { + Logger.error( "Failed to update Owned City to Database"); + return; + } + + sourceGuild.setCityUUID(0); + sourceGuild.setGuildState(GuildState.Errant); + sourceGuild.setNation(null); + + // Transfer the city assets + TransferCityAssets(sourcePlayer, targetBuilding); + + GuildManager.updateAllGuildTags(sourceGuild); + GuildManager.updateAllGuildBinds(sourceGuild, null); + + } + + private void TransferCityAssets(PlayerCharacter sourcePlayer, + Building cityTOL) { + + Zone cityZone; + + // Build list of buildings within this parent zone + cityZone = ZoneManager.findSmallestZone(cityTOL.getLoc()); + + for (Building cityBuilding : cityZone.zoneBuildingSet) { + + Blueprint cityBlueprint; + cityBlueprint = cityBuilding.getBlueprint(); + + // Buildings without blueprints cannot be abandoned + if (cityBlueprint == null) + continue; + + // Transfer ownership of valid city assets + if ((cityBlueprint.getBuildingGroup() == BuildingGroup.TOL) + || (cityBlueprint.getBuildingGroup() == BuildingGroup.SPIRE) + || (cityBlueprint.getBuildingGroup() == BuildingGroup.BARRACK) + || (cityBlueprint.isWallPiece()) + || (cityBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.SHRINE)) + AbandonSingleAsset(sourcePlayer, cityBuilding); + + } + + } + +} diff --git a/src/engine/net/client/handlers/CityChoiceMsgHandler.java b/src/engine/net/client/handlers/CityChoiceMsgHandler.java new file mode 100644 index 00000000..7dd79a04 --- /dev/null +++ b/src/engine/net/client/handlers/CityChoiceMsgHandler.java @@ -0,0 +1,69 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.DispatchChannel; +import engine.exception.MsgSendException; +import engine.gameManager.GuildManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.CityChoiceMsg; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.TeleportRepledgeListMsg; +import engine.objects.City; +import engine.objects.Guild; +import engine.objects.PlayerCharacter; + +/* + * @Author: + * @Summary: Processes application protocol message which keeps + * client's tcp connection open. + */ + +public class CityChoiceMsgHandler extends AbstractClientMsgHandler { + + public CityChoiceMsgHandler() { + super(CityChoiceMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter player = origin.getPlayerCharacter(); + Dispatch dispatch; + + CityChoiceMsg msg = (CityChoiceMsg) baseMsg; + + if (player == null) + return true; + + switch (msg.getMsgType()) { + case 5: + TeleportRepledgeListMsg trlm = new TeleportRepledgeListMsg(player, false); + trlm.configure(); + dispatch = Dispatch.borrow(player, trlm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + break; + case 3: + City city = City.getCity(msg.getCityID()); + + if (city == null) + return true; + + Guild cityGuild = city.getGuild(); + + if (cityGuild == null) + return true; + + if (player.getLevel() < cityGuild.getRepledgeMin() || player.getLevel() > cityGuild.getRepledgeMax()) + return true; + + //if repledge, reguild the player but set his building now. + + GuildManager.joinGuild(player, cityGuild, city.getObjectUUID(), Enum.GuildHistoryType.JOIN); + break; + } + + return true; + } +} \ No newline at end of file diff --git a/src/engine/net/client/handlers/ClaimAssetMsgHandler.java b/src/engine/net/client/handlers/ClaimAssetMsgHandler.java new file mode 100644 index 00000000..50ffb9d6 --- /dev/null +++ b/src/engine/net/client/handlers/ClaimAssetMsgHandler.java @@ -0,0 +1,139 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.BuildingGroup; +import engine.db.archive.CityRecord; +import engine.db.archive.DataWarehouse; +import engine.exception.MsgSendException; +import engine.gameManager.BuildingManager; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClaimAssetMsg; +import engine.net.client.msg.ClientNetMsg; +import engine.objects.Blueprint; +import engine.objects.Building; +import engine.objects.PlayerCharacter; +import org.pmw.tinylog.Logger; + +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import static engine.math.FastMath.sqr; + +/* + * @Author: + * @Summary: Processes application protocol message which keeps + * client's tcp connection open. + */ +public class ClaimAssetMsgHandler extends AbstractClientMsgHandler { + + // Instance variables + + private final ReentrantReadWriteLock claimLock = new ReentrantReadWriteLock(); + + public ClaimAssetMsgHandler() { + super(ClaimAssetMsg.class); + + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + // Member variable declaration + this.claimLock.writeLock().lock(); + + try{ + PlayerCharacter sourcePlayer; + Building targetBuilding; + Blueprint blueprint; + ClaimAssetMsg msg; + int targetUUID; + + msg = (ClaimAssetMsg) baseMsg; + targetUUID = msg.getUUID(); + + sourcePlayer = origin.getPlayerCharacter(); + targetBuilding = BuildingManager.getBuildingFromCache(targetUUID); + + // Oops! *** Refactor: Log error + + if ((sourcePlayer == null) || + (targetBuilding == null)) + return true; + + // Player must be reasonably close to building in order to claim + + if (sourcePlayer.getLoc().distanceSquared2D(targetBuilding.getLoc()) > sqr(100)) + return true; + + // Early exit if object to be claimed is not errant + + if (targetBuilding.getOwnerUUID() != 0) + return true; + + + // Early exit if UUID < the last database derived building UUID. + + if (targetBuilding.getProtectionState() == Enum.ProtectionState.NPC) { + return true; + } + + // Early exit if claiming player does not + // have a guild. + + // Errant players cannot claim + + if (sourcePlayer.getGuild().isErrant()) + return true; + + // Can't claim an object without a blueprint + + if (targetBuilding.getBlueprintUUID() == 0) + return true; + + blueprint = targetBuilding.getBlueprint(); + + //cant claim mine this way. + if (blueprint.getBuildingGroup() == BuildingGroup.MINE) + return true; + + // Players cannot claim shrines + + if ((targetBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.SHRINE)) + return true; + + // Can't claim a tree if your guild already owns one + // *** Refactor : Send error to player here + + if ((sourcePlayer.getGuild().isNation()) && + (blueprint.getBuildingGroup() == BuildingGroup.TOL)) + return true; + + // Process the transfer of the building(s) + + if (blueprint.getBuildingGroup() == BuildingGroup.TOL) { + targetBuilding.getCity().claim(sourcePlayer); + + // Push transfer of city to data warehouse + CityRecord cityRecord = CityRecord.borrow(targetBuilding.getCity(), Enum.RecordEventType.TRANSFER); + DataWarehouse.pushToWarehouse(cityRecord); + + } else + targetBuilding.claim(sourcePlayer); + + } catch(Exception e){ + Logger.error("ClaimAssetMsgHandler", e.getMessage()); + }finally{ + this.claimLock.writeLock().unlock(); + } + return true; + } + +} \ No newline at end of file diff --git a/src/engine/net/client/handlers/ClaimGuildTreeMsgHandler.java b/src/engine/net/client/handlers/ClaimGuildTreeMsgHandler.java new file mode 100644 index 00000000..639a984c --- /dev/null +++ b/src/engine/net/client/handlers/ClaimGuildTreeMsgHandler.java @@ -0,0 +1,191 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.BuildingGroup; +import engine.exception.MsgSendException; +import engine.gameManager.BuildingManager; +import engine.gameManager.ChatManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.*; +import engine.objects.*; +import org.pmw.tinylog.Logger; + +import java.util.concurrent.locks.ReentrantReadWriteLock; + + +/* + * @Author: + * @Summary: Processes application protocol message which keeps + * client's tcp connection open. + */ +public class ClaimGuildTreeMsgHandler extends AbstractClientMsgHandler { + + // Instance variables + + private final ReentrantReadWriteLock claimLock = new ReentrantReadWriteLock(); + private static final int RENAME_TREE = 2; + private static final int BIND_TREE = 3; + private static final int OPEN_CITY = 4; + private static final int CLOSE_CITY = 5; + + public ClaimGuildTreeMsgHandler() { + super(ClaimGuildTreeMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + // Member variable declaration + this.claimLock.writeLock().lock(); + + try{ + PlayerCharacter sourcePlayer; + Building building; + Blueprint blueprint; + Zone playerZone= null; + City playerCity= null; + ClaimGuildTreeMsg msg; + int targetUUID; + Dispatch dispatch; + + msg = (ClaimGuildTreeMsg) baseMsg; + targetUUID = msg.getTargetID(); + + sourcePlayer = origin.getPlayerCharacter(); + building = BuildingManager.getBuildingFromCache(targetUUID); + + if (building != null) + playerZone = building.getParentZone(); + + if (playerZone != null) + playerCity = City.getCity(playerZone.getPlayerCityUUID()); + + // Oops! *** Refactor: Log error + switch (msg.getMessageType()){ + case RENAME_TREE: + if ((sourcePlayer == null) || + (building == null) || playerZone == null ||playerCity == null) + return true; + + // Early exit if object to be claimed is not errant + + if (building.getOwnerUUID() == 0) + return true; + + // Errant players cannot rename + + if (sourcePlayer.getGuild().isErrant()) + return true; + + // Can't rename an object without a blueprint + + if (building.getBlueprintUUID() == 0) + return true; + + blueprint = building.getBlueprint(); + + //can only rename tree this way. + if (blueprint.getBuildingGroup() != BuildingGroup.TOL) + return true; + + //dont rename if guild is null + if (building.getGuild().isErrant()) + return true; + + if (!ManageCityAssetMsgHandler.playerCanManageNotFriends(sourcePlayer, building)) + return true; + + + if (!playerCity.renameCity(msg.getTreeName())){ + ChatManager.chatSystemError(sourcePlayer, "Failed to rename city!"); + return true; + } + + GuildTreeStatusMsg gtsm = new GuildTreeStatusMsg(building,sourcePlayer); + gtsm.configure(); + dispatch = Dispatch.borrow(sourcePlayer, gtsm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + CityZoneMsg czm = new CityZoneMsg(2,playerZone.getLoc().x, playerZone.getLoc().y, playerZone.getLoc().z, playerCity.getCityName(),playerZone, Enum.CityBoundsType.ZONE.extents, Enum.CityBoundsType.ZONE.extents); + DispatchMessage.dispatchMsgToAll(czm); + + break; + case BIND_TREE: + + Guild pcGuild = sourcePlayer.getGuild(); + + //test tree is valid for binding, same guild or same nation + if (!Guild.sameNation(pcGuild, building.getGuild())) { + + return true; + } + + if (building.getGuild().isErrant()) + return true; + + + //get bind city + Zone zone =building.getParentZone(); + + if (zone == null) { + ErrorPopupMsg.sendErrorMsg(sourcePlayer, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + return true; + } + + if (playerCity == null && building.getGuild() != null) + playerCity = building.getGuild().getOwnedCity(); + + if (playerCity == null) + return true; + + + sourcePlayer.setBindBuildingID(building.getObjectUUID()); + dispatch = Dispatch.borrow(sourcePlayer, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + break; + case OPEN_CITY: + case CLOSE_CITY: + if ((sourcePlayer == null) || + (building == null) || playerZone == null ||playerCity == null) + return true; + + if (!ManageCityAssetMsgHandler.playerCanManageNotFriends(sourcePlayer, building)) + return true; + + boolean open = (msg.getMessageType() == OPEN_CITY); + + if (!playerCity.openCity(open)){ + ErrorPopupMsg.sendErrorMsg(sourcePlayer, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + return true; + } + + dispatch = Dispatch.borrow(sourcePlayer, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + break; + default: + break; + } + + } catch(Exception e){ + Logger.error( e.getMessage()); + }finally{ + try{ + this.claimLock.writeLock().unlock(); + }catch(Exception e){ + Logger.info("failClaimsync"); + } + } + return true; + } +} \ No newline at end of file diff --git a/src/engine/net/client/handlers/DestroyBuildingHandler.java b/src/engine/net/client/handlers/DestroyBuildingHandler.java new file mode 100644 index 00000000..7e53d1be --- /dev/null +++ b/src/engine/net/client/handlers/DestroyBuildingHandler.java @@ -0,0 +1,95 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.BuildingGroup; +import engine.InterestManagement.WorldGrid; +import engine.exception.MsgSendException; +import engine.gameManager.BuildingManager; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.DestroyBuildingMsg; +import engine.objects.Blueprint; +import engine.objects.Building; +import engine.objects.City; +import engine.objects.PlayerCharacter; + +/* + * @Author: + * @Summary: Processes application protocol message where the + * client is requesting a building be destroyed. + */ + +public class DestroyBuildingHandler extends AbstractClientMsgHandler { + + public DestroyBuildingHandler() { + super(DestroyBuildingMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter pc = origin.getPlayerCharacter(); + + DestroyBuildingMsg msg; + msg = (DestroyBuildingMsg) baseMsg; + + int buildingUUID = msg.getUUID(); + + Building building = BuildingManager.getBuildingFromCache(buildingUUID); +; + if (pc == null || building == null) + return true; + + Blueprint blueprint; + + blueprint = building.getBlueprint(); + + // Can't destroy buildings without a blueprint. + + if (blueprint == null) + return true; + + // Cannot destroy Oblivion database derived buildings. + + if (building.getProtectionState() == Enum.ProtectionState.NPC) { + return true; + } + + if (!BuildingManager.PlayerCanControlNotOwner(building, pc)) + return true; + + // Can't destroy a tree of life + if (blueprint.getBuildingGroup() == BuildingGroup.TOL) + return true; + + // Can't destroy a shrine + if (blueprint.getBuildingGroup() == BuildingGroup.SHRINE) + return true; + + if (blueprint.getBuildingGroup() == BuildingGroup.MINE) + return true; + + if (blueprint.getBuildingGroup() == BuildingGroup.RUNEGATE) + return true; + + // Turn off spire if destoying + if (blueprint.getBuildingGroup() == BuildingGroup.SPIRE) + building.disableSpire(true); + + if (blueprint.getBuildingGroup() == BuildingGroup.WAREHOUSE) { + + City city = building.getCity(); + + if (city != null) + city.setWarehouseBuildingID(0); + } + + building.setRank(-1); + WorldGrid.RemoveWorldObject(building); + WorldGrid.removeObject(building); + building.getParentZone().zoneBuildingSet.remove(building); + + return true; + } + +} \ No newline at end of file diff --git a/src/engine/net/client/handlers/DisbandGroupHandler.java b/src/engine/net/client/handlers/DisbandGroupHandler.java new file mode 100644 index 00000000..5a749f50 --- /dev/null +++ b/src/engine/net/client/handlers/DisbandGroupHandler.java @@ -0,0 +1,56 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.exception.MsgSendException; +import engine.gameManager.GroupManager; +import engine.gameManager.SessionManager; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.group.DisbandGroupMsg; +import engine.net.client.msg.group.GroupUpdateMsg; +import engine.objects.Group; +import engine.objects.PlayerCharacter; + +public class DisbandGroupHandler extends AbstractClientMsgHandler { + + public DisbandGroupHandler() { + super(DisbandGroupMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter source = SessionManager.getPlayerCharacter(origin); + if (source == null) { + return false; + } + Group group = GroupManager.getGroup(source); + if (group == null) { + return false; + } + if (group.getGroupLead() != source) { + return false; + } + + // Clear all group member lists + GroupUpdateMsg gim = new GroupUpdateMsg(); + gim.setGroup(group); + gim.setMessageType(4); + + // send the disbanded popup to everyone in group + group.sendUpdate(gim); + + // cleanup group + GroupManager.deleteGroup(group); + return true; + + } +} diff --git a/src/engine/net/client/handlers/DisbandGuildHandler.java b/src/engine/net/client/handlers/DisbandGuildHandler.java new file mode 100644 index 00000000..a36e88ca --- /dev/null +++ b/src/engine/net/client/handlers/DisbandGuildHandler.java @@ -0,0 +1,121 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.GuildHistoryType; +import engine.InterestManagement.WorldGrid; +import engine.db.archive.DataWarehouse; +import engine.db.archive.GuildRecord; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.guild.DisbandGuildMsg; +import engine.net.client.msg.guild.LeaveGuildMsg; +import engine.objects.Bane; +import engine.objects.Guild; +import engine.objects.GuildStatusController; +import engine.objects.PlayerCharacter; + +public class DisbandGuildHandler extends AbstractClientMsgHandler { + + public DisbandGuildHandler() { + super(DisbandGuildMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter player; + Guild guild; + Dispatch dispatch; + + player = SessionManager.getPlayerCharacter(origin); + + //don't allow non guild leaders to disband guild. + + if (player == null || GuildStatusController.isGuildLeader(player.getGuildStatus()) == false) + return true; + + guild = player.getGuild(); + + if (guild == null || guild.isErrant()) + return true; + + // Don't allow disbanding if a city is owned + // *** Refactor: We should allow this by abandoning the tree first. + + if (guild.getOwnedCity() != null) { + ErrorPopupMsg.sendErrorMsg(player, "You cannot disband a soverign guild!"); + return true; + } + + Bane guildBane = Bane.getBaneByAttackerGuild(guild); + + if (guildBane != null) { + ErrorPopupMsg.sendErrorMsg(player, "You cannot disband a guild with an active bane!"); + return true; + } + + if (guild.getSubGuildList().size() > 0) { + ErrorPopupMsg.sendErrorMsg(player, "You cannot disband a nation!"); + return true; + } + + // Send message to guild (before kicking everyone out of it) + + ChatManager.chatGuildInfo(guild, guild.getName() + " has been disbanded!"); + + // Log event to data warehous + + GuildRecord guildRecord = GuildRecord.borrow(guild, Enum.RecordEventType.DISBAND); + DataWarehouse.pushToWarehouse(guildRecord); + + // Remove us as a subguild of our nation + + if (guild.getNation() != null && Guild.sameGuild(guild, guild.getNation()) == false && guild.getNation().isErrant() == false) + guild.getNation().removeSubGuild(guild); + + // Update all online guild players + + for (PlayerCharacter pcs : Guild.GuildRoster(guild)) { + + guild.removePlayer(pcs,GuildHistoryType.DISBAND); + } + + //Save Guild data + + player.setGuildLeader(false); + player.setInnerCouncil(false); + guild.setGuildLeaderUUID(0); + guild.setNation(null); + + DbManager.GuildQueries.DELETE_GUILD(guild); + + DbManager.removeFromCache(guild); + WorldGrid.removeObject(guild, player); + + // Send message back to client + + LeaveGuildMsg leaveGuildMsg = new LeaveGuildMsg(); + leaveGuildMsg.setMessage("You guild has been disbanded!"); + dispatch = Dispatch.borrow(player, leaveGuildMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + return true; + } + +} diff --git a/src/engine/net/client/handlers/DismissGuildHandler.java b/src/engine/net/client/handlers/DismissGuildHandler.java new file mode 100644 index 00000000..57631c5f --- /dev/null +++ b/src/engine/net/client/handlers/DismissGuildHandler.java @@ -0,0 +1,147 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.gameManager.GuildManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.guild.DismissGuildMsg; +import engine.net.client.msg.guild.SendGuildEntryMsg; +import engine.objects.*; +import engine.server.MBServerStatics; +import engine.session.Session; + +import java.util.ArrayList; + +public class DismissGuildHandler extends AbstractClientMsgHandler { + + public DismissGuildHandler() { + super(DismissGuildMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + DismissGuildMsg dismissMsg; + PlayerCharacter player; + Guild toDismiss; + Guild nation; + Dispatch dispatch; + + dismissMsg = (DismissGuildMsg) baseMsg; + + player = SessionManager.getPlayerCharacter(origin); + + if (player == null) + return true; + + toDismiss = (Guild) DbManager.getObject(GameObjectType.Guild, dismissMsg.getGuildID()); + + if (toDismiss == null) { + ErrorPopupMsg.sendErrorMsg(player, "A Serious error has occured. Please post details for to ensure transaction integrity"); + return true; + } + + nation = player.getGuild(); + + if (nation == null) { + ErrorPopupMsg.sendErrorMsg(player, "Nothing to disband, your guild is not a nation!"); + return true; + } + + if (!nation.getSubGuildList().contains(toDismiss)) { + ErrorPopupMsg.sendErrorMsg(player, "You do not have authority to dismiss this guild!"); + return true; + } + + if (GuildStatusController.isGuildLeader(player.getGuildStatus()) == false) { + ErrorPopupMsg.sendErrorMsg(player, "Only a guild leader can dismiss a subguild!"); + return true; + } + + // Restriction on active bane desubbing + + if (Bane.getBaneByAttackerGuild(toDismiss) != null) + { + ErrorPopupMsg.sendErrorMsg(player, "You may not dismiss subguild with active bane!"); + return true; + } + + switch (toDismiss.getGuildState()) { + case Sworn: + + if (!DbManager.GuildQueries.UPDATE_PARENT(toDismiss.getObjectUUID(), MBServerStatics.worldUUID)) { + ErrorPopupMsg.sendErrorMsg(player, "A Serious error has occured. Please post details for to ensure transaction integrity"); + return true; + } + nation.getSubGuildList().remove(toDismiss); + toDismiss.downgradeGuildState(); + toDismiss.setNation(null); + GuildManager.updateAllGuildBinds(toDismiss, null); + + break; + case Province: + if (!DbManager.GuildQueries.UPDATE_PARENT(toDismiss.getObjectUUID(),MBServerStatics.worldUUID)) { + ErrorPopupMsg.sendErrorMsg(player, "A Serious error has occured. Please post details for to ensure transaction integrity"); + return true; + } + nation.getSubGuildList().remove(toDismiss); + toDismiss.downgradeGuildState(); + toDismiss.setNation(toDismiss); + + break; + case Petitioner: + nation.getSubGuildList().remove(toDismiss); + toDismiss.downgradeGuildState(); + break; + case Protectorate: + nation.getSubGuildList().remove(toDismiss); + toDismiss.downgradeGuildState(); + break; + } + + GuildManager.updateAllGuildTags(toDismiss); + + if (nation.getSubGuildList().isEmpty()) + nation.downgradeGuildState(); + + SendGuildEntryMsg msg = new SendGuildEntryMsg(player); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + final Session s = SessionManager.getSession(player); + + City.lastCityUpdate = System.currentTimeMillis(); + + + ArrayList guildMembers = SessionManager.getActivePCsInGuildID(nation.getObjectUUID()); + + for (PlayerCharacter member : guildMembers) { + ChatManager.chatGuildInfo(member, toDismiss.getName() + " has been dismissed as a subguild!"); + } + + ArrayList dismissedMembers = SessionManager.getActivePCsInGuildID(toDismiss.getObjectUUID()); + + for (PlayerCharacter member : dismissedMembers) { + ChatManager.chatGuildInfo(member, nation.getName() + "has dismissed you as a sub!"); + } + + return true; + } +} diff --git a/src/engine/net/client/handlers/DoorTryOpenMsgHandler.java b/src/engine/net/client/handlers/DoorTryOpenMsgHandler.java new file mode 100644 index 00000000..8fb76b03 --- /dev/null +++ b/src/engine/net/client/handlers/DoorTryOpenMsgHandler.java @@ -0,0 +1,88 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.DoorState; +import engine.InterestManagement.WorldGrid; +import engine.exception.MsgSendException; +import engine.gameManager.BuildingManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.DoorTryOpenMsg; +import engine.objects.AbstractWorldObject; +import engine.objects.Blueprint; +import engine.objects.Building; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.util.HashSet; + +/* + * @Author: + * @Summary: Processes application protocol message which handle + * open and close door requests to and from the client. + * + */ +public class DoorTryOpenMsgHandler extends AbstractClientMsgHandler { + + public DoorTryOpenMsgHandler() { + super(DoorTryOpenMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + // Member variable declaration + + PlayerCharacter player; + DoorTryOpenMsg msg; + Building targetBuilding; + int doorNumber; + + // Member variable assignment + + msg = (DoorTryOpenMsg)baseMsg; + player = origin.getPlayerCharacter(); + targetBuilding = BuildingManager.getBuildingFromCache(msg.getBuildingUUID()); + + if (player == null || targetBuilding == null) { + Logger.error("Player or Building returned NULL in OpenCloseDoor handling."); + return true; + } + + // Must be within x distance from door to manipulate it + + if (player.getLoc().distanceSquared2D(targetBuilding.getLoc()) > MBServerStatics.OPENCLOSEDOORDISTANCE * MBServerStatics.OPENCLOSEDOORDISTANCE) + return true; + + doorNumber = Blueprint.getDoorNumberbyMesh(msg.getDoorID()); + + if ((targetBuilding.isDoorLocked(doorNumber) == true) && msg.getToggle() == 0x01) { + msg.setUnk1(2); + Dispatch dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return true; //don't open a locked door + } + + if (msg.getToggle() == 0x01) { + targetBuilding.setDoorState(doorNumber, DoorState.OPEN); + targetBuilding.submitOpenDoorJob(msg.getDoorID()); + } else { + targetBuilding.setDoorState(doorNumber, DoorState.CLOSED); + } + + HashSet container = WorldGrid.getObjectsInRangePartial(targetBuilding, MBServerStatics.CHARACTER_LOAD_RANGE, + MBServerStatics.MASK_PLAYER); + + for (AbstractWorldObject awo : container) { + PlayerCharacter playerCharacter = (PlayerCharacter)awo; + Dispatch dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + return true; + } + +} diff --git a/src/engine/net/client/handlers/FormationFollowHandler.java b/src/engine/net/client/handlers/FormationFollowHandler.java new file mode 100644 index 00000000..ff78f996 --- /dev/null +++ b/src/engine/net/client/handlers/FormationFollowHandler.java @@ -0,0 +1,75 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.GroupManager; +import engine.gameManager.SessionManager; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.group.FormationFollowMsg; +import engine.net.client.msg.group.GroupUpdateMsg; +import engine.objects.Group; +import engine.objects.PlayerCharacter; + +public class FormationFollowHandler extends AbstractClientMsgHandler { + + public FormationFollowHandler() { + super(FormationFollowMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) + throws MsgSendException { + FormationFollowMsg msg = (FormationFollowMsg) baseMsg; + + PlayerCharacter source = SessionManager.getPlayerCharacter(origin); + + if (source == null) + return false; + + Group group = GroupManager.getGroup(source); + + if (group == null) + return false; + + if (msg.isFollow()) {// Toggle follow + source.toggleFollow(); + if (group.getGroupLead() == source) { + if (source.getFollow()) { + ChatManager.chatGroupInfo(source, "falls into formation"); + } else { + ChatManager.chatGroupInfo(source, "breaks formation"); + } + } else { + if (source.getFollow()) { + ChatManager.chatGroupInfo(source, source.getFirstName() + " falls into formation"); + } else { + ChatManager.chatGroupInfo(source, source.getFirstName() + " breaks formation"); + } + } + GroupUpdateMsg gum = new GroupUpdateMsg(); + gum.setGroup(group); + gum.setPlayer(source); + gum.setMessageType(8); + + group.sendUpdate(gum); + } else {// Set Formation + if (group.getGroupLead() != source) { + return false; + } + group.setFormation(msg.getFormation()); + + } + return true; + } + +} diff --git a/src/engine/net/client/handlers/FriendAcceptHandler.java b/src/engine/net/client/handlers/FriendAcceptHandler.java new file mode 100644 index 00000000..473d04d8 --- /dev/null +++ b/src/engine/net/client/handlers/FriendAcceptHandler.java @@ -0,0 +1,78 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum.DispatchChannel; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.AcceptFriendMsg; +import engine.net.client.msg.AddFriendMessage; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.objects.PlayerCharacter; +import engine.objects.PlayerFriends; + +public class FriendAcceptHandler extends AbstractClientMsgHandler { + + public FriendAcceptHandler() { + super(AcceptFriendMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, + ClientConnection origin) throws MsgSendException { + + PlayerCharacter player = origin.getPlayerCharacter(); + + if (player == null) + return true; + + + AcceptFriendMsg msg = (AcceptFriendMsg)baseMsg; + + + HandleAcceptFriend(player,msg); + + return true; + } + + + + //change to Request + public static void HandleAcceptFriend(PlayerCharacter player, AcceptFriendMsg msg){ + PlayerCharacter sourceFriend = SessionManager.getPlayerCharacterByLowerCaseName(msg.sourceName); + + if (sourceFriend == null){ + ErrorPopupMsg.sendErrorMsg(player, "Could not find player " + msg.sourceName); + return; + } + + PlayerFriends.AddToFriends(sourceFriend.getObjectUUID(), player.getObjectUUID()); + PlayerFriends.AddToFriends(player.getObjectUUID(), sourceFriend.getObjectUUID()); + + + AddFriendMessage outMsg = new AddFriendMessage(player); + + Dispatch dispatch = Dispatch.borrow(sourceFriend, outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + outMsg = new AddFriendMessage(sourceFriend); + dispatch = Dispatch.borrow(player, outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + ChatManager.chatSystemInfo(sourceFriend, player.getFirstName() + " has agreed to be your friend."); + + } + +} diff --git a/src/engine/net/client/handlers/FriendDeclineHandler.java b/src/engine/net/client/handlers/FriendDeclineHandler.java new file mode 100644 index 00000000..af72702a --- /dev/null +++ b/src/engine/net/client/handlers/FriendDeclineHandler.java @@ -0,0 +1,63 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum.DispatchChannel; +import engine.exception.MsgSendException; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.DeclineFriendMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.objects.PlayerCharacter; + +public class FriendDeclineHandler extends AbstractClientMsgHandler { + + public FriendDeclineHandler() { + super(DeclineFriendMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, + ClientConnection origin) throws MsgSendException { + + PlayerCharacter player = origin.getPlayerCharacter(); + + if (player == null) + return true; + + + DeclineFriendMsg msg = (DeclineFriendMsg)baseMsg; + + + HandleDeclineFriend(player,msg); + + return true; + } + + + + //change to Request + public static void HandleDeclineFriend(PlayerCharacter player, DeclineFriendMsg msg){ + PlayerCharacter sourceFriend = SessionManager.getPlayerCharacterByLowerCaseName(msg.sourceName); + + if (sourceFriend == null){ + ErrorPopupMsg.sendErrorMsg(player, "Could not find player " + msg.sourceName); + return; + } + + Dispatch dispatch = Dispatch.borrow(sourceFriend, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + } + +} diff --git a/src/engine/net/client/handlers/FriendRequestHandler.java b/src/engine/net/client/handlers/FriendRequestHandler.java new file mode 100644 index 00000000..56e94914 --- /dev/null +++ b/src/engine/net/client/handlers/FriendRequestHandler.java @@ -0,0 +1,69 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum.DispatchChannel; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.FriendRequestMsg; +import engine.objects.PlayerCharacter; + +public class FriendRequestHandler extends AbstractClientMsgHandler { + + public FriendRequestHandler() { + super(FriendRequestMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, + ClientConnection origin) throws MsgSendException { + + PlayerCharacter player = origin.getPlayerCharacter(); + + if (player == null) + return true; + + + FriendRequestMsg msg = (FriendRequestMsg)baseMsg; + + HandleRequestFriend(player,msg); + + return true; + } + + + + public static void HandleRequestFriend(PlayerCharacter player, FriendRequestMsg msg){ + PlayerCharacter targetFriend = SessionManager.getPlayerCharacterByLowerCaseName(msg.friendName); + + if (targetFriend == null){ + ErrorPopupMsg.sendErrorMsg(player, "Could not find player " + msg.friendName); + return; + } + + if (targetFriend.equals(player)) + return; + + + + + Dispatch dispatch = Dispatch.borrow(targetFriend, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + ChatManager.chatSystemInfo(player, "Your friend request has been sent."); + + } + +} diff --git a/src/engine/net/client/handlers/FurnitureHandler.java b/src/engine/net/client/handlers/FurnitureHandler.java new file mode 100644 index 00000000..65dce932 --- /dev/null +++ b/src/engine/net/client/handlers/FurnitureHandler.java @@ -0,0 +1,48 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum.DispatchChannel; +import engine.exception.MsgSendException; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.FurnitureMsg; +import engine.objects.PlayerCharacter; + +public class FurnitureHandler extends AbstractClientMsgHandler { + + public FurnitureHandler() { + super(FurnitureMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, + ClientConnection origin) throws MsgSendException { + + FurnitureMsg msg = (FurnitureMsg) baseMsg; + + PlayerCharacter pc = origin.getPlayerCharacter(); + if (pc == null) { + return false; + } + + if (msg.getType() == 1) + msg.setType(2); + + if (msg.getType() == 3) + msg.setType(2); + Dispatch dispatch = Dispatch.borrow(pc, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + } + +} diff --git a/src/engine/net/client/handlers/GroupInviteHandler.java b/src/engine/net/client/handlers/GroupInviteHandler.java new file mode 100644 index 00000000..7f0916d0 --- /dev/null +++ b/src/engine/net/client/handlers/GroupInviteHandler.java @@ -0,0 +1,126 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.exception.MsgSendException; +import engine.gameManager.GroupManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.group.GroupInviteMsg; +import engine.net.client.msg.group.GroupUpdateMsg; +import engine.objects.Group; +import engine.objects.PlayerCharacter; + +public class GroupInviteHandler extends AbstractClientMsgHandler { + + public GroupInviteHandler() { + super(GroupInviteMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, + + ClientConnection origin) throws MsgSendException { + GroupInviteMsg msg = (GroupInviteMsg) baseMsg; + PlayerCharacter source = SessionManager.getPlayerCharacter(origin); + + if (source == null) + return false; + + Group group = GroupManager.getGroup(source); + + // Group is new, create it. + + if (group == null) + group = GroupInviteHandler.createGroup(source, origin); + + if (group == null) + return false; + + if (!group.isGroupLead(source)) // person doing invite must be group lead + return true; + + PlayerCharacter target = null; + + if (msg.getInvited() == 1) { // Use name for invite + target = SessionManager.getPlayerCharacterByLowerCaseName(msg.getName().toLowerCase()); + } else { // Use ID for invite + target = SessionManager.getPlayerCharacterByID(msg.getTargetID()); + } + + if (target == null) + return false; + + // Client must be online + + if (SessionManager.getClientConnection(target) == null) + return false; + + if (source == target) // Inviting self, so we're done + return false; + + + //Skip invite if target is ignoring source + + if (target.isIgnoringPlayer(source)) + return false; + + + // dont block invites to people already in a group and + // dont check for pending invites, the client does it + // Send invite message to target + + msg.setSourceType(GameObjectType.PlayerCharacter.ordinal()); + msg.setSourceID(source.getObjectUUID()); + msg.setTargetType(0); + msg.setTargetID(0); + msg.setGroupType(GameObjectType.Group.ordinal()); + msg.setGroupID(group.getObjectUUID()); + msg.setInvited(1); + msg.setName(source.getFirstName()); + + Dispatch dispatch = Dispatch.borrow(target, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + return true; + } + + // this can only be called if you already know you are not in a group + // and have issued an invite + + private static Group createGroup(PlayerCharacter pc, ClientConnection origin) { + + if (pc == null) + return null; + + Group group = new Group(pc, GroupManager.incrGroupCount()); + group.addGroupMember(pc); + GroupManager.addNewGroup(group); + + pc.setFollow(false); + // Send add self to group message + GroupUpdateMsg msg = new GroupUpdateMsg(); + msg.setGroup(group); + msg.setPlayer(pc); + msg.setMessageType(1); + Dispatch dispatch = Dispatch.borrow(pc, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + + group.addUpdateGroupJob(); + + return group; + } + +} diff --git a/src/engine/net/client/handlers/GroupInviteResponseHandler.java b/src/engine/net/client/handlers/GroupInviteResponseHandler.java new file mode 100644 index 00000000..8db3182d --- /dev/null +++ b/src/engine/net/client/handlers/GroupInviteResponseHandler.java @@ -0,0 +1,112 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.GroupManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.group.GroupInviteResponseMsg; +import engine.net.client.msg.group.GroupUpdateMsg; +import engine.objects.Group; +import engine.objects.PlayerCharacter; + +import java.util.Set; + +import static engine.net.client.handlers.KeyCloneAudit.KEYCLONEAUDIT; + +public class GroupInviteResponseHandler extends AbstractClientMsgHandler { + + public GroupInviteResponseHandler() { + super(GroupInviteResponseMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, + ClientConnection origin) throws MsgSendException { + + GroupInviteResponseMsg msg = (GroupInviteResponseMsg) baseMsg; + + PlayerCharacter player = origin.getPlayerCharacter(); + + if (player == null) + return false; + + // if we are already in a group we are leaving it + + Group currGroup = GroupManager.getGroup(player); + + if (currGroup != null) // if we are already in a group we are leaving it + GroupManager.LeaveGroup(player); + + // not sure we need to test for invites to wrong grp as only + // 1 invite can be on screen at a time + //if (invitesPending.get(player) != msg.getGroupID()) // Can't accept + // invite to + // wrong group + // return; + + Group group = GroupManager.getGroup(msg.getGroupID()); + + if (group == null) + return false; + + if (group.addGroupMember(player) == false) + return false; + { + player.setFollow(true); + GroupManager.addPlayerGroupMapping(player, group); + Set members = group.getMembers(); + GroupUpdateMsg groupUpdateMsg; + + // Send all group members to player added + for (PlayerCharacter groupMember : members) { + + groupUpdateMsg = new GroupUpdateMsg(); + groupUpdateMsg.setGroup(group); + groupUpdateMsg.setMessageType(1); + + if (groupMember == null) + continue; + + if (groupMember.equals(player)) + continue; + + groupUpdateMsg.setPlayer(groupMember); + + Dispatch dispatch = Dispatch.borrow(player, groupUpdateMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + } + + // send new group member to everyone in group. + groupUpdateMsg = new GroupUpdateMsg(); + groupUpdateMsg.setGroup(group); + groupUpdateMsg.setMessageType(1); + groupUpdateMsg.setPlayer(player); + group.sendUpdate(groupUpdateMsg); + + String text = player.getFirstName() + " has joined the group."; + ChatManager.chatGroupInfo(player, text); + + // Run Keyclone Audit + + KEYCLONEAUDIT.audit(player, group); + + return true; + } + } +} + + diff --git a/src/engine/net/client/handlers/GroupUpdateHandler.java b/src/engine/net/client/handlers/GroupUpdateHandler.java new file mode 100644 index 00000000..944a3b5b --- /dev/null +++ b/src/engine/net/client/handlers/GroupUpdateHandler.java @@ -0,0 +1,33 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.exception.MsgSendException; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.group.GroupUpdateMsg; + +public class GroupUpdateHandler extends AbstractClientMsgHandler { + + public GroupUpdateHandler() { + super(GroupUpdateMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, + ClientConnection origin) throws MsgSendException { + + //GroupUpdateMsg msg = (GroupUpdateMsg) baseMsg; + // not sure what to do with these as we spend our time sending them + // to the whole group ourselves + return true; + } + +} diff --git a/src/engine/net/client/handlers/GuildControlHandler.java b/src/engine/net/client/handlers/GuildControlHandler.java new file mode 100644 index 00000000..112b70d2 --- /dev/null +++ b/src/engine/net/client/handlers/GuildControlHandler.java @@ -0,0 +1,74 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum; +import engine.exception.MsgSendException; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.guild.GuildControlMsg; +import engine.net.client.msg.guild.GuildListMsg; +import engine.objects.GuildStatusController; +import engine.objects.PlayerCharacter; + +public class GuildControlHandler extends AbstractClientMsgHandler { + + public GuildControlHandler() { + super(GuildControlMsg.class); + } + + // TODO Don't think this protocolMsg (0x3235E5EA) is actually player history. so + // take further look at it. + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + GuildControlMsg msg = (GuildControlMsg) baseMsg; + Dispatch dispatch; + + // until we know what it's for, just echo it back. + msg.setUnknown05((byte) 1); + + //Send a GuildList msg + if(msg.getUnknown01() == 1) { + + PlayerCharacter player = SessionManager.getPlayerCharacter(origin); + //TODO figure out why GL can't be changed and IC can't be banished + //Bounce back the rank options + msg.setGM((byte) (GuildStatusController.isGuildLeader(player.getGuildStatus()) ? 1 : 0)); + + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + if (GuildStatusController.isInnerCouncil(player.getGuildStatus()) || GuildStatusController.isGuildLeader(player.getGuildStatus())) { + dispatch = Dispatch.borrow(player, new GuildListMsg(player.getGuild())); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } else + ErrorPopupMsg.sendErrorMsg(player, "Only guild leader and inner council have such authority!"); + + + } else if(msg.getUnknown01() == 2) { + PlayerCharacter player = SessionManager.getPlayerCharacter(origin); + + //If we don't get a valid PC for whatever reason.. just ignore it. + PlayerCharacter pc = PlayerCharacter.getFromCache(msg.getUnknown03()); + + if(pc != null) { + dispatch = Dispatch.borrow(player,new GuildListMsg(pc)); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + } + + return true; + } + +} diff --git a/src/engine/net/client/handlers/GuildCreationCloseHandler.java b/src/engine/net/client/handlers/GuildCreationCloseHandler.java new file mode 100644 index 00000000..f72edff8 --- /dev/null +++ b/src/engine/net/client/handlers/GuildCreationCloseHandler.java @@ -0,0 +1,30 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.exception.MsgSendException; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.guild.GuildCreationCloseMsg; + +public class GuildCreationCloseHandler extends AbstractClientMsgHandler { + + public GuildCreationCloseHandler() { + super(GuildCreationCloseMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { +// GuildCreationCloseMsg msg = (GuildCreationCloseMsg) baseMsg; +// origin.sendMsg(msg); + return true; + } + +} diff --git a/src/engine/net/client/handlers/GuildCreationFinalizeHandler.java b/src/engine/net/client/handlers/GuildCreationFinalizeHandler.java new file mode 100644 index 00000000..57295aa1 --- /dev/null +++ b/src/engine/net/client/handlers/GuildCreationFinalizeHandler.java @@ -0,0 +1,165 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.DispatchChannel; +import engine.Enum.GuildHistoryType; +import engine.Enum.ItemType; +import engine.Enum.OwnerType; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.gameManager.GuildManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.guild.GuildCreationFinalizeMsg; +import engine.net.client.msg.guild.GuildInfoMsg; +import engine.objects.*; +import engine.util.StringUtils; + +public class GuildCreationFinalizeHandler extends AbstractClientMsgHandler { + + public GuildCreationFinalizeHandler() { + super(GuildCreationFinalizeMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter player; + GuildCreationFinalizeMsg msg; + Enum.GuildType charterType; + Guild newGuild; + ItemBase itemBase; + Item charter; + Dispatch dispatch; + + msg = (GuildCreationFinalizeMsg) baseMsg; + + player = SessionManager.getPlayerCharacter(origin); + + boolean isGuildLeader = GuildStatusController.isGuildLeader(player.getGuildStatus()); + if (GuildStatusController.isGuildLeader(player.getGuildStatus()) || player.getGuild() != null && player.getGuild().getGuildLeaderUUID() == player.getObjectUUID()) { + ErrorPopupMsg.sendErrorPopup(player, GuildManager.MUST_LEAVE_GUILD); + return true; + } + + //Validate the Charter + + charter = msg.getCharter(); + + if (charter == null || charter.getOwnerType() != OwnerType.PlayerCharacter || charter.getOwnerID() != player.getObjectUUID()) { + ErrorPopupMsg.sendErrorPopup(player, GuildManager.NO_CHARTER_FOUND); + return true; + } + + + + itemBase = charter.getItemBase(); + + + // Item must be a valid charterType (type 10 in db) + + if (itemBase == null || (itemBase.getType().equals(ItemType.GUILDCHARTER) == false)) { + ErrorPopupMsg.sendErrorPopup(player, GuildManager.NO_CHARTER_FOUND); + return true; + } + charterType = Enum.GuildType.getGuildTypeFromCharter(itemBase); + + + + if (charterType == null){ + ErrorPopupMsg.sendErrorPopup(player, GuildManager.NO_CHARTER_FOUND); + return true; + } + + + + + + //Validate Guild Tags + + if (!msg.getGuildTag().isValid()) { + ErrorPopupMsg.sendErrorPopup(player, GuildManager.CREST_RESERVED); + return true; + } + + // Validation passes. Leave current guild and create new one. + + if (player.getGuild() != null && player.getGuild().getObjectUUID() != 0) + player.getGuild().removePlayer(player,GuildHistoryType.LEAVE); + + + + int leadershipType = ((msg.getICVoteFlag() << 1) | msg.getMemberVoteFlag()); + + newGuild = new Guild( msg.getName(),null, charterType.ordinal(), + charterType.getLeadershipType(leadershipType), msg.getGuildTag(), + StringUtils.truncate(msg.getMotto(), 120)); + + newGuild.setGuildLeaderForCreate(player); + + synchronized (this) { + if (!DbManager.GuildQueries.IS_NAME_UNIQUE(msg.getName())) { + ErrorPopupMsg.sendErrorPopup(player, GuildManager.UNIQUE_NAME); + return true; + } + + if (!DbManager.GuildQueries.IS_CREST_UNIQUE(msg.getGuildTag())) { + ErrorPopupMsg.sendErrorPopup(player, GuildManager.UNIQUE_CREST); + return true; + } + + newGuild = DbManager.GuildQueries.SAVE_TO_DATABASE(newGuild); + } + + if (newGuild == null) { + ErrorPopupMsg.sendErrorPopup(player, GuildManager.FAILURE_TO_SWEAR_GUILD); + return true; + } + + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + GuildManager.joinGuild(player, newGuild, GuildHistoryType.CREATE); + + newGuild.setGuildLeader(player); + player.setGuildLeader(true); + player.setInnerCouncil(true); + player.setFullMember(true); + player.setGuildTitle(charterType.getNumberOfRanks() - 1); + player.getCharItemManager().delete(charter); + player.getCharItemManager().updateInventory(); + player.incVer(); + + DispatchMessage.sendToAllInRange(player, new GuildInfoMsg(player, newGuild, 2)); + + ChatManager.chatSystemInfo(player, msg.getName() + " has arrived on Grief server!"); + + return true; + } +} diff --git a/src/engine/net/client/handlers/GuildCreationOptionsHandler.java b/src/engine/net/client/handlers/GuildCreationOptionsHandler.java new file mode 100644 index 00000000..6dbbb680 --- /dev/null +++ b/src/engine/net/client/handlers/GuildCreationOptionsHandler.java @@ -0,0 +1,47 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.exception.MsgSendException; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.guild.GuildCreationOptionsMsg; +import engine.objects.PlayerCharacter; + +public class GuildCreationOptionsHandler extends AbstractClientMsgHandler { + + public GuildCreationOptionsHandler() { + super(GuildCreationOptionsMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + GuildCreationOptionsMsg msg = (GuildCreationOptionsMsg) baseMsg; + PlayerCharacter sourcePlayer = origin.getPlayerCharacter(); + Dispatch dispatch; + + if(msg.getScreenType() == 1) { + msg.setScreenType(3); + } else if(msg.getScreenType() == 2) { + msg.setScreenType(4); + } + + if (sourcePlayer == null) + return true; + + dispatch = Dispatch.borrow(sourcePlayer, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + + return true; + } + +} diff --git a/src/engine/net/client/handlers/GuildInfoHandler.java b/src/engine/net/client/handlers/GuildInfoHandler.java new file mode 100644 index 00000000..8e490a97 --- /dev/null +++ b/src/engine/net/client/handlers/GuildInfoHandler.java @@ -0,0 +1,67 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum.GameObjectType; +import engine.exception.MsgSendException; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.guild.GuildInfoMsg; +import engine.objects.PlayerCharacter; + +public class GuildInfoHandler extends AbstractClientMsgHandler { + + public GuildInfoHandler() { + super(GuildInfoMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + GuildInfoMsg msg = (GuildInfoMsg) baseMsg; + Dispatch dispatch; + + // get source player + PlayerCharacter sourcePlayer = SessionManager + .getPlayerCharacter(origin); + + if (sourcePlayer == null) + return true; + + if(msg.getMsgType() == 1) { + dispatch = Dispatch.borrow(sourcePlayer, new GuildInfoMsg(sourcePlayer, sourcePlayer.getGuild(), 4)); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + } else if(msg.getMsgType() == 5) { + + if(msg.getObjectType() == GameObjectType.PlayerCharacter.ordinal()) { + PlayerCharacter pc = PlayerCharacter.getPlayerCharacter(msg.getObjectID()); + dispatch = Dispatch.borrow(sourcePlayer, new GuildInfoMsg(pc, pc.getGuild(), 5)); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + } else { + //TODO Change this to a null object when we make a null object. + + dispatch = Dispatch.borrow(sourcePlayer,new GuildInfoMsg(sourcePlayer, sourcePlayer.getGuild(), 1)); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + } + } + + // Send PromoteDemoteScreen info message response. 0x001D4DF6 + + // Send guild member list? 0x6949C720 + // GuildList(source, origin); + + // send 0x3235E5EA? See what that is + + return true; + } + +} diff --git a/src/engine/net/client/handlers/GuildListHandler.java b/src/engine/net/client/handlers/GuildListHandler.java new file mode 100644 index 00000000..f4eb052c --- /dev/null +++ b/src/engine/net/client/handlers/GuildListHandler.java @@ -0,0 +1,36 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.exception.MsgSendException; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.guild.GuildListMsg; + +public class GuildListHandler extends AbstractClientMsgHandler { + + public GuildListHandler() { + super(GuildListMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { +// GuildListMsg msg = (GuildListMsg) baseMsg; + + // GuildListMsg msg = new GuildListMsg(origin); + // GuildTableList gtl = new GuildTableList(); + // gtl.setUUID(pc.getUUID()); gtl.setName(pc.getName()); + // msg.add(gtl); + // origin.sendMsg(msg); + + return true; + } + +} diff --git a/src/engine/net/client/handlers/GuildUnknownHandler.java b/src/engine/net/client/handlers/GuildUnknownHandler.java new file mode 100644 index 00000000..c488e0cf --- /dev/null +++ b/src/engine/net/client/handlers/GuildUnknownHandler.java @@ -0,0 +1,29 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.exception.MsgSendException; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.guild.GuildUnknownMsg; + +public class GuildUnknownHandler extends AbstractClientMsgHandler { + + public GuildUnknownHandler() { + super(GuildUnknownMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { +// GuildUnknownMsg msg = (GuildUnknownMsg) baseMsg; + return true; + } + +} diff --git a/src/engine/net/client/handlers/HirelingServiceMsgHandler.java b/src/engine/net/client/handlers/HirelingServiceMsgHandler.java new file mode 100644 index 00000000..c5c90a70 --- /dev/null +++ b/src/engine/net/client/handlers/HirelingServiceMsgHandler.java @@ -0,0 +1,94 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + + +import engine.Enum.DispatchChannel; +import engine.exception.MsgSendException; +import engine.gameManager.BuildingManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.HirelingServiceMsg; +import engine.net.client.msg.ManageNPCMsg; +import engine.objects.Building; +import engine.objects.NPC; +import engine.objects.PlayerCharacter; + +public class HirelingServiceMsgHandler extends AbstractClientMsgHandler { + + public HirelingServiceMsgHandler() { + super(HirelingServiceMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter player; + HirelingServiceMsg msg; + + msg = (HirelingServiceMsg) baseMsg; + + // get PlayerCharacter of person accepting invite + + player = SessionManager.getPlayerCharacter(origin); + + if (player == null) + return true; + + switch (msg.messageType){ + case HirelingServiceMsg.SETREPAIRCOST: + Building building = BuildingManager.getBuildingFromCache(msg.buildingID); + + if (building == null) + return true; + + NPC npc = NPC.getFromCache(msg.npcID); + + if (npc == null) + return true; + + if (!BuildingManager.playerCanManage(player, building)) + return true; + + + + npc.setRepairCost(msg.repairCost); + ManageNPCMsg outMsg = new ManageNPCMsg(npc); + Dispatch dispatch = Dispatch.borrow(player, msg); + + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + dispatch = Dispatch.borrow(player, outMsg); + + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + break; + } + + + return true; + + + } + +} diff --git a/src/engine/net/client/handlers/InviteToGuildHandler.java b/src/engine/net/client/handlers/InviteToGuildHandler.java new file mode 100644 index 00000000..4ccadb4c --- /dev/null +++ b/src/engine/net/client/handlers/InviteToGuildHandler.java @@ -0,0 +1,151 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.GuildManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.guild.InviteToGuildMsg; +import engine.objects.GuildStatusController; +import engine.objects.PlayerCharacter; + +public class InviteToGuildHandler extends AbstractClientMsgHandler { + + public InviteToGuildHandler() { + super(InviteToGuildMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + InviteToGuildMsg msg; + PlayerCharacter sourcePlayer; + PlayerCharacter targetPlayer; + Dispatch dispatch; + + msg = (InviteToGuildMsg) baseMsg; + + // First see if this is a refusal to another guild invite + + if (msg.getResponse() == 4) + return true; // Player refused invite + + // get sourcePlayer player + + sourcePlayer = SessionManager.getPlayerCharacter(origin); + + if (sourcePlayer == null) + return true; + + if (msg.getTargetUUID() == 0) { + // get targetPlayer player by name + targetPlayer = SessionManager.getPlayerCharacterByLowerCaseName(msg.getTargetName()); + + if (targetPlayer == null) { + ChatManager.chatGuildError(sourcePlayer, + "No such player exists!"); + return true; + } + } else + if (msg.getTargetType() == GameObjectType.PlayerCharacter.ordinal()) { + + targetPlayer = SessionManager.getPlayerCharacterByID(msg.getTargetUUID()); + + if (targetPlayer == null) { + ChatManager.chatGuildError(sourcePlayer, + "No such player exists!"); + return true; + } + } else { + ChatManager.chatGuildError(sourcePlayer, + "You cannot invite that character!"); + return true; + } + + // get sourcePlayer guild. Verify sourcePlayer player is in guild + + if (sourcePlayer.getGuild().getObjectUUID() == 0 || sourcePlayer.getGuild().isErrant()) { + ChatManager.chatGuildError(sourcePlayer, + "You cannot invite someone for errant!"); + return true; + } + + Enum.GuildType guildType = Enum.GuildType.values()[sourcePlayer.getGuild().getCharter()]; + + if (guildType == null){ + ErrorPopupMsg.sendErrorPopup(sourcePlayer, GuildManager.NO_CHARTER_FOUND); + return true; + } + + + + // verify sourcePlayer player is full member so they can invite + + if (GuildStatusController.isFullMember(sourcePlayer.getGuildStatus()) == false) { + ChatManager.chatGuildError(sourcePlayer, + "You do not have authority to invite!"); + return true; + } + + //block invite is targetPlayer is ignoring sourcePlayer + + if (targetPlayer.isIgnoringPlayer(sourcePlayer)) + return true; + + if ((targetPlayer.getGuild().isErrant() == false)) { + ChatManager.chatGuildError(sourcePlayer, + targetPlayer.getFirstName() + " already belongs to a guild!"); + return true; + } + + // verify targetPlayer player is not on banish list + + if (sourcePlayer.getGuild().getBanishList().contains(targetPlayer)) { + ErrorPopupMsg.sendErrorPopup(sourcePlayer, 135);// Character is considered BANISHED by guild leadership + return true; + } + + //verify targetPlayer meets level requirements of guild + + if ((targetPlayer.getLevel() < sourcePlayer.getGuild().getRepledgeMin()) || targetPlayer.getLevel() > sourcePlayer.getGuild().getRepledgeMax()) { + ErrorPopupMsg.sendErrorPopup(sourcePlayer, 135);// you do not meet the level required for this SWORN guild + return true; + } + + targetPlayer.setLastGuildToInvite(sourcePlayer.getGuild().getObjectUUID()); + + // setup guild invite message to send to targetPlayer + + msg.setSourceType(sourcePlayer.getObjectType().ordinal()); + msg.setSourceUUID(sourcePlayer.getObjectUUID()); + msg.setTargetType(targetPlayer.getObjectType().ordinal()); + + msg.setTargetUUID(targetPlayer.getObjectUUID()); + msg.setGuildTag(sourcePlayer.getGuild().getGuildTag()); + msg.setGuildName(sourcePlayer.getGuild().getName()); + msg.setGuildType(sourcePlayer.getGuild().getObjectType().ordinal()); + msg.setGuildUUID(sourcePlayer.getGuild().getObjectUUID()); + msg.setTargetName(""); + + dispatch = Dispatch.borrow(targetPlayer, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + return true; + } + +} diff --git a/src/engine/net/client/handlers/InviteToSubHandler.java b/src/engine/net/client/handlers/InviteToSubHandler.java new file mode 100644 index 00000000..9c16ce90 --- /dev/null +++ b/src/engine/net/client/handlers/InviteToSubHandler.java @@ -0,0 +1,133 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.guild.InviteToSubMsg; +import engine.objects.Guild; +import engine.objects.GuildStatusController; +import engine.objects.PlayerCharacter; + +public class InviteToSubHandler extends AbstractClientMsgHandler { + + public InviteToSubHandler() { + super(InviteToSubMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter source; + PlayerCharacter target; + Guild sourceGuild; + Guild targetGuild; + InviteToSubMsg msg = (InviteToSubMsg) baseMsg; + Dispatch dispatch; + + source = SessionManager.getPlayerCharacter(origin); + + if (source == null) + return true; + + target = (PlayerCharacter) DbManager.getObject(GameObjectType.PlayerCharacter, msg.getTargetUUID()); + + if (target == null) { + ErrorPopupMsg.sendErrorMsg(source, "A Serious error has occured. Please post details for to ensure transaction integrity"); + return true; + } + + //Ignore invites to sub if ignoring player + + if (target.isIgnoringPlayer(source)) + return true; + + sourceGuild = source.getGuild(); + targetGuild = target.getGuild(); + + //source must be in guild + + if (sourceGuild == null) { + sendChat(source, "You must be in a guild to invite to sub."); + return true; + } + + if (sourceGuild.isErrant()){ + sendChat(source, "You must be in a guild to invite to sub."); + return true; + } + + //source must be GL or IC + + if (GuildStatusController.isInnerCouncil(source.getGuildStatus()) == false) { + sendChat(source, "Only guild leadership can invite to sub."); + return true; + } + + if (sourceGuild.getNation().isErrant()) + return true; + + //target must be in a guild + + if (targetGuild == null) + return true; + + if (sourceGuild.equals(targetGuild)) + return true; + + //target must be GL or IC + + if (GuildStatusController.isInnerCouncil(target.getGuildStatus()) == false && GuildStatusController.isGuildLeader(target.getGuildStatus()) == false) { + sendChat(source, "Target player is not guild leadership."); + return true; + } + + //Can't already be same nation or errant + //source guild is limited to 7 subs + //TODO this should be based on TOL rank + + + if (!sourceGuild.canSubAGuild(targetGuild)) { + sendChat(source, "This Guild can't be subbed."); + return true; + } + + //all tests passed, let's send invite. + + if (target.getClientConnection() != null) { + msg.setGuildTag(sourceGuild.getGuildTag()); + msg.setGuildName(sourceGuild.getName()); + msg.setGuildUUID(sourceGuild.getObjectUUID()); + msg.setUnknown02(1); + + dispatch = Dispatch.borrow(target, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + } else { + sendChat(source, "Failed to send sub invite to target."); + return true; + } + + return true; + } + + private static void sendChat(PlayerCharacter source, String msg) { + ChatManager.chatGuildError(source, msg); + } +} diff --git a/src/engine/net/client/handlers/ItemProductionMsgHandler.java b/src/engine/net/client/handlers/ItemProductionMsgHandler.java new file mode 100644 index 00000000..f0848bf6 --- /dev/null +++ b/src/engine/net/client/handlers/ItemProductionMsgHandler.java @@ -0,0 +1,521 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.Enum.ItemType; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.ItemProductionMsg; +import engine.net.client.msg.ManageNPCMsg; +import engine.objects.*; +import org.pmw.tinylog.Logger; + +import java.util.HashMap; + +/* + * @Summary: Processes application protocol message which modifies + * hireling inventory through rolling, junking or depositing. + */ +public class ItemProductionMsgHandler extends AbstractClientMsgHandler { + + private static final int ACTION_PRODUCE = 1; + private static final int ACTION_JUNK = 2; + private static final int ACTION_RECYCLE = 3; + private static final int ACTION_COMPLETE = 4; + private static final int ACTION_DEPOSIT = 6; + private static final int ACTION_SETPRICE = 5; + private static final int ACTION_TAKE = 7; + private static final int ACTION_CONFIRM_SETPRICE = 9; // Unsure. Sent by client + private static final int ACTION_CONFIRM_DEPOSIT = 10; // Unsure. Sent by client + private static final int ACTION_CONFIRM_TAKE = 11; // Unsure. Sent by client + + public ItemProductionMsgHandler() { + super(ItemProductionMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + // Member variable declaration + + PlayerCharacter player; + NPC vendorNPC; + ItemProductionMsg msg; + Dispatch dispatch; + + // Member variable assignment + + msg = (ItemProductionMsg) baseMsg; + player = origin.getPlayerCharacter(); + + if (player == null) + return true; + + // Grab reference to vendor we are interacting with + + vendorNPC = (NPC) DbManager.getObject(engine.Enum.GameObjectType.NPC, msg.getNpcUUID()); + + // Oops? + + if (vendorNPC == null) + return true; + + // Process Request + + switch (msg.getActionType()) { + + case ACTION_PRODUCE: + boolean isRandom = false; + if (msg.getUnknown03() != 0 && msg.getpToken() == 0 && msg.getsToken() == 0) + isRandom = true; + //Create Multiple Item Function.. Fill all empty slots + if (msg.isMultiple()){ + int emptySlots = vendorNPC.getRank() - vendorNPC.getRolling().size(); + if (emptySlots > 0){ + for (int i = 0;i 500) { + ErrorPopupMsg.sendErrorPopup(player, 21); + return; + } + + if (!targetItem.validForInventory(origin, player, itemMan)){ + ErrorPopupMsg.sendErrorPopup(player, 19); + return; + } + + // Transfer item from player to vendor's inventory + + if (!itemMan.sellToNPC(targetItem, vendor)){ + ErrorPopupMsg.sendErrorPopup(player, 109); + return; + } + + + outMsg = new ItemProductionMsg(vendor.getBuilding(), vendor, targetItem, ACTION_DEPOSIT, true); + dispatch = Dispatch.borrow(player, outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + outMsg = new ItemProductionMsg(vendor.getBuilding(), vendor, targetItem, ACTION_CONFIRM_DEPOSIT, true); + dispatch = Dispatch.borrow(player, outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + origin.getPlayerCharacter().getCharItemManager().updateInventory(); + }catch (Exception e){ + Logger.error(e); + }finally { + origin.sellLock.unlock(); + } + + } + + + + } + + // Method completes an item that has been previously rolled + // adding it to the NPC's inventory + + private static void completeItem(int itemUUID, NPC vendor, ClientConnection origin, ItemProductionMsg msg) { + + Item targetItem; + ManageNPCMsg outMsg; + Dispatch dispatch; + + PlayerCharacter player = origin.getPlayerCharacter(); + + if (player == null) + return; + + if (origin.buyLock.tryLock()) { + try { + targetItem = Item.getFromCache(itemUUID); + + if (targetItem == null) + return; + + + if (!vendor.getCharItemManager().forgeContains(targetItem, vendor)) + return; + + boolean worked = DbManager.ItemQueries.UPDATE_FORGE_TO_INVENTORY(targetItem); + if (!worked) { + Guild guild = vendor.getGuild(); + if (guild == null) + return; + //ChatManager.chatGuildInfo(guild, "Failed to complete Item " + targetItem.getName()); + return; + } + + targetItem.containerType = Enum.ItemContainerType.INVENTORY; + targetItem.setOwner(vendor); + vendor.getCharItemManager().addItemToInventory(targetItem); + + vendor.removeItemFromForge(targetItem); + + outMsg = new ManageNPCMsg(vendor); + outMsg.setMessageType(ACTION_PRODUCE); + dispatch = Dispatch.borrow(player, outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } finally { + origin.buyLock.unlock(); + } + } + } + + // Method handles recycling of an item + + private static void recycleItem(HashMap itemList, NPC vendor, ClientConnection origin) { + + Item targetItem; + ItemProductionMsg outMsg; + int totalValue = 0; + int currentStrongbox; + Dispatch dispatch; + + if (vendor.getBuilding() == null) + return; + + PlayerCharacter player = origin.getPlayerCharacter(); + + if (player == null) + return; + + if (itemList == null) + return; + + if (origin.sellLock.tryLock()) { + try { + + + + for (int itemUUID : itemList.keySet()) { + int itemValue = 0; + + int type = itemList.get(itemUUID); + + if (type == GameObjectType.Item.ordinal()) + targetItem = Item.getFromCache(itemUUID); + else + targetItem = MobLoot.getFromCache(itemUUID); + + if (targetItem == null) + continue; + + if (targetItem.getItemBase().getType() == ItemType.GOLD) + return; + + if (!vendor.getCharItemManager().doesCharOwnThisItem(targetItem.getObjectUUID())) + continue; + if (vendor.getCharItemManager().inventoryContains(targetItem) == false) + continue; + + itemValue = targetItem.getBaseValue(); + + if (vendor.getBuilding().getStrongboxValue() + itemValue > vendor.getBuilding().getMaxGold()) { + ErrorPopupMsg.sendErrorPopup(player, 201); + break; + } + + switch (targetItem.getItemBase().getType()) { + case CONTRACT: + case GUILDCHARTER: + case DEED: + case REALMCHARTER: + case SCROLL: + case TEARS: + itemValue = 0; + continue; + } + totalValue += itemValue; + long start = System.currentTimeMillis(); + vendor.getCharItemManager().recycle(targetItem); + long end = System.currentTimeMillis(); + long timetook = end - start; + + // ChatManager.chatSystemInfo(player, "Took " + timetook + " ms to finish"); + + outMsg = new ItemProductionMsg(vendor.getBuilding(), vendor, targetItem, ACTION_TAKE, true); + + dispatch = Dispatch.borrow(origin.getPlayerCharacter(), outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + // Refund a portion of the gold + + if (!vendor.getBuilding().transferGold(totalValue,false)) + return; + + + + }catch (Exception e){ + Logger.error(e); + }finally { + + origin.sellLock.unlock(); + } + + } + + // Refresh vendor's inventory to client + + } + + // Method junks an item that has been rolled but not completed + + private static void junkItem(int itemUUID, NPC vendor, ClientConnection origin) { + + MobLoot targetItem; + ManageNPCMsg outMsg; + Dispatch dispatch; + + if (origin.sellLock.tryLock()) { + try { + targetItem = MobLoot.getFromCache(itemUUID); + + PlayerCharacter player = origin.getPlayerCharacter(); + + if (player == null) + return; + + // Can't junk nothing! + + if (targetItem == null) + return; + + + + if (!vendor.getCharItemManager().forgeContains(targetItem, vendor)) + return; + + // Cannot junk items without a forge! + + if (vendor.getBuilding() == null) + return; + + // Delete the item and cancel any pending rolling timer jobs + + targetItem.recycle(vendor); + vendor.removeItemFromForge(targetItem); + + // Refresh vendor's inventory to client + + outMsg = new ManageNPCMsg(vendor); + outMsg.setMessageType(1); + dispatch = Dispatch.borrow(player, outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + ; + } finally { + origin.sellLock.unlock(); + } + } + + } + + // Method removes item from an NPC's inventory and transferes it to a player + + private static void takeItem(HashMap itemList, NPC vendor, ClientConnection origin) { + + Item targetItem; + + + PlayerCharacter player = origin.getPlayerCharacter(); + + if (player == null) + return; + + + + for (int itemUUID : itemList.keySet()) { + + int type = itemList.get(itemUUID); + if (type == GameObjectType.Item.ordinal()){ + targetItem = Item.getFromCache(itemUUID); + + } + else{ + targetItem = MobLoot.getFromCache(itemUUID); + + } + + if (targetItem == null) + return; + + + if (targetItem.getItemBase().getType() == ItemType.GOLD) + return; + if (vendor.getCharItemManager().inventoryContains(targetItem) == false) + return; + + if (player.getCharItemManager().hasRoomInventory(targetItem.getItemBase().getWeight()) == false) + return; + + player.getCharItemManager().buyFromNPC(targetItem, vendor); + + } + + player.getCharItemManager().updateInventory(); + + // Update NPC inventory to client + + + } + + // Method handles rolling item requests from the client + +} diff --git a/src/engine/net/client/handlers/KeepAliveServerClientHandler.java b/src/engine/net/client/handlers/KeepAliveServerClientHandler.java new file mode 100644 index 00000000..827590df --- /dev/null +++ b/src/engine/net/client/handlers/KeepAliveServerClientHandler.java @@ -0,0 +1,48 @@ +package engine.net.client.handlers; + +import engine.Enum.DispatchChannel; +import engine.exception.MsgSendException; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.KeepAliveServerClientMsg; +import engine.objects.PlayerCharacter; + +/* + * @Author: + * @Summary: Processes application protocol message which keeps + * client's tcp connection open. + */ + +public class KeepAliveServerClientHandler extends AbstractClientMsgHandler { + + public KeepAliveServerClientHandler() { + super(KeepAliveServerClientMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter pc = origin.getPlayerCharacter(); + + + + // Member variable declaration + + KeepAliveServerClientMsg msg; + + // Member variable assignment + + msg = (KeepAliveServerClientMsg) baseMsg; + + + // Send ping to client + + Dispatch dispatch = Dispatch.borrow(pc, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + + return true; + } + +} \ No newline at end of file diff --git a/src/engine/net/client/handlers/KeyCloneAudit.java b/src/engine/net/client/handlers/KeyCloneAudit.java new file mode 100644 index 00000000..94e7a162 --- /dev/null +++ b/src/engine/net/client/handlers/KeyCloneAudit.java @@ -0,0 +1,32 @@ +package engine.net.client.handlers; + +import engine.gameManager.DbManager; +import engine.objects.Group; +import engine.objects.PlayerCharacter; +import org.pmw.tinylog.Logger; + +public enum KeyCloneAudit { + KEYCLONEAUDIT; + + void audit(PlayerCharacter player, Group group) { + + int machineCount = 0; + String machineID; + + machineID = player.getClientConnection().machineID; + + for (PlayerCharacter member : group.getMembers()) + if (machineID.equals(member.getClientConnection().machineID)) + machineCount = machineCount + 1; + + // (int) ConfigManager.WORLDSERVER.config.get("keyclone") + if (machineCount > 4) { + Logger.error("Keyclone detected from: " + player.getAccount().getUname() + + " with machine count of: " + machineCount); + DbManager.AccountQueries.SET_TRASH(machineID); + } + // Refactor to separate file to log keyclones + // DbManager.AccountQueries.EMPTY_TRASH(machineID); + + } +} diff --git a/src/engine/net/client/handlers/LeaveGroupHandler.java b/src/engine/net/client/handlers/LeaveGroupHandler.java new file mode 100644 index 00000000..e181dca0 --- /dev/null +++ b/src/engine/net/client/handlers/LeaveGroupHandler.java @@ -0,0 +1,31 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.exception.MsgSendException; +import engine.gameManager.GroupManager; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.group.LeaveGroupMsg; + +public class LeaveGroupHandler extends AbstractClientMsgHandler { + + public LeaveGroupHandler() { + super(LeaveGroupMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, + ClientConnection origin) throws MsgSendException { + GroupManager.LeaveGroup(origin); + return true; + } + +} diff --git a/src/engine/net/client/handlers/LeaveGuildHandler.java b/src/engine/net/client/handlers/LeaveGuildHandler.java new file mode 100644 index 00000000..3764f7c8 --- /dev/null +++ b/src/engine/net/client/handlers/LeaveGuildHandler.java @@ -0,0 +1,72 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum.GuildHistoryType; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.guild.LeaveGuildMsg; +import engine.objects.Guild; +import engine.objects.GuildStatusController; +import engine.objects.PlayerCharacter; + +public class LeaveGuildHandler extends AbstractClientMsgHandler { + + public LeaveGuildHandler() { + super(LeaveGuildMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + LeaveGuildMsg msg = (LeaveGuildMsg) baseMsg; + Dispatch dispatch; + + // get PlayerCharacter of person leaving invite + + PlayerCharacter sourcePlayer = SessionManager.getPlayerCharacter(origin); + + if (sourcePlayer == null) + return true; + + // Guild leader can't leave guild. must pass GL or disband + + if (GuildStatusController.isGuildLeader(sourcePlayer.getGuildStatus())) { + msg.setMessage("You must switch leadership of your guild before leaving!"); + dispatch = Dispatch.borrow(sourcePlayer, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + return true; + } + + // get old Guild + Guild oldGuild = sourcePlayer.getGuild(); + + if (oldGuild == null || oldGuild.isErrant()) { + return true; + } + + // Send left guild message to rest of guild + ChatManager.chatGuildInfo(oldGuild, sourcePlayer.getFirstName() + " has left the guild."); + + oldGuild.removePlayer(sourcePlayer, GuildHistoryType.LEAVE); + + // Send message back to client + msg.setMessage("You have left the guild."); + dispatch = Dispatch.borrow(sourcePlayer, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + + return true; + } + +} diff --git a/src/engine/net/client/handlers/LockUnlockDoorMsgHandler.java b/src/engine/net/client/handlers/LockUnlockDoorMsgHandler.java new file mode 100644 index 00000000..367c6911 --- /dev/null +++ b/src/engine/net/client/handlers/LockUnlockDoorMsgHandler.java @@ -0,0 +1,88 @@ +package engine.net.client.handlers; + +import engine.exception.MsgSendException; +import engine.gameManager.BuildingManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.LockUnlockDoorMsg; +import engine.objects.Blueprint; +import engine.objects.Building; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +/* + * @Author: + * @Summary: Processes application protocol message which handle + * lock and unlock door requests to and from the client. + * + */ + +public class LockUnlockDoorMsgHandler extends AbstractClientMsgHandler { + + public LockUnlockDoorMsgHandler() { + super(LockUnlockDoorMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + // Member variable declarations + + PlayerCharacter player; + Building targetBuilding; + int doorNum; + LockUnlockDoorMsg msg; + + // Member variable assignment + + msg = (LockUnlockDoorMsg) baseMsg; + player = SessionManager.getPlayerCharacter(origin); + targetBuilding = BuildingManager.getBuilding((int) msg.getTargetID()); + + if (player == null || targetBuilding == null) { + Logger.warn("Player or Building returned NULL in LockUnlock msg handling."); + return true; + } + + if (player.getLoc().distanceSquared2D(targetBuilding.getLoc()) > MBServerStatics.OPENCLOSEDOORDISTANCE * MBServerStatics.OPENCLOSEDOORDISTANCE) { + return true; + } + + if (!BuildingManager.playerCanManage(player, targetBuilding)) { + return true; + } + + doorNum = Blueprint.getDoorNumberbyMesh(msg.getDoorID()); + + // Debugging code + + // Logger.debug("DoorLockUnlock", "Door mesh: " + msg.getDoorID() + " Door number: " + doorNum); + + boolean stateChanged; + + if (targetBuilding.isDoorLocked(doorNum)) { + stateChanged = targetBuilding.setDoorState(doorNum, engine.Enum.DoorState.UNLOCKED); + } else { + stateChanged = targetBuilding.setDoorState(doorNum, engine.Enum.DoorState.LOCKED); + } + + if (stateChanged == false) { + Logger.error("WorldServerMsgHandler.LockUnlockDoor", "Failed to update db for building: " + targetBuilding.getObjectUUID() + ", door: " + msg.getDoorID()); + } + + if (targetBuilding.isDoorLocked(doorNum)) { + msg.setUnk1(1); // Which is this, locked or unlocaked? + } else { + msg.setUnk1(0); + } + + Dispatch dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + + return true; + } +} diff --git a/src/engine/net/client/handlers/LoginToGameServerMsgHandler.java b/src/engine/net/client/handlers/LoginToGameServerMsgHandler.java new file mode 100644 index 00000000..175dccc4 --- /dev/null +++ b/src/engine/net/client/handlers/LoginToGameServerMsgHandler.java @@ -0,0 +1,119 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.InterestManagement.WorldGrid; +import engine.exception.MsgSendException; +import engine.gameManager.DbManager; +import engine.gameManager.SessionManager; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.LoginToGameServerMsg; +import engine.net.client.msg.login.LoginErrorMsg; +import engine.objects.Account; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; +import engine.session.CSSession; +import engine.session.Session; +import org.pmw.tinylog.Logger; + +/* + * @Author: + * @Summary: Processes application protocol message which keeps + * logs the character nto the game using the CSession key generated + * by the Login server. + */ + +public class LoginToGameServerMsgHandler extends AbstractClientMsgHandler { + + public LoginToGameServerMsgHandler() { + super(LoginToGameServerMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + LoginToGameServerMsg msg = (LoginToGameServerMsg) baseMsg; + + CSSession sessionInfo = CSSession.getCrossServerSession(msg.getSecKey()); + + if (sessionInfo == null) { + Logger.error("Failed to validate session information from " + origin.getLocalAddressAndPortAsString()); + origin.kickToLogin(MBServerStatics.LOGINERROR_UNABLE_TO_LOGIN, "Unable to validate session data"); + // TODO Evaluate if we need to delete CSSessions here. We couldn't + // find it before, why would this attempt be different? + + return true; + } + + Account acc = sessionInfo.getAccount(); + + if (acc == null) { + String err = "Session returned NULL Account. Conn:" + origin.getLocalAddressAndPortAsString(); + Logger.error(err); + origin.kickToLogin(MBServerStatics.LOGINERROR_UNABLE_TO_LOGIN, err); + return true; + } + + PlayerCharacter pc = sessionInfo.getPlayerCharacter(); + + if (pc == null) { + String err = "Session returned NULL PlayerCharacter. Conn:" + origin.getLocalAddressAndPortAsString(); + + Logger.error(err); + origin.kickToLogin(MBServerStatics.LOGINERROR_UNABLE_TO_LOGIN, err); + return true; + } + + // If account is suspended, kick + + if (acc.status.equals(Enum.AccountStatus.BANNED)) { + origin.kickToLogin(MBServerStatics.LOGINERROR_NO_MORE_PLAYTIME_ON_ACCOUNT, "Account banned."); + return true; + } + + ClientConnection old = SessionManager.getClientConnection(acc); + + if (old != null) + if (old != origin) { + Logger.info("Disconnecting other client connection Using Same Account " + old.getRemoteAddressAndPortAsString()); + old.disconnect(); + } + + // Set machine ID here from CSS info + origin.machineID = sessionInfo.getMachineID(); + + // Send response + msg.setSecKey(""); + + if (!origin.sendMsg(msg)) { + Logger.error("Failed to send message"); + origin.kickToLogin(MBServerStatics.LOGINERROR_UNABLE_TO_LOGIN, "Unable to send ValidateGameServer to client."); + return true; + } + + //# Why was this all changed? + // CLEAN UP OTHER INSTANCES OF THIS CHARACTER + + Session toKill = SessionManager.getSession(sessionInfo.getPlayerCharacter()); + + if (toKill != null) { + if (toKill.getConn() != null) { + LoginErrorMsg lom = new LoginErrorMsg(MBServerStatics.LOGINERROR_UNABLE_TO_LOGIN, "You may not login the same character twice!"); + ClientConnection conn = toKill.getConn(); + if (conn != null && !conn.sendMsg(lom)) + Logger.error("Failed to send message"); // TODO Do we just accept this failure to send Msg? + } + SessionManager.remSession(toKill); + WorldGrid.RemoveWorldObject(sessionInfo.getPlayerCharacter()); + } + Session s = SessionManager.getNewSession(sessionInfo.getAccount(), origin); + SessionManager.setPlayerCharacter(s, sessionInfo.getPlayerCharacter()); + + Logger.info("Login from Account: " + sessionInfo.getAccount().getUname() + " Character: " + + sessionInfo.getPlayerCharacter().getName() + " machineID: " + sessionInfo.getMachineID()); + + DbManager.AccountQueries.SET_ACCOUNT_LOGIN(sessionInfo.getAccount(), sessionInfo.getPlayerCharacter().getName(), origin.getClientIpAddress(), sessionInfo.getMachineID()); + return true; + } + +} \ No newline at end of file diff --git a/src/engine/net/client/handlers/MOTDCommitHandler.java b/src/engine/net/client/handlers/MOTDCommitHandler.java new file mode 100644 index 00000000..b5b0c084 --- /dev/null +++ b/src/engine/net/client/handlers/MOTDCommitHandler.java @@ -0,0 +1,82 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.guild.MOTDCommitMsg; +import engine.objects.Guild; +import engine.objects.GuildStatusController; +import engine.objects.PlayerCharacter; + +public class MOTDCommitHandler extends AbstractClientMsgHandler { + + public MOTDCommitHandler() { + super(MOTDCommitMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + MOTDCommitMsg msg = (MOTDCommitMsg) baseMsg; + Dispatch dispatch; + + // get source player + PlayerCharacter sourcePlayer = SessionManager.getPlayerCharacter( + origin); + + if (sourcePlayer == null) + return true; + + int type = msg.getType(); + + if (type == 0 || type == 1 || type == 3) { + + if (GuildStatusController.isInnerCouncil(sourcePlayer.getGuildStatus()) == false) + return true; + + Guild guild = sourcePlayer.getGuild(); + + if (guild == null) + return true; + + if (type == 1) { // Guild MOTD + guild.setMOTD(msg.getMessage()); + ChatManager.chatGuildMOTD(sourcePlayer, msg.getMessage(), + true); + } else if (type == 3) { // IC MOTD + guild.setICMOTD(msg.getMessage()); + ChatManager + .chatICMOTD(sourcePlayer, msg.getMessage(), true); + } else if (type == 0) { // Nation MOTD + Guild nation = guild.getNation(); + if (nation == null) + return true; + if (nation.isNation()) { // only + // nation's + // primary guild can + // set nation motd + nation.setMOTD(msg.getMessage()); + ChatManager.chatNationMOTD(sourcePlayer, + msg.getMessage(), true); + } + } + dispatch = Dispatch.borrow(sourcePlayer, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + } + + return true; + } + +} diff --git a/src/engine/net/client/handlers/MOTDEditHandler.java b/src/engine/net/client/handlers/MOTDEditHandler.java new file mode 100644 index 00000000..f5d1436a --- /dev/null +++ b/src/engine/net/client/handlers/MOTDEditHandler.java @@ -0,0 +1,83 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum; +import engine.exception.MsgSendException; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.guild.LeaveGuildMsg; +import engine.net.client.msg.guild.MOTDMsg; +import engine.objects.Guild; +import engine.objects.GuildStatusController; +import engine.objects.PlayerCharacter; + +public class MOTDEditHandler extends AbstractClientMsgHandler { + + public MOTDEditHandler() { + super(MOTDMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + MOTDMsg msg = (MOTDMsg) baseMsg; + Dispatch dispatch; + + // get source player + PlayerCharacter playerCharacter = SessionManager.getPlayerCharacter( + origin); + + if (playerCharacter == null) + return true; + + int type = msg.getType(); + + msg.setResponse((byte) 1); + if (type == 0 || type == 1 || type == 3) { + if (GuildStatusController.isInnerCouncil(playerCharacter.getGuildStatus()) == false) { + ErrorPopupMsg.sendErrorMsg(playerCharacter, "You do not have such authority!"); + return true; + } + + Guild guild = playerCharacter.getGuild(); + + if (guild == null || guild.getObjectUUID() == 0) { + + LeaveGuildMsg leaveGuildMsg = new LeaveGuildMsg(); + leaveGuildMsg.setMessage("You do not belong to a guild!"); + dispatch = Dispatch.borrow(playerCharacter, leaveGuildMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + return true; + } + + if (type == 1) // Guild MOTD + msg.setMessage(guild.getMOTD()); + else if (type == 3) // IC MOTD + msg.setMessage(guild.getICMOTD()); + else if (type == 0) { // Nation MOTD + Guild nation = guild.getNation(); + if (nation == null || !nation.isNation()) { + ErrorPopupMsg.sendErrorMsg(playerCharacter, "You do not have such authority!"); + return true; + } + msg.setMessage(nation.getMOTD()); + } + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + return true; + } +} diff --git a/src/engine/net/client/handlers/ManageCityAssetMsgHandler.java b/src/engine/net/client/handlers/ManageCityAssetMsgHandler.java new file mode 100644 index 00000000..6928b207 --- /dev/null +++ b/src/engine/net/client/handlers/ManageCityAssetMsgHandler.java @@ -0,0 +1,364 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.DispatchChannel; +import engine.Enum.GameObjectType; +import engine.exception.MsgSendException; +import engine.gameManager.BuildingManager; +import engine.gameManager.SessionManager; +import engine.gameManager.ZoneManager; +import engine.math.Bounds; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.ManageCityAssetsMsg; +import engine.net.client.msg.PlaceAssetMsg; +import engine.objects.*; +import org.joda.time.DateTime; + +/* + * @Author: + * @Summary: Processes application protocol message which opens + * and processes the various building asset management windows. + */ +public class ManageCityAssetMsgHandler extends AbstractClientMsgHandler { + + public ManageCityAssetMsgHandler() { + super(ManageCityAssetsMsg.class); + } + + public static boolean playerCanManageNotFriends(PlayerCharacter player, Building building){ + + //Player Can only Control Building if player is in Same Guild as Building and is higher rank than IC. + + if (player == null) + return false; + + if (building.getRank() == -1) + return false; + + if (BuildingManager.IsOwner(building, player)) + return true; + + if (GuildStatusController.isGuildLeader(player.getGuildStatus()) == false && GuildStatusController.isInnerCouncil(player.getGuildStatus()) == false) + return false; + + //Somehow guild leader check fails above? lets check if Player is true Guild GL. + if (building.getGuild() != null && building.getGuild().isGuildLeader(player.getObjectUUID())) + return true; + + return Guild.sameGuild(building.getGuild(), player.getGuild()); + + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + ManageCityAssetsMsg msg; + PlayerCharacter player; + ManageCityAssetsMsg outMsg; + Building building; + + msg = (ManageCityAssetsMsg) baseMsg; + + player = SessionManager.getPlayerCharacter(origin); + + if (player == null) + return true; + + building = BuildingManager.getBuildingFromCache(msg.getTargetID()); + + if (building == null){ + if (msg.actionType == 14) { + + Zone zone = ZoneManager.findSmallestZone(player.getLoc()); + + if (!zone.isPlayerCity()){ + ErrorPopupMsg.sendErrorMsg(player, "Unable to find city to command."); + return true; + } + + City city = City.GetCityFromCache(zone.getPlayerCityUUID()); + + if (city == null){ + ErrorPopupMsg.sendErrorMsg(player, "Unable to find city to command."); + return true; + } + + if (!city.getGuild().equals(player.getGuild())){ + ErrorPopupMsg.sendErrorMsg(player, "You are not in the correct guild to command this city."); + return true; + } + + if (!GuildStatusController.isInnerCouncil(player.getGuildStatus()) && !GuildStatusController.isGuildLeader(player.getGuildStatus())){ + ErrorPopupMsg.sendErrorMsg(player, "You must be an Inner Council or Guild leader to access city commands."); + return true; + } + ManageCityAssetsMsg mca = new ManageCityAssetsMsg(player, building); + mca.actionType = 15; + Dispatch dispatch = Dispatch.borrow(player, mca); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + } + return true; + } + + outMsg = new ManageCityAssetsMsg(player, building); + + if (player.isSafeMode()){ + outMsg.actionType = 4; + outMsg.setTargetType(building.getObjectType().ordinal()); + outMsg.setTargetID(building.getObjectUUID()); + outMsg.setAssetName(building.getName()); + Dispatch dispatch = Dispatch.borrow(player, outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + } + + if (msg.actionType == 2 || msg.actionType == 22) { + + if (building.getBlueprint() != null && building.getBlueprint().getBuildingGroup() == engine.Enum.BuildingGroup.BANESTONE) { + + outMsg.actionType = 18; + outMsg.setTargetType(building.getObjectType().ordinal()); + outMsg.setTargetID(building.getObjectUUID()); + + } else if (BuildingManager.playerCanManage(player, building)) { //TODO allow Friends list. + configWindowState(player, building, outMsg); + outMsg.actionType = 3; + outMsg.setTargetType(building.getObjectType().ordinal()); + outMsg.setTargetID(building.getObjectUUID()); + outMsg.setTargetType3(building.getObjectType().ordinal()); + outMsg.setTargetID3(building.getObjectUUID()); + outMsg.setUnknown54(1); + + } else { + + if (building.getBlueprintUUID() != 0) + switch (building.getBlueprint().getBuildingGroup()) { + case SHRINE: + if (building.getRank() == -1) { + if (!Bounds.collide(player.getLoc(), building)) { + ErrorPopupMsg.sendErrorPopup(player, 64); + return true; + } + + Shrine shrine = Shrine.shrinesByBuildingUUID.get(building.getObjectUUID()); + + if (shrine == null) + return true; + + if (shrine.getFavors() == 0) { + ErrorPopupMsg.sendErrorPopup(player, 166); // There is no more favor in this shrine to loot + return true; + } + + BuildingManager.lootBuilding(player, building); + return true; + } + break; + case WAREHOUSE: + //TODO check + if (building.getRank() == -1) { + if (!Bounds.collide(player.getLoc(), building)) { + ErrorPopupMsg.sendErrorPopup(player, 64); + return true; + } + + Warehouse warehouse = Warehouse.warehouseByBuildingUUID.get(building.getObjectUUID()); + + if (warehouse == null) + return true; + + if (warehouse.isEmpty()) { + ErrorPopupMsg.sendErrorPopup(player, 167); // no more resources. + return true; + } + + BuildingManager.lootBuilding(player, building); + return true; + } + } + + if (building.getRank() == -1) + return true; + + AbstractCharacter owner = building.getOwner(); + + //no owner, send building info + if (owner == null) { + msg.actionType = 4; + + Dispatch dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + } + outMsg.actionType = 4; + outMsg.setTargetType(building.getObjectType().ordinal()); + outMsg.setTargetID(building.getObjectUUID()); + outMsg.setAssetName(building.getName()); + + } + Dispatch dispatch = Dispatch.borrow(player, outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + } + + if (msg.actionType == 13) { + outMsg.actionType = 13; + Dispatch dispatch = Dispatch.borrow(player, outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + } + + + + //Rename Building. + + if (msg.actionType == 5) { + + //TODO we need to check names before allowing + building.setName(msg.getAssetName()); + configWindowState(player, building, outMsg); + + outMsg.actionType = 3; + outMsg.setTargetType(building.getObjectType().ordinal()); + outMsg.setTargetID(building.getObjectUUID()); + outMsg.setTargetType3(GameObjectType.Building.ordinal()); + outMsg.setTargetID3(building.getObjectUUID()); + outMsg.setAssetName1(building.getName()); + outMsg.setUnknown54(1); + + Dispatch dispatch = Dispatch.borrow(player, outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + return true; + + //TOL, update city name also + //TODO update city and zone in database + //TODO update city map data in game server + } + + if (msg.actionType == 14) { + ManageCityAssetsMsg mca = new ManageCityAssetsMsg(player, building); + mca.actionType = 15; + Dispatch dispatch = Dispatch.borrow(player, mca); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + } + + if (msg.actionType == 20) { + + Zone baneZone = building.getParentZone(); + + if (baneZone == null) + return true; + + City banedCity = City.getCity(baneZone.getPlayerCityUUID()); + + if (banedCity == null) + return true; + + Bane bane = banedCity.getBane(); + + if (bane == null || bane.getLiveDate() != null || player.getGuild() != banedCity.getGuild() || GuildStatusController.isInnerCouncil(player.getGuildStatus()) == false) + return true; + + int baneHour = msg.getBaneHour(); + + if (baneHour < 16 || baneHour > 24) { + PlaceAssetMsg.sendPlaceAssetError(origin, 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + return true; + } + + DateTime baneLive = new DateTime(bane.getPlacementDate()); + baneLive = baneHour == 24 ? baneLive.plusDays(3) : baneLive.plusDays(2); + baneLive = baneHour == 24 ? baneLive.hourOfDay().setCopy(0) : baneLive.hourOfDay().setCopy(baneHour); + baneLive = baneLive.minuteOfHour().setCopy(0); + baneLive = baneLive.secondOfMinute().setCopy(1); + bane.setLiveDate(baneLive); + outMsg.actionType = 18; + + Dispatch dispatch = Dispatch.borrow(player, outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + } + return true; + } + + public void configWindowState(PlayerCharacter player, Building building, ManageCityAssetsMsg manageCityAssetsMsg) { + + // Tests to turn on upgrade button if a building is not + // at it's maximum allowed rank or currently ranking + + + // Owner is obviously allowed to upgrade his own buildings + + if (building.getOwner().equals(player)) { + + // Players cannot destroy or transfer a TOL. + + if (building.getBlueprint() == null){ + manageCityAssetsMsg.buttonDestroy = 0; + manageCityAssetsMsg.buttonTransfer = 0; + manageCityAssetsMsg.buttonAbandon = 0; + manageCityAssetsMsg.buttonUpgrade = 0; + } + else + if (building.getBlueprint().getBuildingGroup() == Enum.BuildingGroup.TOL) { + manageCityAssetsMsg.buttonDestroy = 0; + manageCityAssetsMsg.buttonTransfer = 0; + manageCityAssetsMsg.buttonAbandon = 1; + manageCityAssetsMsg.buttonUpgrade = 1; + } + else if (building.getBlueprint().getBuildingGroup() == Enum.BuildingGroup.MINE) { + manageCityAssetsMsg.buttonDestroy = 0; + manageCityAssetsMsg.buttonTransfer = 0; + manageCityAssetsMsg.buttonAbandon = 0; + manageCityAssetsMsg.buttonUpgrade = 0; // Cannot upgrade a mine + } + else{ + manageCityAssetsMsg.buttonDestroy = 1; + manageCityAssetsMsg.buttonTransfer = 1; + manageCityAssetsMsg.buttonAbandon = 1; + manageCityAssetsMsg.buttonUpgrade = 1; + } + } + + // Inner Council of the same guild can also upgrade + + if ((player.getGuild().equals(building.getGuild())) && + GuildStatusController.isInnerCouncil(player.getGuildStatus())) + manageCityAssetsMsg.buttonUpgrade = 1; + + // Disable upgrade button if at max rank. + + if (building.getBlueprint() == null) + manageCityAssetsMsg.buttonUpgrade = 0; + else + if (building.getRank() >= building.getBlueprint().getMaxRank()) + manageCityAssetsMsg.buttonUpgrade = 0;; + + // If a building is not protected we can exit here + + if (building.assetIsProtected() == false) + return; + + // Protection is displayed as "UNDER SIEGE" if + // an active bane is invalidating the protection + // contracts of the city. + + if ((building.getCity() != null) && + (building.getCity().protectionEnforced == false)) { + manageCityAssetsMsg.labelProtected = 0; + manageCityAssetsMsg.labelSiege = 1; + manageCityAssetsMsg.labelCeaseFire = 0; + return; + } + + // Building is currently protected by a TOL + + manageCityAssetsMsg.labelProtected = 1; + } +} diff --git a/src/engine/net/client/handlers/MerchantMsgHandler.java b/src/engine/net/client/handlers/MerchantMsgHandler.java new file mode 100644 index 00000000..687a29f9 --- /dev/null +++ b/src/engine/net/client/handlers/MerchantMsgHandler.java @@ -0,0 +1,465 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.GuildHistoryType; +import engine.InterestManagement.RealmMap; +import engine.exception.MsgSendException; +import engine.gameManager.*; +import engine.job.JobScheduler; +import engine.jobs.TeleportJob; +import engine.math.Vector3fImmutable; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.*; +import engine.objects.*; +import engine.powers.PowersBase; +import engine.server.MBServerStatics; + +import java.util.ArrayList; + +/* + * @Author: + * @Summary: Processes a variety of windows the client can open + * such as realm blessings and warehouse deposits. + */ + +public class MerchantMsgHandler extends AbstractClientMsgHandler { + + public MerchantMsgHandler() { + super(MerchantMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + // Member variable declaration + + MerchantMsg msg; + PlayerCharacter player; + NPC npc; + int msgType; + Building warehouse; + Dispatch dispatch; + + // Member variable assignment + + player = SessionManager.getPlayerCharacter(origin); + msg = (MerchantMsg) baseMsg; + npc = NPC.getNPC(msg.getNPCID()); + + // Early exit if something goes awry + + if ((player == null) || (npc == null)) + return true; + + // Player must be within talking range + + if (player.getLoc().distanceSquared2D(npc.getLoc()) > MBServerStatics.NPC_TALK_RANGE * MBServerStatics.NPC_TALK_RANGE) { + ErrorPopupMsg.sendErrorPopup(player, 14); + return true; + } + + // Process application protocol message + + msgType = msg.getType(); + + switch (msgType) { + case 3: + break; + case 5: + + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + requestSwearAsSubGuild(msg, origin, player, npc); + break; + case 10: + teleportRepledgeScreen(msg, origin, player, false, npc); + break; + case 11: + teleportRepledge(msg, origin, player, false, npc); + break; + case 12: + teleportRepledgeScreen(msg, origin, player, true, npc); + break; + case 13: + teleportRepledge(msg, origin, player, true, npc); + break; + case 14: + if (isHermit(npc)) + requestHermitBlessing(msg, origin, player, npc); + else + requestBoon(msg, origin, player, npc); + break; + case 15: + LeaderboardMessage lbm = new LeaderboardMessage(); + dispatch = Dispatch.borrow(player, lbm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + break; + case 16: + ViewResourcesMessage vrm = new ViewResourcesMessage(player); + warehouse = npc.getBuilding(); + vrm.setGuild(player.getGuild()); + vrm.setWarehouseBuilding(warehouse); + vrm.configure(); + dispatch = Dispatch.borrow(player, vrm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + break; + case 17: + Warehouse.warehouseWithdraw(msg, player, npc, origin); + break; + case 18: + Warehouse.warehouseDeposit(msg, player, npc, origin); + break; + case 19: + Warehouse.warehouseLock(msg, player, npc, origin); + break; + } + + return true; + + } + + private static void requestSwearAsSubGuild(MerchantMsg msg, ClientConnection origin, PlayerCharacter player, NPC npc) { + + boolean Disabled = true; + + if (Disabled){ + ErrorPopupMsg.sendErrorMsg(player, "Swearing to Safeholds have been temporary disabled."); //Cannot sub as errant guild. + return; + } + + if (player.getGuild().isErrant()){ + ErrorPopupMsg.sendErrorMsg(player, "You do not belong to a guild!"); //Cannot sub as errant guild. + return; + } + + if (player.getGuild().getNation() != null && !player.getGuild().getNation().isErrant()){ + ErrorPopupMsg.sendErrorMsg(player, "You already belong to a nation!"); //Cannot sub as errant guild. + return; + } + + if (player.getGuild().getGuildLeaderUUID() != player.getObjectUUID()){ + ErrorPopupMsg.sendErrorMsg(player, "You must be a Guild Leader to Swear your guild as a Sub Guild!"); //Cannot sub as errant guild. + return; + } + + if (!GuildStatusController.isGuildLeader(player.getGuildStatus())){ + ErrorPopupMsg.sendErrorMsg(player, "You must be a Guild Leader to Swear your guild as a Sub Guild!"); //Cannot sub as errant guild. + return; + } + + + if (!npc.getGuild().isNPCGuild()){ + ErrorPopupMsg.sendErrorMsg(player, "Runemaster does not belong to a safehold!"); //Cannot sub as errant guild. + return; + } + + if (npc.getGuild().getRepledgeMin() > player.getLevel()){ + ErrorPopupMsg.sendErrorMsg(player, "You are too low of a level to sub to this guild!"); //Cannot sub as errant guild. + return; + } + + if (npc.getGuild().getRepledgeMax() < 75){ + ErrorPopupMsg.sendErrorMsg(player, "Runemaster Guild Cannot Swear in your guild!"); //Cannot sub as errant guild. + return; + } + + + + + + if (!DbManager.GuildQueries.UPDATE_PARENT(player.getGuild().getObjectUUID(), npc.getGuild().getObjectUUID())) { + ErrorPopupMsg.sendErrorMsg(player, "A Serious error has occured. Please post details for to ensure transaction integrity"); + return; + } + + + GuildManager.updateAllGuildBinds(player.getGuild(), npc.getGuild().getOwnedCity()); + + + + //update Guild state. + player.getGuild().setNation(npc.getGuild()); + GuildManager.updateAllGuildTags(player.getGuild()); + + //update state twice, errant to petitioner, to sworn. + player.getGuild().upgradeGuildState(false);//to petitioner + player.getGuild().upgradeGuildState(false);//to sworn + + + + + + } + + + + private static void requestHermitBlessing(MerchantMsg msg, ClientConnection origin, PlayerCharacter player, NPC npc) { + + Guild guild; + Realm realm; + City city; + Building tol; + + // Validate player can obtain blessing + + if (GuildStatusController.isGuildLeader(player.getGuildStatus()) == false) { + ErrorPopupMsg.sendErrorPopup(player, 173); // You must be the leader of a guild to receive a blessing + return; + } + + guild = player.getGuild(); + city = guild.getOwnedCity(); + + if (city == null) { + ErrorPopupMsg.sendErrorPopup(player, 179); // Only landed guilds may claim a territory + return; + } + tol = city.getTOL(); + + if (tol.getRank() != 7) { + ErrorPopupMsg.sendErrorPopup(player, 181); // Your tree must be rank 7 before claiming a territory + return; + } + + realm = RealmMap.getRealmForCity(city); + + if (realm.getCanBeClaimed() == false) { + ErrorPopupMsg.sendErrorPopup(player, 180); // This territory cannot be ruled by anyone + return; + } + + if (realm.isRuled() == true) { + ErrorPopupMsg.sendErrorPopup(player, 178); // This territory is already claimed + return; + } + + // Everything should be good, apply boon for this hermit + + PowersManager.applyPower(player, player, player.getLoc(), getPowerforHermit(npc).getToken(), 40, false); + + } + + private static void requestBoon(MerchantMsg msg, ClientConnection origin, PlayerCharacter player, NPC npc) { + + Building shrineBuilding; + Shrine shrine; + + if (npc.getGuild() != player.getGuild()) + return; + + shrineBuilding = npc.getBuilding(); + + if (shrineBuilding == null) + return; + + if (shrineBuilding.getBlueprint() != null && shrineBuilding.getBlueprint().getBuildingGroup() != engine.Enum.BuildingGroup.SHRINE) + return; + + if (shrineBuilding.getRank() == -1) + return; + + shrine = Shrine.shrinesByBuildingUUID.get(shrineBuilding.getObjectUUID()); + + if (shrine == null) + return; + + if (shrine.getFavors() == 0) { + ErrorPopupMsg.sendErrorPopup(player, 172); + return; + } + + //already haz boon. + + if (player.containsEffect(shrine.getShrineType().getPowerToken())) { + ErrorPopupMsg.sendErrorPopup(player, 199); + return; + } + + if (!Shrine.canTakeFavor(player, shrine)) + return; + + if (!shrine.takeFavor(player)) + return; + + PowersBase shrinePower = PowersManager.getPowerByToken(shrine.getShrineType().getPowerToken()); + + if (shrinePower == null) { + ChatManager.chatSystemError(player, "FAILED TO APPLY POWER!"); + return; + } + + int rank = shrine.getRank(); + //R8 trees always get atleast rank 2 boons. rank uses index, where 0 is first place, 1 is second, etc... + if (shrineBuilding.getCity() != null && shrineBuilding.getCity().getTOL() != null && shrineBuilding.getCity().getTOL().getRank() == 8) + if (rank != 0) + rank = 1; + int trains = 40 - (rank * 10); + if (trains < 0) + trains = 0; + + //System.out.println(trains); + PowersManager.applyPower(player, player, player.getLoc(), shrinePower.getToken(), trains, false); + ChatManager.chatGuildInfo(player.getGuild(), player.getName() + " has recieved a boon costing " + 1 + " point of favor."); + shrineBuilding.addEffectBit(1000000 << 2); + shrineBuilding.updateEffects(); + + //remove the effect so players loading shrines dont see the effect go off. + shrineBuilding.removeEffectBit(1000000 << 2); + } + + private static void teleportRepledgeScreen(MerchantMsg msg, ClientConnection origin, PlayerCharacter pc, boolean isTeleport, NPC npc) { + + Dispatch dispatch; + TeleportRepledgeListMsg trlm; + + //verify npc is runemaster + + Contract contract = npc.getContract(); + + if (contract == null || !contract.isRuneMaster()) + return; + + if (!isTeleport) + trlm = new TeleportRepledgeListMsg(pc, false); + else + trlm = new TeleportRepledgeListMsg(pc, true); + + trlm.configure(); + + dispatch = Dispatch.borrow(pc, trlm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + private static void teleportRepledge(MerchantMsg msg, ClientConnection origin, PlayerCharacter player, boolean isTeleport, NPC npc) { + + //verify npc is runemaster + + Contract contract = npc.getContract(); + Dispatch dispatch; + + if (contract == null || !contract.isRuneMaster()) + return; + + //get city to teleport/repledge to and verify valid + + ArrayList cities; + + City targetCity = null; + + if (isTeleport) + cities = City.getCitiesToTeleportTo(player); + else + cities = City.getCitiesToRepledgeTo(player); + for (City city : cities) { + if (city.getObjectUUID() == msg.getCityID()) { + targetCity = city; + break; + } + } + + if (targetCity == null) + return; + + //verify level required to teleport or repledge + + Guild toGuild = targetCity.getGuild(); + + if (toGuild != null) + if (isTeleport) { + if (player.getLevel() < toGuild.getTeleportMin() || player.getLevel() > toGuild.getTeleportMax()) + return; + } + else if (player.getLevel() < toGuild.getRepledgeMin() || player.getLevel() > toGuild.getRepledgeMax()) + return; + + boolean joinedGuild = false; + + //if repledge, reguild the player + + if (!isTeleport) + joinedGuild = GuildManager.joinGuild(player, targetCity.getGuild(), targetCity.getObjectUUID(), GuildHistoryType.JOIN); + + int time; + + if (!isTeleport) //repledge + time = MBServerStatics.REPLEDGE_TIME_IN_SECONDS; + else + time = MBServerStatics.TELEPORT_TIME_IN_SECONDS; + + //resend message + msg.setTeleportTime(time); + + if ((!isTeleport && joinedGuild) || (isTeleport)) { + + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + //teleport player to city + + Vector3fImmutable teleportLoc; + + if (targetCity.getTOL().getRank() == 8) + teleportLoc = targetCity.getTOL().getStuckLocation(); + else + teleportLoc = Vector3fImmutable.getRandomPointOnCircle(targetCity.getTOL().getLoc(), MBServerStatics.TREE_TELEPORT_RADIUS); + + if (time > 0) { + //TODO add timer to teleport + TeleportJob tj = new TeleportJob(player, npc, teleportLoc, origin, true); + JobScheduler.getInstance().scheduleJob(tj, time * 1000); + } + else if (joinedGuild) { + player.teleport(teleportLoc); + player.setSafeMode(); + } + } + + private static PowersBase getPowerforHermit(NPC npc) { + + int contractID; + PowersBase power; + Contract contract; + + contract = npc.getContract(); + contractID = contract.getContractID(); + power = null; + + switch (contractID) { + case 435579: + power = PowersManager.getPowerByIDString("BLS-POWER"); + break; + case 435580: + power = PowersManager.getPowerByIDString("BLS-FORTUNE"); + break; + case 435581: + power = PowersManager.getPowerByIDString("BLS-WISDOM"); + break; + + } + return power; + } + + private static boolean isHermit(NPC npc) { + + int contractID; + boolean retValue = false; + + contractID = npc.getContractID(); + + switch (contractID) { + case 435579: + case 435580: + case 435581: + retValue = true; + break; + default: + break; + } + + return retValue; + } + +} \ No newline at end of file diff --git a/src/engine/net/client/handlers/MinionTrainingMsgHandler.java b/src/engine/net/client/handlers/MinionTrainingMsgHandler.java new file mode 100644 index 00000000..c0bd4819 --- /dev/null +++ b/src/engine/net/client/handlers/MinionTrainingMsgHandler.java @@ -0,0 +1,316 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.DispatchChannel; +import engine.InterestManagement.WorldGrid; +import engine.ai.MobileFSM; +import engine.exception.MsgSendException; +import engine.gameManager.BuildingManager; +import engine.gameManager.DbManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.*; +import engine.objects.*; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.HashMap; + +/* + * @Author: + * @Summary: Processes application protocol message which + * processes training of minions in guard barracks + */ + +public class MinionTrainingMsgHandler extends AbstractClientMsgHandler { + + public static HashMap> _minionsByCaptain = null; + + public MinionTrainingMsgHandler() { + super(MinionTrainingMessage.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + MinionTrainingMessage minionMsg = (MinionTrainingMessage) baseMsg; + + PlayerCharacter player = SessionManager.getPlayerCharacter(origin); + + if (player == null) + return true; + + if (minionMsg.getNpcType() == Enum.GameObjectType.NPC.ordinal()){ + + NPC npc = NPC.getFromCache(minionMsg.getNpcID()); + + if (npc == null) + return true; + + Building b = BuildingManager.getBuildingFromCache(minionMsg.getBuildingID()); + + if (b == null) + return true; + + //clear minion + + if (npc.minionLock.writeLock().tryLock()) { + try { + if (minionMsg.getType() == 2) { + + Mob toRemove = Mob.getFromCache(minionMsg.getUUID()); + + if (!npc.getSiegeMinionMap().containsKey(toRemove)) + return true; + + toRemove.setState(MobileFSM.STATE.Disabled); + npc.getSiegeMinionMap().remove(toRemove); + + //toRemove.disableIntelligence(); + WorldGrid.RemoveWorldObject(toRemove); + + if (toRemove.getParentZone() != null) + toRemove.getParentZone().zoneMobSet.remove(toRemove); + + DbManager.removeFromCache(toRemove); + PlayerCharacter petOwner = toRemove.getOwner(); + + if (petOwner != null) { + petOwner.setPet(null); + toRemove.setOwner(null); + PetMsg petMsg = new PetMsg(5, null); + Dispatch dispatch = Dispatch.borrow(petOwner, petMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + } + + // we Found the move to remove, lets break the for loop so it doesnt look for more. + + ManageCityAssetsMsg mca1 = new ManageCityAssetsMsg(player, b); + mca1.actionType = 3; + mca1.setTargetType(b.getObjectType().ordinal()); + mca1.setTargetID(b.getObjectUUID()); + + mca1.setTargetType3(npc.getObjectType().ordinal()); + mca1.setTargetID3(npc.getObjectUUID()); + mca1.setAssetName1(b.getName()); + mca1.setUnknown54(1); + + Dispatch dispatch = Dispatch.borrow(player, mca1); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + ManageNPCMsg mnm = new ManageNPCMsg(npc); + dispatch = Dispatch.borrow(player, mnm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + //Add Minion + } + else { + Zone zone = npc.getParentZone(); + + if (zone == null) + return true; + + int maxSlots = 3; + + if (npc.getContractID() == 842) + maxSlots = 1; + + if (npc.getSiegeMinionMap().size() == maxSlots) + return true; + + int mobBase; + + switch (minionMsg.getMinion()){ + case 9: + mobBase = 13171; + break; + case 2: + mobBase = 13758; + break; + case 3: + mobBase = 13757; + break; + case 4: + mobBase = 2111; + break; + case 5: + mobBase = 12402; + break; + case 6: + mobBase = 2113; + break; + default: + mobBase = minionMsg.getMinion(); + } + + if (mobBase == 0) + return true; + + Mob toCreate = npc.createSiegeMob(mobBase, npc.getGuild(), zone, b.getLoc(), (short) 1); + + if (toCreate == null) + return true; + + // toCreate.despawn(); + if (toCreate != null) { + toCreate.setSpawnTime(60 * 15); + toCreate.setTimeToSpawnSiege(System.currentTimeMillis() + (60 * 15 * 1000)); + toCreate.setDeathTime(System.currentTimeMillis()); + toCreate.setState(MobileFSM.STATE.Respawn); + } + } + + ManageNPCMsg mnm = new ManageNPCMsg(npc); + mnm.setMessageType(1); + Dispatch dispatch = Dispatch.borrow(player, mnm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + } finally { + npc.minionLock.writeLock().unlock(); + } + } + + }else if (minionMsg.getNpcType() == Enum.GameObjectType.Mob.ordinal()){ + + Mob npc = Mob.getFromCache(minionMsg.getNpcID()); + + if (npc == null) + return true; + + Building b = BuildingManager.getBuildingFromCache(minionMsg.getBuildingID()); + + if (b == null) + return true; + + //clear minion + + if (npc.minionLock.writeLock().tryLock()) { + try { + if (minionMsg.getType() == 2) { + + Mob toRemove = Mob.getFromCache(minionMsg.getUUID()); + if (!npc.getSiegeMinionMap().containsKey(toRemove)) + return true; + + if (!DbManager.MobQueries.REMOVE_FROM_GUARDS(npc.getObjectUUID(), toRemove.getMobBaseID(), npc.getSiegeMinionMap().get(toRemove))) + return true; + + toRemove.setState(MobileFSM.STATE.Disabled); + npc.getSiegeMinionMap().remove(toRemove); + + //toRemove.disableIntelligence(); + WorldGrid.RemoveWorldObject(toRemove); + + if (toRemove.getParentZone() != null) + toRemove.getParentZone().zoneMobSet.remove(toRemove); + + DbManager.removeFromCache(toRemove); + PlayerCharacter petOwner = toRemove.getOwner(); + + if (petOwner != null) { + petOwner.setPet(null); + toRemove.setOwner(null); + PetMsg petMsg = new PetMsg(5, null); + Dispatch dispatch = Dispatch.borrow(petOwner, petMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + } + + // we Found the move to remove, lets break the for loop so it doesnt look for more. + + ManageCityAssetsMsg mca1 = new ManageCityAssetsMsg(player, b); + mca1.actionType = 3; + mca1.setTargetType(b.getObjectType().ordinal()); + mca1.setTargetID(b.getObjectUUID()); + + mca1.setTargetType3(npc.getObjectType().ordinal()); + mca1.setTargetID3(npc.getObjectUUID()); + mca1.setAssetName1(b.getName()); + mca1.setUnknown54(1); + + Dispatch dispatch = Dispatch.borrow(player, mca1); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY);; + + ManageNPCMsg mnm = new ManageNPCMsg(npc); + dispatch = Dispatch.borrow(player, mnm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + //Add Minion + } + else { + Zone zone = npc.getParentZone(); + + if (zone == null) + return true; + + int maxSlots = 5; + + if (npc.getContract().getContractID() == 842) + maxSlots = 1; + + switch (npc.getRank()){ + case 1: + case 2: + maxSlots = 1; + break; + case 3: + maxSlots = 2; + break; + case 4: + case 5: + maxSlots = 3; + break; + case 6: + maxSlots = 4; + break; + case 7: + maxSlots = 5; + break; + } + + if (npc.getSiegeMinionMap().size() == maxSlots) + return true; + + int mobBase = npc.getMobBaseID(); + + if (mobBase == 0) + return true; + + String pirateName = NPC.getPirateName(mobBase); + + if (!DbManager.MobQueries.ADD_TO_GUARDS(npc.getObjectUUID(), mobBase, pirateName, npc.getSiegeMinionMap().size() + 1)) + return true; + + Mob toCreate = npc.createGuardMob(mobBase, npc.getGuild(), zone, b.getLoc(), npc.getLevel(),pirateName); + + if (toCreate == null) + return true; + + // toCreate.despawn(); + if (toCreate != null) { + toCreate.setTimeToSpawnSiege(System.currentTimeMillis() + MBServerStatics.FIFTEEN_MINUTES); + toCreate.setDeathTime(System.currentTimeMillis()); + toCreate.setState(MobileFSM.STATE.Respawn); + } + } + + ManageNPCMsg mnm = new ManageNPCMsg(npc); + mnm.setMessageType(1); + Dispatch dispatch = Dispatch.borrow(player, mnm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + }catch (Exception e){ + Logger.error(e); + }finally { + + npc.minionLock.writeLock().unlock(); + } + } + + } + return true; + } + +} \ No newline at end of file diff --git a/src/engine/net/client/handlers/MoveToPointHandler.java b/src/engine/net/client/handlers/MoveToPointHandler.java new file mode 100644 index 00000000..f699b7b0 --- /dev/null +++ b/src/engine/net/client/handlers/MoveToPointHandler.java @@ -0,0 +1,38 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.exception.MsgSendException; +import engine.gameManager.MovementManager; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.MoveToPointMsg; +import engine.objects.PlayerCharacter; + +public class MoveToPointHandler extends AbstractClientMsgHandler { + + public MoveToPointHandler() { + super(MoveToPointMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, + ClientConnection origin) throws MsgSendException { + MoveToPointMsg msg = (MoveToPointMsg) baseMsg; + + PlayerCharacter pc = (origin != null) ? (origin.getPlayerCharacter()) : null; + if (pc == null) + return false; + + MovementManager.movement(msg, pc); + return true; + } + +} diff --git a/src/engine/net/client/handlers/ObjectActionMsgHandler.java b/src/engine/net/client/handlers/ObjectActionMsgHandler.java new file mode 100644 index 00000000..10de0018 --- /dev/null +++ b/src/engine/net/client/handlers/ObjectActionMsgHandler.java @@ -0,0 +1,569 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.BuildingGroup; +import engine.Enum.DispatchChannel; +import engine.Enum.ItemType; +import engine.InterestManagement.RealmMap; +import engine.InterestManagement.WorldGrid; +import engine.exception.MsgSendException; +import engine.gameManager.*; +import engine.math.Bounds; +import engine.math.Vector3fImmutable; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.*; +import engine.objects.*; +import engine.powers.PowersBase; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/* + * @Author: + * @Summary: Processes application protocol message which actives + * items such as charters and deeds in the character's inventory + */ +public class ObjectActionMsgHandler extends AbstractClientMsgHandler { + + // Reentrant lock for dropping banes + + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + + public ObjectActionMsgHandler() { + super(ObjectActionMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + // Member variable declaration + ObjectActionMsg msg; + PlayerCharacter player; + CharacterItemManager itemMan; + ArrayList comps; + Dispatch dispatch; + boolean waterbucketBypass = false; + + // Member variable assignment + msg = (ObjectActionMsg) baseMsg; + player = SessionManager.getPlayerCharacter(origin); + + if (player == null) { + return true; + } + + itemMan = player.getCharItemManager(); + + if (itemMan == null) { + return true; + } + + comps = msg.getTargetCompID(); + + if (comps.isEmpty()) { + return true; + } + + long comp = comps.get(0); + + if (((int) comp) != 0) { + Item item = Item.getFromCache((int) comp); + + if (item == null) { + return true; + } + + //dupe check + if (!item.validForInventory(origin, player, itemMan)) { + return true; + } + + ItemBase ib = item.getItemBase(); + + if (ib == null) { + return true; + } + + if (itemMan.doesCharOwnThisItem(item.getObjectUUID())) { + + if (ib.isConsumable() || ib.getType() == ItemType.FARMABLE) { + + int uuid = ib.getUUID(); + int type = ib.getType().getValue(); + + switch (type) { + case 27: //Mithril repair + break; + case 10: //charters + //don't think they're handled here? + break; + case 19: //buildings + //Call add building screen here, ib.getUseID() get's building ID + + //if inside player city, center loc on tol. otherwise center on player. + Vector3fImmutable loc = player.getLoc(); + Zone zone = ZoneManager.findSmallestZone(player.getLoc()); + + if (zone != null) { + if (zone.isPlayerCity()) { + loc = zone.getLoc(); + } + } + + PlaceAssetMsg pam = new PlaceAssetMsg(); + pam.setActionType(2); + pam.setContractID(item.getObjectUUID()); + pam.setX(loc.getX() + 64); //offset grid from tol + pam.setY(loc.getY()); + pam.setZ(loc.getZ() + 64); //offset grid from tol + pam.addPlacementInfo(ib.getUseID()); + + dispatch = Dispatch.borrow(player, pam); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + //itemMan.consume(item); //temporary fix for dupe.. TODO Make Item Unusable after This message is sent. + break; + case 25: //furniture + //Call add furniture screen here. ib.getUseID() get's furniture ID + break; + case 33: + long shrineCompID = comps.get(1); + Building shrineBuilding = BuildingManager.getBuilding((int)shrineCompID); + if (shrineBuilding == null) { + return true; + } + if (shrineBuilding.getBlueprint() != null && shrineBuilding.getBlueprint().getBuildingGroup() != engine.Enum.BuildingGroup.SHRINE) { + return true; + } + + if (shrineBuilding.getRank() == -1) { + return true; + } + Shrine shrine = Shrine.shrinesByBuildingUUID.get(shrineBuilding.getObjectUUID()); + + if (shrine == null) { + return true; + } + + if (shrine.addFavor(player, item)) { + shrineBuilding.addEffectBit(1000000 << 2); + shrineBuilding.updateEffects(); + shrineBuilding.removeEffectBit(1000000 << 2); + } + break; + + case 35: + int charterType = 0; + switch (uuid) { + case 910020: + charterType = 762228431; + break; + case 910021: + charterType = -15978914; + break; + case 910022: + charterType = -600065291; + break; + } + if (claimRealm(player, charterType) == true) { + itemMan.consume(item); + } + break; + case 7: //rod of command + long compID = comps.get(1); + + int objectType = AbstractWorldObject.extractTypeID(compID).ordinal(); + Mob toCommand; + if (objectType == engine.Enum.GameObjectType.Mob.ordinal()) { + toCommand = Mob.getFromCache((int)compID); + } //Only Command Mob Types. + else { + return true; + } + + if (toCommand == null) { + return true; + } + + if (!toCommand.isSiege()) + return true; + + if (player.commandSiegeMinion(toCommand)) { + itemMan.consume(item); + } + break; + //ANNIVERSERY GIFT + case 31: + + + if (ib.getUUID() == 971012){ + int random = ThreadLocalRandom.current().nextInt(ItemBase.AnniverseryGifts.size()); + int annyID = ItemBase.AnniverseryGifts.get(random); + + ItemBase annyIB = ItemBase.getItemBase(annyID); + if (annyIB != null){ + Item gift = MobLoot.createItemForPlayer(player, annyIB); + if (gift != null){ + itemMan.addItemToInventory(gift); + itemMan.consume(item); + } + } + break; + } + + LootTable.CreateGamblerItem(item, player); + + + break; + + case 30: //water bucket + case 8: //potions, tears of saedron + + case 5: //runes, petition, warrant, scrolls + if (uuid > 3000 && uuid < 3050) { //Discipline Runes + if (ApplyRuneMsg.applyRune(uuid, origin, player)) { + itemMan.consume(item); + } + break; + } else if (uuid > 249999 && uuid < 250123) { //stat and mastery runes + if (ApplyRuneMsg.applyRune(uuid, origin, player)) { + itemMan.consume(item); + } + break; + } else if (uuid > 250114 && uuid < 250123) { //mastery runes + if (ApplyRuneMsg.applyRune(uuid, origin, player)) { + itemMan.consume(item); + } + break; + } else if (uuid > 252122 && uuid < 252128) { //mastery runes + if (ApplyRuneMsg.applyRune(uuid, origin, player)) { + itemMan.consume(item); + } + break; + } else if (uuid > 680069 && uuid < 680074) //Handle Charter, Deed, Petition, Warrant here + { + break; + } else if (uuid > 910010 && uuid < 910019) { + + int rank = uuid - 910010; + + if (rank < 1 || rank > 8) { + ChatManager.chatSystemError(player, "Invalid Rank for bane scroll!"); + return true; + } + // Only one banestone at a time + lock.writeLock().lock(); + + try { + if (Bane.summonBanestone(player, origin, rank) == true) + itemMan.consume(item); + } finally { + lock.writeLock().unlock(); + } + break; + } else if (uuid == 910010) { //tears of saedron + if (comps.size() > 1) { + removeRune(player, origin, comps.get(1).intValue()); + } + break; + } + + else if (item.getChargesRemaining() > 0) { + ArrayList tarList = msg.getTargetCompID(); + AbstractWorldObject target = player; + if (tarList.size() > 1) { + long tarID = tarList.get(1); + if (tarID != 0) { + AbstractGameObject tarAgo = AbstractGameObject.getFromTypeAndID(tarID); + if (tarAgo != null && tarAgo instanceof AbstractWorldObject) { + target = (AbstractWorldObject) tarAgo; + } + } + } + + // Bypass for waterbuckets + + // test character targeted + + if (ib.getUUID() == 910005) { + + // test for valid target type + if (target.getObjectType() == Enum.GameObjectType.PlayerCharacter) + waterbucketBypass = true; + else { + // test distance to structure + Building targetBuilding = (Building) target; + Bounds testBounds = Bounds.borrow(); + testBounds.setBounds(player.getLoc(), 25); + + if (Bounds.collide(targetBuilding.getBounds(), testBounds, .1f) == false) { + ChatManager.chatSystemError(player, "Not in range of structura for to heal!"); + return true; + } + } + + // Send piss bucket animation + + VisualUpdateMessage vum = new VisualUpdateMessage(player, 16323); + vum.configure(); + DispatchMessage.sendToAllInRange(player, vum); + } + + if (waterbucketBypass == false) + PowersManager.applyPower(player, target, Vector3fImmutable.ZERO, ib.getUseID(), ib.getUseAmount(), true); + + itemMan.consume(item); + } else //just remove the item at this point + itemMan.consume(item); + + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + player.cancelOnSpell(); + break; + default: //shouldn't be here, consume item + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + // itemMan.consume(item); + } + } + } else { + // TODO log item does not belong to player + // System.out.println("Item does not belong to player"); + // Cleanup duped item here + } + } + + return true; + } + + private static boolean claimRealm(PlayerCharacter player, int charterUUID) { + + Guild guild; + Realm realm; + City city; + Building tol; + float hPMod; + Warehouse warehouse; + boolean hasResources = true; + int resourceValue; + + if (GuildStatusController.isGuildLeader(player.getGuildStatus()) == false) { + ErrorPopupMsg.sendErrorPopup(player, 176); // Only guild leaders can claim a territory + return false; + } + + guild = player.getGuild(); + city = guild.getOwnedCity(); + + if (city == null) { + ErrorPopupMsg.sendErrorPopup(player, 179); // Only landed guilds may claim a territory + return false; + } + + if (city.isLocationOnCityGrid(player.getLoc()) == false) { + ErrorPopupMsg.sendErrorPopup(player, 186); // Your tree is not inside a territory! + return false; + } + + tol = city.getTOL(); + + if (tol.getRank() != 7) { + ErrorPopupMsg.sendErrorPopup(player, 181); // Your tree must be rank 7 before claiming a territory + return false; + } + + realm = RealmMap.getRealmForCity(city); + + if (realm.getCanBeClaimed() == false) { + ErrorPopupMsg.sendErrorPopup(player, 180); // This territory cannot be ruled by anyone + return false; + } + + if (realm.isRuled() == true) { + ErrorPopupMsg.sendErrorPopup(player, 178); // This territory is already claimed + return false; + } + + if (!Realm.HasAllBlessings(player)) { + ErrorPopupMsg.sendErrorPopup(player, 185); // You must seek the blessing of the three sages before you can rule + return false; + } + + // Must have the required resources in warehouse to claim realm + + warehouse = city.getWarehouse(); + + if (warehouse == null) { + ErrorPopupMsg.sendErrorPopup(player, 188); // You must have a warehouse to become a capital + return false; + } + + resourceValue = warehouse.getResources().get(Warehouse.goldIB); + + if (resourceValue < 5000000) + hasResources = false; + + resourceValue = warehouse.getResources().get(Warehouse.stoneIB); + + if (resourceValue < 8000) + hasResources = false; + + resourceValue = warehouse.getResources().get(Warehouse.lumberIB); + + if (resourceValue < 8000) + hasResources = false; + + resourceValue = warehouse.getResources().get(Warehouse.galvorIB); + + if (resourceValue < 15) + hasResources = false; + + resourceValue = warehouse.getResources().get(Warehouse.wormwoodIB); + + if (resourceValue < 15) + hasResources = false; + + if (hasResources == false) { + ErrorPopupMsg.sendErrorPopup(player, 184); // Insufficient gold or resources to upgrade to capital + return false; + } + + // Remove resources from warehouse before claiming realm + + resourceValue = warehouse.getResources().get(Warehouse.goldIB); + + if (DbManager.WarehouseQueries.updateGold(warehouse, resourceValue - 5000000) == true) { + warehouse.getResources().put(Warehouse.goldIB, resourceValue - 5000000); + warehouse.AddTransactionToWarehouse(engine.Enum.GameObjectType.Building, tol.getObjectUUID(), Enum.TransactionType.WITHDRAWL, Resource.GOLD, 5000000); + } else { + Logger.error("gold update failed for warehouse of UUID:" + warehouse.getObjectUUID()); + return false; + } + + resourceValue = warehouse.getResources().get(Warehouse.stoneIB); + + if (DbManager.WarehouseQueries.updateStone(warehouse, resourceValue - 8000) == true) { + warehouse.getResources().put(Warehouse.stoneIB, resourceValue - 8000); + warehouse.AddTransactionToWarehouse(engine.Enum.GameObjectType.Building, tol.getObjectUUID(), Enum.TransactionType.WITHDRAWL, Resource.STONE, 8000); + } else { + Logger.error( "stone update failed for warehouse of UUID:" + warehouse.getObjectUUID()); + return false; + } + + resourceValue = warehouse.getResources().get(Warehouse.lumberIB); + + if (DbManager.WarehouseQueries.updateLumber(warehouse, resourceValue - 8000) == true) { + warehouse.getResources().put(Warehouse.lumberIB, resourceValue - 8000); + warehouse.AddTransactionToWarehouse(engine.Enum.GameObjectType.Building, tol.getObjectUUID(), Enum.TransactionType.WITHDRAWL, Resource.LUMBER, 8000); + } else { + Logger.error("lumber update failed for warehouse of UUID:" + warehouse.getObjectUUID()); + return false; + } + + resourceValue = warehouse.getResources().get(Warehouse.galvorIB); + + if (DbManager.WarehouseQueries.updateGalvor(warehouse, resourceValue - 15) == true) { + warehouse.getResources().put(Warehouse.galvorIB, resourceValue - 15); + warehouse.AddTransactionToWarehouse(engine.Enum.GameObjectType.Building, tol.getObjectUUID(), Enum.TransactionType.WITHDRAWL, Resource.GALVOR, 15); + } else { + Logger.error("galvor update failed for warehouse of UUID:" + warehouse.getObjectUUID()); + return false; + } + + resourceValue = warehouse.getResources().get(Warehouse.wormwoodIB); + + if (DbManager.WarehouseQueries.updateWormwood(warehouse, resourceValue - 15) == true) { + warehouse.getResources().put(Warehouse.wormwoodIB, resourceValue - 15); + warehouse.AddTransactionToWarehouse(engine.Enum.GameObjectType.Building, tol.getObjectUUID(), Enum.TransactionType.WITHDRAWL, Resource.WORMWOOD, 15); + } else { + Logger.error("wormwood update failed for warehouse of UUID:" + warehouse.getObjectUUID()); + return false; + } + + realm.claimRealmForCity(city, charterUUID); + + tol.setRank(8); + WorldGrid.updateObject(tol); + + for (Building building : city.getParent().zoneBuildingSet) { + + if (building.getBlueprintUUID() != 0) { + + // TOL Health set through regular linear equation + if (building.getBlueprint().getBuildingGroup() == BuildingGroup.TOL) { + continue; + } + + hPMod = (building.getMaxHitPoints() * Realm.getRealmHealthMod(city)); + building.setMaxHitPoints(building.getMaxHitPoints() + hPMod); + } + } + + if (!guild.getNation().equals(guild)) { + guild.getNation().setRealmsOwned(guild.getNation().getRealmsOwned() + 1); + GuildManager.updateAllGuildTags(guild.getNation()); + } + + guild.setRealmsOwned(guild.getRealmsOwned() + 1); + GuildManager.updateAllGuildTags(guild); + + removeAllBlessings(player); + + return true; + + } + + private static void removeAllBlessings(PlayerCharacter player) { + + PowersBase[] powers = new PowersBase[3]; + + powers[0] = PowersManager.getPowerByIDString("BLS-POWER"); + powers[1] = PowersManager.getPowerByIDString("BLS-FORTUNE"); + powers[2] = PowersManager.getPowerByIDString("BLS-WISDOM"); + + for (PowersBase power : powers) { + PowersManager.removeEffect(player, power.getActions().get(0), true, false); + } + + } + // Handle activation of tears of seadron: Removes rune from player. + + private static void removeRune(PlayerCharacter pc, ClientConnection origin, int runeID) { + + if (pc == null || origin == null) { + return; + } + + //remove only if rune is discipline + if (runeID < 3001 || runeID > 3048) { + return; + } + + //see if pc has rune + ArrayList runes = pc.getRunes(); + + if (runes == null) + return; + + CharacterRune found = pc.getRune(runeID); + + if (found == null) + return; + + //TODO see if player needs to refine skills or powers first + //attempt remove rune from player + + if (!CharacterRune.removeRune(pc, runeID)) + return; + + //update client with removed rune. + ApplyRuneMsg arm = new ApplyRuneMsg(pc.getObjectType().ordinal(), pc.getObjectUUID(), runeID); + Dispatch dispatch = Dispatch.borrow(pc, arm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + } + +} diff --git a/src/engine/net/client/handlers/OpenFriendsCondemnListMsgHandler.java b/src/engine/net/client/handlers/OpenFriendsCondemnListMsgHandler.java new file mode 100644 index 00000000..27fc4083 --- /dev/null +++ b/src/engine/net/client/handlers/OpenFriendsCondemnListMsgHandler.java @@ -0,0 +1,397 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.DispatchChannel; +import engine.Enum.GameObjectType; +import engine.exception.MsgSendException; +import engine.gameManager.BuildingManager; +import engine.gameManager.DbManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.OpenFriendsCondemnListMsg; +import engine.objects.*; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; + +/* + * @Author: + * @Summary: Processes application protocol message which handles + * client requests for various lists + */ + +public class OpenFriendsCondemnListMsgHandler extends AbstractClientMsgHandler { + + public OpenFriendsCondemnListMsgHandler() { + super(OpenFriendsCondemnListMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter player = SessionManager.getPlayerCharacter(origin); + Building sourceBuilding; + OpenFriendsCondemnListMsg msg; + OpenFriendsCondemnListMsg openFriendsCondemnListMsg; + Enum.FriendListType friendListType; + Dispatch dispatch; + + if (player == null) + return true; + + msg = (OpenFriendsCondemnListMsg) baseMsg; + openFriendsCondemnListMsg = new OpenFriendsCondemnListMsg(msg); + friendListType = Enum.FriendListType.getListTypeByID(msg.getMessageType()); + + if (friendListType == null){ + Logger.error("Invalid FriendListType for messageType " + msg.getMessageType()); + return true; + } + + switch (friendListType) { + case VIEWHERALDRY: // Heraldry + + Heraldry.ValidateHeraldry(player.getObjectUUID()); + OpenFriendsCondemnListMsg outMsg = new OpenFriendsCondemnListMsg(msg); + outMsg.setOrigin(origin); + outMsg.setMessageType(2); + dispatch = Dispatch.borrow(player, outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + break; + + case ADDHERALDRY: + Heraldry.ValidateHeraldry(player.getObjectUUID()); + if (msg.getPlayerID() <= 0){ + //ErrorPopupMsg.sendErrorMsg(player, "Invalid Heraldry Object."); + return true; + } + AbstractCharacter toAdd = null; + if (msg.getPlayerType() == GameObjectType.PlayerCharacter.ordinal()) + toAdd = PlayerCharacter.getFromCache(msg.getPlayerID()); + else if (msg.getPlayerType() == GameObjectType.NPC.ordinal()) + toAdd = NPC.getFromCache(msg.getPlayerID()); + else if (msg.getPlayerType() == GameObjectType.Mob.ordinal()) + toAdd = Mob.getFromCache(msg.getPlayerID()); + else{ + ErrorPopupMsg.sendErrorMsg(player, "Invalid Heraldry Object."); + return true; + } + + if (toAdd == null){ + ErrorPopupMsg.sendErrorMsg(player, "Invalid Heraldry Object."); + return true; + } + + Heraldry.AddToHeraldy(player.getObjectUUID(), toAdd); + + + outMsg = new OpenFriendsCondemnListMsg(msg); + outMsg.setOrigin(origin); + outMsg.setMessageType(2); + dispatch = Dispatch.borrow(player, outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + break; + case REMOVEHERALDRY: + Heraldry.ValidateHeraldry(player.getObjectUUID()); + Heraldry.RemoveFromHeraldy(player.getObjectUUID(), msg.getPlayerID()); + + + outMsg = new OpenFriendsCondemnListMsg(msg); + outMsg.setOrigin(origin); + outMsg.setMessageType(2); + dispatch = Dispatch.borrow(player, outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + break; + + case DEALTHS: // Death List + openFriendsCondemnListMsg.updateMsg(8, new ArrayList<>(player.pvpDeaths)); + dispatch = Dispatch.borrow(player, openFriendsCondemnListMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + break; + + case KILLS: // Kill List + openFriendsCondemnListMsg.updateMsg(10, new ArrayList<>(player.pvpKills)); + dispatch = Dispatch.borrow(player, openFriendsCondemnListMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + break; + + case VIEWCONDEMN: + + sourceBuilding = BuildingManager.getBuildingFromCache(msg.getBuildingID()); + + if (sourceBuilding == null) + return true; + + openFriendsCondemnListMsg = new OpenFriendsCondemnListMsg(12, sourceBuilding.getCondemned(), sourceBuilding.reverseKOS); + openFriendsCondemnListMsg.configure(); + dispatch = Dispatch.borrow(player, openFriendsCondemnListMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + //msg.updateMsg(12, DbManager.GuildQueries.) + break; + //REMOVE CONDEMN + case REMOVECONDEMN: + + sourceBuilding = BuildingManager.getBuildingFromCache(msg.getBuildingID()); + + if (sourceBuilding == null) + return true; + + if (!BuildingManager.PlayerCanControlNotOwner(sourceBuilding, player)) + return true; + + Condemned removeCondemn = sourceBuilding.getCondemned().get(msg.getRemoveFriendID()); + + if (removeCondemn == null) + return true; + + if (!DbManager.BuildingQueries.REMOVE_FROM_CONDEMNED_LIST(removeCondemn.getParent(), removeCondemn.getPlayerUID(), removeCondemn.getGuildUID(), removeCondemn.getFriendType())) + return true; + + sourceBuilding.getCondemned().remove(msg.getRemoveFriendID()); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + break; + + case TOGGLEACTIVE: + + sourceBuilding = BuildingManager.getBuildingFromCache(msg.getBuildingID()); + + if (sourceBuilding == null) + return true; + + if (!BuildingManager.PlayerCanControlNotOwner(sourceBuilding, player)) + return true; + + Condemned condemn = sourceBuilding.getCondemned().get(msg.getRemoveFriendID()); + + if (condemn == null) + return true; + + condemn.setActive(msg.isReverseKOS()); + openFriendsCondemnListMsg.setReverseKOS(condemn.isActive()); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + break; + case REVERSEKOS: + + + sourceBuilding = BuildingManager.getBuildingFromCache(msg.getBuildingID()); + + if (sourceBuilding == null) + return true; + + if (!BuildingManager.PlayerCanControlNotOwner(sourceBuilding, player)) + return true; + + if (!sourceBuilding.setReverseKOS(msg.isReverseKOS())) + return true; + break; + + //ADD GUILD CONDEMN + case ADDCONDEMN: + + sourceBuilding = BuildingManager.getBuildingFromCache(msg.getBuildingID()); + + if (sourceBuilding == null) + return true; + + if (!BuildingManager.PlayerCanControlNotOwner(sourceBuilding, player)) + return true; + + switch (msg.getInviteType()) { + case 2: + + if (msg.getPlayerID() == 0) + return true; + + if (msg.getPlayerType() != GameObjectType.PlayerCharacter.ordinal()) + return true; + + PlayerCharacter playerCharacter = PlayerCharacter.getFromCache(msg.getPlayerID()); + + if (playerCharacter == null) + return true; + + if (Guild.sameNationExcludeErrant(sourceBuilding.getGuild(), playerCharacter.getGuild())) + return true; + + if (sourceBuilding.getCondemned().containsKey(playerCharacter.getObjectUUID())) + return true; + + if (!DbManager.BuildingQueries.ADD_TO_CONDEMNLIST(sourceBuilding.getObjectUUID(), playerCharacter.getObjectUUID(), msg.getGuildID(), msg.getInviteType())) { + Logger.debug( "Failed to add Condemned: " + playerCharacter.getFirstName() + " to Building With UID " + sourceBuilding.getObjectUUID()); + return true; + } + + sourceBuilding.getCondemned().put(playerCharacter.getObjectUUID(), new Condemned(playerCharacter.getObjectUUID(), sourceBuilding.getObjectUUID(), msg.getGuildID(), msg.getInviteType())); + break; + case 4: + if (msg.getGuildID() == 0) + return true; + + if (sourceBuilding.getCondemned().containsKey(msg.getGuildID())) + return true; + + Guild condemnedGuild = Guild.getGuild(msg.getGuildID()); + + if (condemnedGuild == null) + return true; + + if (!DbManager.BuildingQueries.ADD_TO_CONDEMNLIST(sourceBuilding.getObjectUUID(), msg.getPlayerID(), condemnedGuild.getObjectUUID(), msg.getInviteType())) { + Logger.debug("Failed to add Condemned: " + condemnedGuild.getName() + " to Building With UID " + sourceBuilding.getObjectUUID()); + return true; + } + + sourceBuilding.getCondemned().put(condemnedGuild.getObjectUUID(), new Condemned(msg.getPlayerID(), sourceBuilding.getObjectUUID(), condemnedGuild.getObjectUUID(), msg.getInviteType())); + break; + case 5: + if (msg.getNationID() == 0) + return true; + + if (sourceBuilding.getCondemned().containsKey(msg.getNationID())) + return true; + + Guild condemnedNation = Guild.getGuild(msg.getNationID()); + + if (condemnedNation == null) + return true; + + if (!DbManager.BuildingQueries.ADD_TO_CONDEMNLIST(sourceBuilding.getObjectUUID(), msg.getPlayerID(), condemnedNation.getObjectUUID(), msg.getInviteType())) { + Logger.debug( "Failed to add Condemned: " + condemnedNation.getName() + " to Building With UID " + sourceBuilding.getObjectUUID()); + return true; + } + + sourceBuilding.getCondemned().put(condemnedNation.getObjectUUID(), new Condemned(msg.getPlayerID(), sourceBuilding.getObjectUUID(), condemnedNation.getObjectUUID(), msg.getInviteType())); + break; + + } + + openFriendsCondemnListMsg = new OpenFriendsCondemnListMsg(12, sourceBuilding.getCondemned(), sourceBuilding.reverseKOS); + openFriendsCondemnListMsg.configure(); + dispatch = Dispatch.borrow(player, openFriendsCondemnListMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + break; + + //ADD FRIEND BUILDING + case ADDFRIEND: + sourceBuilding = BuildingManager.getBuildingFromCache(msg.getBuildingID()); + + if (sourceBuilding == null) + return true; + + if (msg.getGuildID() == 0) + return true; + + if (!BuildingManager.PlayerCanControlNotOwner(sourceBuilding, player)) + return true; + + PlayerCharacter playerCharacter = null; + + + + Guild guildInvited = Guild.getGuild(msg.getGuildID()); + + if (guildInvited == null) + return true; + + //Check to see if the invited is already on the friends list. + switch (msg.getInviteType()) { + case 7: + playerCharacter = PlayerCharacter.getFromCache(msg.getPlayerID()); + if (playerCharacter == null) + return true; + if (sourceBuilding.getFriends().containsKey(playerCharacter.getObjectUUID())) + return true; + break; + case 8: + case 9: + if (sourceBuilding.getFriends().containsKey(guildInvited.getObjectUUID())) + return true; + break; + } + + if (!DbManager.BuildingQueries.ADD_TO_FRIENDS_LIST(sourceBuilding.getObjectUUID(), msg.getPlayerID(), guildInvited.getObjectUUID(), msg.getInviteType())) { + Logger.debug( "Failed to add Friend: " + playerCharacter.getFirstName() + " to Building With UID " + sourceBuilding.getObjectUUID()); + return true; + } + + switch (msg.getInviteType()) { + case 7: + sourceBuilding.getFriends().put(playerCharacter.getObjectUUID(), new BuildingFriends(playerCharacter.getObjectUUID(), sourceBuilding.getObjectUUID(), playerCharacter.getGuild().getObjectUUID(), 7)); + break; + case 8: + sourceBuilding.getFriends().put(guildInvited.getObjectUUID(), new BuildingFriends(msg.getPlayerID(), sourceBuilding.getObjectUUID(), guildInvited.getObjectUUID(), 8)); + break; + case 9: + sourceBuilding.getFriends().put(guildInvited.getObjectUUID(), new BuildingFriends(msg.getPlayerID(), sourceBuilding.getObjectUUID(), guildInvited.getObjectUUID(), 9)); + break; + } + + openFriendsCondemnListMsg = new OpenFriendsCondemnListMsg(26, sourceBuilding.getFriends()); + openFriendsCondemnListMsg.configure(); + + dispatch = Dispatch.borrow(player, openFriendsCondemnListMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + break; + //REMOVE from friends list. + case REMOVEFRIEND: + sourceBuilding = BuildingManager.getBuildingFromCache(msg.getBuildingID()); + + if (sourceBuilding == null) + return true; + + if (!BuildingManager.PlayerCanControlNotOwner(sourceBuilding, player)) + return true; + + BuildingFriends friend = sourceBuilding.getFriends().get(msg.getRemoveFriendID()); + + if (friend == null) + return true; + + if (!DbManager.BuildingQueries.REMOVE_FROM_FRIENDS_LIST(sourceBuilding.getObjectUUID(), friend.getPlayerUID(), friend.getGuildUID(), friend.getFriendType())) { + Logger.debug( "Failed to remove Friend: " + msg.getRemoveFriendID() + " from Building With UID " + sourceBuilding.getObjectUUID()); + return true; + } + sourceBuilding.getFriends().remove(msg.getRemoveFriendID()); + + openFriendsCondemnListMsg = new OpenFriendsCondemnListMsg(26, sourceBuilding.getFriends()); + openFriendsCondemnListMsg.configure(); + dispatch = Dispatch.borrow(player, openFriendsCondemnListMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + break; + //view Friends + case VIEWFRIENDS: + + Building building = BuildingManager.getBuildingFromCache(msg.getBuildingID()); + + if (building == null) + return true; + + if (!BuildingManager.PlayerCanControlNotOwner(building, player)) + return true; + + //this message is sent twice back????? + + openFriendsCondemnListMsg = new OpenFriendsCondemnListMsg(26, building.getFriends()); + openFriendsCondemnListMsg.configure(); + + + dispatch = Dispatch.borrow(player, openFriendsCondemnListMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + break; + + default: + break; + } + return false; + } + +} \ No newline at end of file diff --git a/src/engine/net/client/handlers/OrderNPCMsgHandler.java b/src/engine/net/client/handlers/OrderNPCMsgHandler.java new file mode 100644 index 00000000..3335727c --- /dev/null +++ b/src/engine/net/client/handlers/OrderNPCMsgHandler.java @@ -0,0 +1,575 @@ +package engine.net.client.handlers; + +import engine.Enum.DispatchChannel; +import engine.Enum.GameObjectType; +import engine.Enum.ProfitType; +import engine.exception.MsgSendException; +import engine.gameManager.BuildingManager; +import engine.gameManager.DbManager; +import engine.gameManager.SessionManager; +import engine.math.FastMath; +import engine.math.Vector3fImmutable; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.*; +import engine.objects.*; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; + +/* + * @Author: + * @Summary: Processes application protocol message which keeps + * client's tcp connection open. + */ +public class OrderNPCMsgHandler extends AbstractClientMsgHandler { + + // Constants used for incoming message type + + private static final int CLIENT_UPGRADE_REQUEST = 3; + private static final int CLIENT_REDEED_REQUEST = 6; + private static final int SVR_CLOSE_WINDOW = 4; + + public OrderNPCMsgHandler() { + super(OrderNPCMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + // Member variable declarations + + PlayerCharacter player; + NPC npc; + Mob mob; + Building building; + OrderNPCMsg orderNPCMsg; + ManageCityAssetsMsg outMsg; + + // Member variable assignment + orderNPCMsg = (OrderNPCMsg) baseMsg; + + if (origin.ordernpcspam > System.currentTimeMillis()) + return true; + + origin.ordernpcspam = System.currentTimeMillis() + 500; + + player = SessionManager.getPlayerCharacter(origin); + + if (player == null) + return true; + + if (orderNPCMsg.getActionType() == 28) { + OrderNPCMsgHandler.handleCityCommand(orderNPCMsg, player); + return true; + } + + if (orderNPCMsg.getObjectType() == GameObjectType.NPC.ordinal()) { + + npc = NPC.getFromCache(orderNPCMsg.getNpcUUID()); + + if (npc == null) + return true; + + building = BuildingManager.getBuildingFromCache(orderNPCMsg.getBuildingUUID()); + + if (building == null) + return true; + + if (building.getHirelings().containsKey(npc) == false) + return true; + + + if (player.getCharItemManager().getTradingWith() != null) { + ErrorPopupMsg.sendErrorMsg(player, "Cannot barter and trade with same timings."); + return true; + } + + player.lastBuildingAccessed = building.getObjectUUID(); + + switch (orderNPCMsg.getActionType()) { + + case 2: + player = SessionManager.getPlayerCharacter(origin); + + if (ManageCityAssetMsgHandler.playerCanManageNotFriends(player, building) == false) + return true; + + if (building.getHirelings().containsKey(npc) == false) + return true; + + if (npc.remove() == false) { + PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + return true; + } + + ManageCityAssetsMsg manageCityAssetsMsg = new ManageCityAssetsMsg(); + manageCityAssetsMsg.actionType = SVR_CLOSE_WINDOW; + manageCityAssetsMsg.setTargetType(building.getObjectType().ordinal()); + manageCityAssetsMsg.setTargetID(building.getObjectUUID()); + + Dispatch dispatch = Dispatch.borrow(player, manageCityAssetsMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + return true; + + case CLIENT_UPGRADE_REQUEST: + + if (BuildingManager.playerCanManage(player, building) == false) + return true; + + processUpgradeNPC(player, npc); + + outMsg = new ManageCityAssetsMsg(player, building); + + // Action TYPE + outMsg.actionType = 3; + outMsg.setTargetType(building.getObjectType().ordinal()); + outMsg.setTargetID(building.getObjectUUID()); + outMsg.setTargetType3(building.getObjectType().ordinal()); + outMsg.setTargetID3(building.getObjectUUID()); + outMsg.setAssetName1(building.getName()); + outMsg.setUnknown54(1); + + dispatch = Dispatch.borrow(player, outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + break; + case CLIENT_REDEED_REQUEST: + + if (BuildingManager.PlayerCanControlNotOwner(building, player) == false) + return true; + + processRedeedNPC(npc, building, origin); + return true; + //MB TODO HANDLE all profits. + case 7: + case 8: + case 9: + + if (BuildingManager.PlayerCanControlNotOwner(building, player) == false) + return true; + + modifySellProfit(orderNPCMsg, origin); + dispatch = Dispatch.borrow(player, orderNPCMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 10: + case 11: + case 12: + + if (BuildingManager.PlayerCanControlNotOwner(building, player) == false) + return true; + + modifyBuyProfit(orderNPCMsg, origin); + dispatch = Dispatch.borrow(player, orderNPCMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + } + + // Validation check Owner or IC or friends + if (BuildingManager.PlayerCanControlNotOwner(building, player) == false) + if (BuildingManager.playerCanManage(player, building) == false) + return true; + + ManageNPCMsg manageNPCMsg = new ManageNPCMsg(npc); + Dispatch dispatch = Dispatch.borrow(player, manageNPCMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + + } else if (orderNPCMsg.getObjectType() == GameObjectType.Mob.ordinal()) { + + mob = Mob.getFromCacheDBID(orderNPCMsg.getNpcUUID()); + + if (mob == null) + return true; + + building = BuildingManager.getBuildingFromCache(orderNPCMsg.getBuildingUUID()); + + if (building == null) + return true; + + if (!building.getHirelings().containsKey(mob)) + return true; + + if (player.getCharItemManager().getTradingWith() != null) { + ErrorPopupMsg.sendErrorMsg(player, "Cannot barter and trade with same timings."); + return true; + } + + player.lastBuildingAccessed = building.getObjectUUID(); + + switch (orderNPCMsg.getActionType()) { + case 2: + + if (BuildingManager.playerCanManage(player, building) == false) + return true; + + if (building.getHirelings().containsKey(mob) == false) + return true; + + if (mob.remove(building) == false) { + PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + return true; + } + + ManageCityAssetsMsg manageCityAssetsMsg = new ManageCityAssetsMsg(); + manageCityAssetsMsg.actionType = SVR_CLOSE_WINDOW; + manageCityAssetsMsg.setTargetType(building.getObjectType().ordinal()); + manageCityAssetsMsg.setTargetID(building.getObjectUUID()); + Dispatch dispatch = Dispatch.borrow(player, manageCityAssetsMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + break; + case 3: + + if (BuildingManager.PlayerCanControlNotOwner(building, player) == false) + return true; + + processUpgradeNPC(player, mob); + + outMsg = new ManageCityAssetsMsg(player, building); + + // Action TYPE + outMsg.actionType = 3; + outMsg.setTargetType(building.getObjectType().ordinal()); + outMsg.setTargetID(building.getObjectUUID()); + outMsg.setTargetType3(building.getObjectType().ordinal()); + outMsg.setTargetID3(building.getObjectUUID()); + outMsg.setAssetName1(building.getName()); + outMsg.setUnknown54(1); + + dispatch = Dispatch.borrow(player, outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + break; + case 6: + + if (BuildingManager.PlayerCanControlNotOwner(building, player) == false) + return true; + + processRedeedNPC(mob, building, origin); + return true; + //MB TODO HANDLE all profits. + case 7: + case 8: + case 9: + break; + case 10: + case 11: + case 12: + break; + } + + // Validation check Owner or IC + if (BuildingManager.PlayerCanControlNotOwner(building, player) == false) + if (BuildingManager.playerCanManage(player, building) == false) + return true; + + ManageNPCMsg manageNPCMsg = new ManageNPCMsg(mob); + Dispatch dispatch = Dispatch.borrow(player, manageNPCMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + } + return true; + } + + private static void modifyBuyProfit(OrderNPCMsg msg, ClientConnection origin) { + NPC npc; + PlayerCharacter player; + Building building; + float percent; + + ProfitType profitType = null; + player = origin.getPlayerCharacter(); + + if (player == null) + return; + + npc = NPC.getFromCache(msg.getNpcUUID()); + + if (npc == null) + return; + + building = npc.getBuilding(); + + if (building == null) + return; + + NPCProfits profit = NPC.GetNPCProfits(npc); + + if (profit == null) + return; + + switch (msg.getActionType()) { + case 10: + profitType = ProfitType.BuyNormal; + break; + case 11: + profitType = ProfitType.BuyGuild; + break; + case 12: + profitType = ProfitType.BuyNation; + } + + percent = msg.getBuySellPercent(); + percent = FastMath.clamp(percent, 0.0f, 1.0f); + + NPCProfits.UpdateProfits(npc, profit, profitType, percent); + } + + private static void modifySellProfit(OrderNPCMsg orderNPCMsg, ClientConnection origin) { + NPC npc; + PlayerCharacter player; + Building building; + float percent; + + ProfitType profitType = null; + + player = origin.getPlayerCharacter(); + + if (player == null) + return; + + npc = NPC.getFromCache(orderNPCMsg.getNpcUUID()); + + if (npc == null) + return; + + building = npc.getBuilding(); + + if (building == null) + return; + + NPCProfits profit = NPC.GetNPCProfits(npc); + + if (profit == null) + return; + + switch (orderNPCMsg.getActionType()) { + case 7: + profitType = ProfitType.SellNormal; + break; + case 8: + profitType = ProfitType.SellGuild; + break; + case 9: + profitType = ProfitType.SellNation; + } + + percent = orderNPCMsg.getBuySellPercent(); + + percent -= 1f; + percent = FastMath.clamp(percent, 0.0f, 3.0f); + + NPCProfits.UpdateProfits(npc, profit, profitType, percent); + } + + private static void handleCityCommand(OrderNPCMsg orderNpcMsg, PlayerCharacter player) { + + Building building = BuildingManager.getBuildingFromCache(orderNpcMsg.getBuildingUUID()); + + if (building == null) + return; + + if (ManageCityAssetMsgHandler.playerCanManageNotFriends(player, building) == false) + return; + + if (orderNpcMsg.getPatrolSize() >= 20) + Logger.info(player.getName() + " is attempting to add patrol points amount " + orderNpcMsg.getPatrolSize()); + + if (orderNpcMsg.getSentrySize() >= 20) + Logger.info(player.getName() + " is attempting to add patrol points amount " + orderNpcMsg.getSentryPoints()); + + if (orderNpcMsg.getPatrolPoints() != null) { + + if ( !AddPatrolPoints(building.getObjectUUID(), orderNpcMsg.getPatrolPoints())){ + ErrorPopupMsg.sendErrorMsg(player, "Patrol Points must be placed on city zone."); + return; + } + + for (AbstractCharacter guard : building.getHirelings().keySet()) { + if (guard.getObjectType() == GameObjectType.Mob) + ((Mob) guard).setPatrolPointIndex(0); + } + } else if (building.getPatrolPoints() != null) + ClearPatrolPoints(building.getObjectUUID()); + + if (orderNpcMsg.getSentryPoints() != null) { + AddSentryPoints(building.getObjectUUID(), orderNpcMsg.getSentryPoints()); + } else if (building.getSentryPoints() != null) + ClearSentryPoints(building.getObjectUUID()); + + // Dispatch dispatch = Dispatch.borrow(pc, msg); + // DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + } + + private static void processUpgradeNPC(PlayerCharacter player, AbstractCharacter abstractCharacter) { + + Building building; + + switch (abstractCharacter.getObjectType()) { + + case NPC: + NPC npc = (NPC) abstractCharacter; + building = npc.getBuilding(); + + // Cannot upgrade an npc not within a building + + if (building == null) + return; + + City buildingCity = building.getCity(); + + if (buildingCity == null) { + npc.processUpgradeNPC(player); + return; + } + + buildingCity.transactionLock.writeLock().lock(); + + try { + npc.processUpgradeNPC(player); + } catch (Exception e) { + Logger.error(e); + } finally { + buildingCity.transactionLock.writeLock().unlock(); + } + break; + case Mob: + + Mob mob = (Mob) abstractCharacter; + building = mob.getBuilding(); + + if (mob.getBuilding() == null) + return; + + City mobCity = building.getCity(); + + if (mobCity == null) { + mob.processUpgradeMob(player); + return; + } + + mobCity.transactionLock.writeLock().lock(); + + try { + mob.processUpgradeMob(player); + } catch (Exception e) { + // TODO Auto-generated catch block + Logger.error(e); + } finally { + mobCity.transactionLock.writeLock().unlock(); + } + break; + } + } + + private synchronized void processRedeedNPC(AbstractCharacter abstractCharacter, Building building, ClientConnection origin) { + + // Member variable declaration + + switch (abstractCharacter.getObjectType()) { + case NPC: + NPC npc = (NPC) abstractCharacter; + + Building cityBuilding = npc.getBuilding(); + + if (cityBuilding == null) + return; + + npc.processRedeedNPC(origin); + break; + case Mob: + Mob mob = (Mob) abstractCharacter; + mob.processRedeedMob(origin); + break; + } + } + + private static boolean AddPatrolPoints(int buildingID, ArrayList patrolPoints) { + + Building building = BuildingManager.getBuildingFromCache(buildingID); + + if (building == null) + return false; + + Zone zone = building.getParentZone(); + + if (zone == null) + return false; + + if (zone.getPlayerCityUUID() == 0) + return false; + + City city = building.getCity(); + + if (city == null) + return false; + + + + //clear first. + + for (Vector3fImmutable point : patrolPoints) { + + if (city.isLocationOnCityZone(point) == false){ + return false; + } + + } + + DbManager.BuildingQueries.CLEAR_PATROL(buildingID); + + for (Vector3fImmutable point : patrolPoints) { + + if (!DbManager.BuildingQueries.ADD_TO_PATROL(buildingID, point)) + return false; + } + building.patrolPoints = patrolPoints; + return true; + } + + private static boolean AddSentryPoints(int buildingID, ArrayList sentryPoints) { + + Building building = BuildingManager.getBuildingFromCache(buildingID); + + if (building == null) + return false; + + building.sentryPoints = sentryPoints; + return true; + } + + private static boolean ClearPatrolPoints(int buildingID) { + + Building building = BuildingManager.getBuildingFromCache(buildingID); + + if (building == null) + return false; + + if (building.patrolPoints == null) + return true; + + if (DbManager.BuildingQueries.CLEAR_PATROL(buildingID) == false) + return false; + + building.patrolPoints.clear(); + return true; + } + + private static boolean ClearSentryPoints(int buildingID) { + + Building building = BuildingManager.getBuildingFromCache(buildingID); + + if (building == null) + return false; + + if (building.sentryPoints == null) + return true; + + building.sentryPoints.clear(); + return true; + } + +} \ No newline at end of file diff --git a/src/engine/net/client/handlers/PlaceAssetMsgHandler.java b/src/engine/net/client/handlers/PlaceAssetMsgHandler.java new file mode 100644 index 00000000..6b4c1c84 --- /dev/null +++ b/src/engine/net/client/handlers/PlaceAssetMsgHandler.java @@ -0,0 +1,1380 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.*; +import engine.InterestManagement.HeightMap; +import engine.InterestManagement.RealmMap; +import engine.InterestManagement.WorldGrid; +import engine.db.archive.CityRecord; +import engine.db.archive.DataWarehouse; +import engine.exception.MsgSendException; +import engine.gameManager.*; +import engine.math.Bounds; +import engine.math.Vector3fImmutable; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.CityZoneMsg; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.PlaceAssetMsg; +import engine.net.client.msg.PlaceAssetMsg.PlacementInfo; +import engine.objects.*; +import engine.server.MBServerStatics; +import org.joda.time.DateTime; +import org.pmw.tinylog.Logger; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/* + * @Summary: Processes application protocol message which requests + * creation of new city / buildings from seeds/deeds in inventory. + */ +public class PlaceAssetMsgHandler extends AbstractClientMsgHandler { + + // Useful constants + // ActionType 1 = client request + // 2 = Server confirms open window + // 3 = Request to place asset + // 4 = Server confirms/close window + private static final int CLIENTREQ_UNKNOWN = 1; + private static final int SERVER_OPENWINDOW = 2; + private static final int CLIENTREQ_NEWBUILDING = 3; // Request to place asset + private static final int SERVER_CLOSEWINDOW = 4; + + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + + public PlaceAssetMsgHandler() { + + super(PlaceAssetMsg.class); + + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + // Member variable declaration + + PlaceAssetMsg msg; + Boolean buildingCreated; + + // Character location and session + + PlayerCharacter playerCharacter; + PlacementInfo buildingList; + Blueprint buildingBlueprint; + + // Tell compiler it's ok to trust us and parse + // what we need from the message structure + + msg = (PlaceAssetMsg) baseMsg; + + // Action type 3 is a client requesting to place an object + // For all other action types let's just early exit + + if (msg.getActionType() != CLIENTREQ_NEWBUILDING) + return true; + + // assign our character + + playerCharacter = SessionManager.getPlayerCharacter(origin); + + // We need to figure out what exactly the player is attempting + // to place, as some objects like tol/bane/walls are edge cases. + // So let's get the first item in their list. + + buildingList = msg.getFirstPlacementInfo(); + + // Early exit if null building list. + + if (buildingList == null) { + Logger.error("Player " + playerCharacter.getCombinedName() + + " null building list on deed use"); + PlaceAssetMsg.sendPlaceAssetError(origin, 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + closePlaceAssetWindow(origin); + return true; + } + + Item contract = null; + + for (Item inventoryItem : playerCharacter.getInventory()) { + if (inventoryItem.getItemBase().getUseID() == buildingList.getBlueprintUUID()) { + contract = inventoryItem; + break; + } + } + + // Grab the blueprint from the uuid in the message + + buildingBlueprint = Blueprint.getBlueprint(buildingList.getBlueprintUUID()); + + // Early exit if blueprint can't be retrieved for the object. + + if (buildingBlueprint == null) { + Logger.error("Player " + playerCharacter.getCombinedName() + + " null blueprint UUID: " + buildingList.getBlueprintUUID() + " on deed use"); + PlaceAssetMsg.sendPlaceAssetError(origin, 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + closePlaceAssetWindow(origin); + return true; + } + + // Let's now attempt to place the building + + buildingCreated = false; + + // Many buildings have particular validation and + // post-creation cleanup requirements. + + boolean close = true; + lock.writeLock().lock(); + + try { + switch (buildingBlueprint.getBuildingGroup()) { + + case TOL: + if (contract == null) + break; + buildingCreated = placeTreeOfLife(playerCharacter, origin, msg); + break; + case WAREHOUSE: + if (contract == null) + break; + if (!playerCharacter.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID())) + break; + buildingCreated = placeWarehouse(playerCharacter, origin, msg); + break; + case SIEGETENT: + case BULWARK: + if (contract == null) + break; + if (!playerCharacter.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID())) + break; + buildingCreated = placeSiegeEquip(playerCharacter, origin, msg); + break; + case SPIRE: + if (contract == null) + break; + if (!playerCharacter.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID())) + break; + buildingCreated = placeSpire(playerCharacter, origin, msg); + break; + case SHRINE: + if (contract == null) + break; + if (!playerCharacter.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID())) + break; + buildingCreated = placeShrine(playerCharacter, origin, msg); + break; + case BARRACK: + if (contract == null) + break; + if (!playerCharacter.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID())) + break; + buildingCreated = placeBarrack(playerCharacter, origin, msg); + break; + case WALLSTRAIGHT: + case WALLCORNER: + case SMALLGATE: + case ARTYTOWER: + case WALLSTAIRS: + buildingCreated = placeCityWalls(playerCharacter, origin, msg); + close = false; + break; + default: + if (contract == null) + break; + if (!playerCharacter.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID())) + break; + buildingCreated = placeSingleBuilding(playerCharacter, origin, msg); + break; + } + } catch (Exception e) { + Logger.error("PlaceAssetHandler", e.getMessage()); + e.printStackTrace(); + } finally { + lock.writeLock().unlock(); + } + + // Update the player's last contract (What is this used for?) + + playerCharacter.setLastContract(msg.getContractID()); + + // Remove the appropiate deed. + if (buildingCreated == true) + if (contract != null) { + playerCharacter.getCharItemManager().delete(contract); + playerCharacter.getCharItemManager().updateInventory(); + } + + // Close the window. We're done! + //DONT CLOSE THE WINDOW IF WALL KTHANX + + if (close) + closePlaceAssetWindow(origin); + return true; + } + + // Default method: Validates and places all buildings that do not + // require special treatment in some fashion. + + private boolean placeSingleBuilding(PlayerCharacter playerCharacter, ClientConnection origin, PlaceAssetMsg msg) { + + PlacementInfo buildingList; + Zone serverZone; + + // Retrieve the building details we're placing + + buildingList = msg.getFirstPlacementInfo(); + + serverZone = ZoneManager.findSmallestZone(buildingList.getLoc()); + + // Early exit if something went horribly wrong + // with locating the current or zone + + if (serverZone == null) { + Logger.error("Null zone in placeSingleBuilding"); + return false; + } + + // Method checks validation conditions arising when placing + // buildings. Player must be on a city grid, must be + // inner council of the city's guild, etc. + + if (validateBuildingPlacement(serverZone, msg, origin, playerCharacter, buildingList) == false) + return false; // Close window here? + + // Place the building + if (createStructure(playerCharacter, buildingList, serverZone) == null) { + PlaceAssetMsg.sendPlaceAssetError(origin, 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + return false; + } + + return true; + } + + private boolean placeWarehouse(PlayerCharacter player, ClientConnection origin, PlaceAssetMsg msg) { + + Zone serverZone; + City cityObject; + PlacementInfo buildingList; + + // Retrieve the building details we're placing + + buildingList = msg.getFirstPlacementInfo(); + + // Setup working variables we'll need + + serverZone = ZoneManager.findSmallestZone(buildingList.getLoc()); + + // Early exit if something went horribly wrong + + if (serverZone == null) + return false; + + cityObject = City.getCity(serverZone.getPlayerCityUUID()); + + // Early exit if something went horribly wrong + + if (cityObject == null) + return false; + + // Method checks validation conditions arising when placing + // buildings. Player must be on a city grid, must be + // inner council of the city's guild, etc. + + if (validateCityBuildingPlacement(serverZone, msg, origin, player, buildingList) == false) + return false; + + if (cityObject.getWarehouse() != null) { + PlaceAssetMsg.sendPlaceAssetError(origin, 50, ""); //"You can only have one warehouse" + return false; + } + + // Create the warehouse object and it's entry in the database + + if (createWarehouse(player, msg.getFirstPlacementInfo(), serverZone) == false) { + PlaceAssetMsg.sendPlaceAssetError(origin, 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + return false; + } + + return true; + } + + private boolean placeSiegeEquip(PlayerCharacter player, ClientConnection origin, PlaceAssetMsg msg) { + + Zone serverZone; + Building siegeBuilding; + PlacementInfo buildingList; + City serverCity; + int numAttackerBuildings = 0; + int numDefenderBuildings = 0; + + // Retrieve the building details we're placing + + buildingList = msg.getFirstPlacementInfo(); + + // Setup working variables we'll need + + serverZone = ZoneManager.findSmallestZone(buildingList.getLoc()); + + // Early exit if something went horribly wrong + // with locating the current city and/or zone + + if (serverZone == null) { + Logger.error("Error obtaining reference to zone"); + return false; + } + + // Checks validation conditions arising when placing + // generic structures. + + if (validateBuildingPlacement(serverZone, msg, origin, player, buildingList) == false) + return false; + + // If there is a bane placed, only the attackers and defenders can + // place siege assets + + serverCity = ZoneManager.getCityAtLocation(buildingList.getLoc()); + + //no city found + //check if attacker city. + if (serverCity == null){ + Bane bane = Bane.getBaneByAttackerGuild(player.getGuild()); + City attackerCity = null; + if (bane != null) + attackerCity = bane.getCity(); + + if (attackerCity != null) + if (buildingList.getLoc().isInsideCircle(attackerCity.getLoc(), Enum.CityBoundsType.SIEGE.extents)) + serverCity = attackerCity; + } + //no city found for attacker city, + //check if defender city + + if (serverCity == null){ + if (player.getGuild().getOwnedCity() != null) + if (buildingList.getLoc().isInsideCircle(player.getGuild().getOwnedCity().getLoc(), Enum.CityBoundsType.SIEGE.extents)) + serverCity = player.getGuild().getOwnedCity(); + } + + + + if ((serverCity != null) && + (serverCity.getBane() != null)) { + + // Set the server zone to the city zone in order to account for being inside + // the siege bounds buffer area + + serverZone = serverCity.getParent(); + + if ((player.getGuild().equals(serverCity.getBane().getOwner().getGuild()) == false) + && (player.getGuild().equals(serverCity.getGuild()) == false)) { + PlaceAssetMsg.sendPlaceAssetError(origin, 54, ""); // Must belong to attacker or defender + return false; + } + } + + // cant place siege equipment off city zone. + + + // Create the siege Building + + siegeBuilding = createStructure(player, msg.getFirstPlacementInfo(), serverZone); + if (serverCity == null) + return true; + // Oops something went really wrong + + if (siegeBuilding == null) + return false; + + + if (serverCity.getBane() == null) + return true; + + // If there is an bane placed, we protect 2x the stone rank's worth of attacker assets + // and 1x the tree rank's worth of assets automatically + + HashSet awoList = WorldGrid.getObjectsInRangePartial(serverCity, 1000, MBServerStatics.MASK_BUILDING); + + + + for (AbstractWorldObject awo : awoList) { + Building building = (Building)awo; + + if (building.getBlueprint() != null) + if (!building.getBlueprint().isSiegeEquip()) + continue; + + if (!building.getLoc().isInsideCircle(serverCity.getLoc(), Enum.CityBoundsType.SIEGE.extents)) + continue; + + if (building.getGuild() == null) + continue; + + if (building.getGuild().isErrant()) + continue; + + + if (!building.getGuild().equals(serverCity.getGuild()) && !building.getGuild().equals(serverCity.getBane().getOwner().getGuild())) + continue; + + // Only count auto protected buildings + if (building.getProtectionState() != ProtectionState.PROTECTED) + continue; + + if (building.getGuild().equals(serverCity.getGuild())) + numDefenderBuildings++; + else + if (building.getGuild().equals(serverCity.getBane().getOwner().getGuild())) + numAttackerBuildings++; + + + } + + // Validate bane limits on siege assets + + if (serverCity.getBane() != null) + if ((player.getGuild().equals(serverCity.getBane().getOwner().getGuild())) && + (numAttackerBuildings >= serverCity.getBane().getStone().getRank() * 2)) { + return true; + } + + if ((player.getGuild().equals(serverCity.getGuild())) && + (numDefenderBuildings >= serverCity.getTOL().getRank())) { + return true; + } + + + + // passes validation: can assign auto-protection to war asset + + if (serverCity.getBane() != null) + if (serverCity.isLocationOnCityGrid(siegeBuilding.getBounds())) + if (player.getGuild().equals(serverCity.getBane().getOwner().getGuild())) + return true; + + + + + + + siegeBuilding.setProtectionState(ProtectionState.PROTECTED); + // No bane placed. We're done! + + + return true; + } + + private boolean placeTreeOfLife(PlayerCharacter playerCharacter, ClientConnection origin, PlaceAssetMsg msg) { + + Realm serverRealm; + Zone serverZone; + ArrayList cityObjects; // MySql result set + PlacementInfo treeInfo; + Building treeObject = null; + City cityObject = null; + Zone cityZone = null; + Guild playerNation; + PlacementInfo treePlacement = msg.getFirstPlacementInfo(); + + // Setup working variables we'll need + + serverRealm = RealmMap.getRealmAtLocation(treePlacement.getLoc()); + serverZone = ZoneManager.findSmallestZone(treePlacement.getLoc()); + + // Early exit if something went horribly wrong + // with locating the current realm and/or zone + + if (serverRealm == null || serverZone == null) + return false; + + // Method checks validation conditions arising when placing + // trees + + if (validateTreeOfLifePlacement(playerCharacter, serverRealm, serverZone, origin, msg) == false) + return false; + + // Retrieve tree info for the w value it's passing. + + treeInfo = msg.getFirstPlacementInfo(); + + if (treeInfo == null) { + PlaceAssetMsg.sendPlaceAssetError(origin, 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + return false; + } + + Vector3fImmutable plantLoc = new Vector3fImmutable(treeInfo.getLoc().x, + serverZone.getHeightMap().getInterpolatedTerrainHeight(treeInfo.getLoc()), + treeInfo.getLoc().z); + + cityObjects = DbManager.CityQueries.CREATE_CITY(playerCharacter.getObjectUUID(), serverZone.getObjectUUID(), + serverRealm.getRealmID(), + plantLoc.x - serverZone.getAbsX(), plantLoc.y, + plantLoc.z - serverZone.getAbsZ(), treeInfo.getRot().y, treeInfo.getW(), playerCharacter.getGuild().getName(), LocalDateTime.now()); + + // Uh oh! + if (cityObjects == null || cityObjects.isEmpty()) { + PlaceAssetMsg.sendPlaceAssetError(origin, 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + return false; + } + + // Assign our worker variables after figuring out what + // is what in the result set. + + for (AbstractGameObject gameObject : cityObjects) { + + switch (gameObject.getObjectType()) { + case Building: + treeObject = (Building) gameObject; + treeObject.runAfterLoad(); + break; + case City: + cityObject = (City) gameObject; + break; + case Zone: + cityZone = (Zone) gameObject; + break; + default: + // log some error here? *** Refactor + } + } + + //?? your not allowed to plant a tree if ur not an errant guild. + // Desub from any previous nation. + // This should be done automatically in a method inside Guild *** Refactor + // Player is now a Soverign guild, configure them as such. + + playerCharacter.getGuild().setNation(playerCharacter.getGuild()); + playerNation = playerCharacter.getGuild(); + playerNation.setGuildState(GuildState.Sovereign); + + // Link the zone with the city and then add + // to the appropritae hash tables and cache + + cityZone.setPlayerCity(true); + + if (cityZone.getParent() != null) + cityZone.getParent().addNode(cityZone); //add as child to parent + + ZoneManager.addZone(cityZone.getObjectUUID(), cityZone); + ZoneManager.addPlayerCityZone(cityZone); + serverZone.addNode(cityZone); + + cityZone.generateWorldAltitude(); + + cityObject.setParent(cityZone); + cityObject.setObjectTypeMask(MBServerStatics.MASK_CITY); // *** Refactor : should have it already + //Link the tree of life with the new zone + + treeObject.setObjectTypeMask(MBServerStatics.MASK_BUILDING); + treeObject.setParentZone(cityZone); + MaintenanceManager.setMaintDateTime(treeObject, LocalDateTime.now().plusDays(7)); + + // Update guild binds and tags + //load the new city on the clients + + CityZoneMsg czm = new CityZoneMsg(1, treeObject.getLoc().x, treeObject.getLoc().y, treeObject.getLoc().z, cityObject.getCityName(), cityZone, Enum.CityBoundsType.ZONE.extents, Enum.CityBoundsType.ZONE.extents); + DispatchMessage.dispatchMsgToAll(czm); + + GuildManager.updateAllGuildBinds(playerNation, cityObject); + GuildManager.updateAllGuildTags(playerNation); + + // Send all the cities to the clients? + // *** Refactor : figure out how to send like, one? + + City.lastCityUpdate = System.currentTimeMillis(); + WorldGrid.addObject(treeObject, playerCharacter); + + serverRealm.addCity(cityObject.getObjectUUID()); + playerNation.setCityUUID(cityObject.getObjectUUID()); + + // Bypass warehouse entry if we're an admin + + if (playerCharacter.getAccount().status.equals(AccountStatus.ADMIN)) + return true; + + // Push this event to the data warehouse + + CityRecord cityRecord = CityRecord.borrow(cityObject, RecordEventType.CREATE); + DataWarehouse.pushToWarehouse(cityRecord); + + return true; + } + + private boolean placeSpire(PlayerCharacter playerCharacter, ClientConnection origin, PlaceAssetMsg msg) { + + Zone serverZone; + Building spireBuilding; + Blueprint blueprint; + City cityObject; + PlacementInfo buildingList; + + // Setup working variables we'll need + + buildingList = msg.getFirstPlacementInfo(); + + serverZone = ZoneManager.findSmallestZone(buildingList.getLoc()); + + // Early exit if something went horribly wrong + // with locating the current realm and/or city + + if (serverZone == null) + return false; + + cityObject = City.getCity(serverZone.getPlayerCityUUID()); + + if (cityObject == null) + return false; + + // Method checks validation conditions arising when placing + // buildings. Player must be on a city grid, must be + // inner council of the city's guild, etc. + + if (validateCityBuildingPlacement(serverZone, msg, origin, playerCharacter, buildingList) == false) + return false; + + // Loop through all buildings in this city looking for a spire of the. + // same type we are placing. There can be only one of each type + + int spireCount = 0; + + blueprint = Blueprint.getBlueprint(msg.getFirstPlacementInfo().getBlueprintUUID()); + + for (Building building : serverZone.zoneBuildingSet) { + + if (building.getBlueprint().getBuildingGroup() == BuildingGroup.SPIRE) { + + if (building.getBlueprintUUID() == blueprint.getMeshForRank(0)) { + PlaceAssetMsg.sendPlaceAssetError(origin, 46, ""); // "Spire of that type exists" + return false; + } + spireCount++; + } + } + + // Too many spires for this tree's rank? + + if (spireCount >= Blueprint.getMaxShrines(cityObject.getTOL().getRank())) { + PlaceAssetMsg.sendPlaceAssetError(origin, 45, ""); //Tree cannot support anymore spires + return false; + } + + // Create the spire + + spireBuilding = createStructure(playerCharacter, msg.getFirstPlacementInfo(), serverZone); + return spireBuilding != null; + } + + private boolean placeShrine(PlayerCharacter playerCharacter, ClientConnection origin, PlaceAssetMsg msg) { + + Zone serverZone; + Blueprint blueprint; + City cityObject; + PlacementInfo buildingList; + + // Setup working variables we'll need + buildingList = msg.getFirstPlacementInfo(); + + serverZone = ZoneManager.findSmallestZone(buildingList.getLoc()); + + // Early exit if something went horribly wrong + // with locating the current realm and/or zone + if (serverZone == null) + return false; + + // Method checks validation conditions arising when placing + // buildings. Player must be on a city grid, must be + // inner council of the city's guild, etc. + + if (validateCityBuildingPlacement(serverZone, msg, origin, playerCharacter, buildingList) == false) + return false; + + // Loop through all buildings in this city looking for a shrine of the. + // same type we are placing. There can be only one of each type + + int shrineCount = 0; + + cityObject = City.getCity(serverZone.getPlayerCityUUID()); + + // Cannot place shrine in abanadoned city. Shrines must be owned + // by the tol owner not the person placing them. + + if (cityObject.getTOL().getOwnerUUID() == 0) { + PlaceAssetMsg.sendPlaceAssetError(origin, 42, ""); //Tree cannot support anymore shrines + return false; + } + + blueprint = Blueprint.getBlueprint(msg.getFirstPlacementInfo().getBlueprintUUID()); + + if (blueprint == null){ + return false; + } + + for (Building building : serverZone.zoneBuildingSet) { + if (building.getBlueprint() == null) + continue; + + if (building.getBlueprint().getBuildingGroup() == BuildingGroup.SHRINE) { + if (building.getBlueprintUUID() == blueprint.getMeshForRank(0)) { + PlaceAssetMsg.sendPlaceAssetError(origin, 43, ""); // "shrine of that type exists" + return false; + } + shrineCount++; + } + } + + // Too many shrines for this tree's rank? + + if (shrineCount >= Blueprint.getMaxShrines(cityObject.getTOL().getRank())) { + PlaceAssetMsg.sendPlaceAssetError(origin, 42, ""); //Tree cannot support anymore shrines + return false; + } + + // Create the shrine + + return createShrine((PlayerCharacter)cityObject.getTOL().getOwner(), msg.getFirstPlacementInfo(), serverZone); + } + + private boolean placeBarrack(PlayerCharacter playerCharacter, ClientConnection origin, PlaceAssetMsg msg) { + + Zone serverZone; + City cityObject; + PlacementInfo buildingList; + + // Setup working variables we'll need + buildingList = msg.getFirstPlacementInfo(); + + serverZone = ZoneManager.findSmallestZone(buildingList.getLoc()); + + // Early exit if something went horribly wrong + // with locating the current realm and/or zone + + if (serverZone == null) + return false; + + // Method checks validation conditions arising when placing + // buildings. Player must be on a city grid, must be + // inner council of the city's guild, etc. + + if (validateCityBuildingPlacement(serverZone, msg, origin, playerCharacter, buildingList) == false) + return false; + + // Loop through all buildings in this city counting barracks . + + int barracksCount = 0; + + cityObject = City.getCity(serverZone.getPlayerCityUUID()); + + // Cannot place barracks in abanadoned city. + + if (cityObject.getTOL().getOwnerUUID() == 0) { + PlaceAssetMsg.sendPlaceAssetError(origin, 42, ""); //Tree cannot support anymore shrines + return false; + } + + for (Building building : serverZone.zoneBuildingSet) { + if (building.getBlueprint().getBuildingGroup() == BuildingGroup.BARRACK) + barracksCount++; + } + + // Too many shrines for this tree's rank? + + if (barracksCount >= cityObject.getTOL().getRank()) { + PlaceAssetMsg.sendPlaceAssetError(origin, 47, ""); //Tree cannot support anymore shrines + return false; + } + + // Create the shrine + + return createBarracks((PlayerCharacter)cityObject.getTOL().getOwner(), msg.getFirstPlacementInfo(), serverZone); + } + + private boolean placeCityWalls(PlayerCharacter player, ClientConnection origin, PlaceAssetMsg msg) { + + // Member variables + + Zone serverZone; + City cityObject; + int placementCost = 0; + CharacterItemManager itemMan; + Item goldItem; + Building wallPiece; + + // Setup working variables we'll need + + serverZone = ZoneManager.findSmallestZone(player.getLoc()); + + // Early exit if something went horribly wrong + + if (serverZone == null) + return false; + + + if (player.getCharItemManager().getGoldTrading() > 0){ + ErrorPopupMsg.sendErrorPopup(player, 195); + return false; + } + + + // Method checks validation conditions arising when placing + // buildings. Player must be on a city grid, must be + // inner council of the city's guild, etc. + + if (validateCityBuildingPlacement(serverZone, msg, origin, player, msg.getFirstPlacementInfo()) == false) + return false; + + cityObject = City.getCity(serverZone.getPlayerCityUUID()); + + // We need to be able to access how much gold a character is carrying + + itemMan = player.getCharItemManager(); + + if (itemMan == null) + + return false; + + goldItem = itemMan.getGoldInventory(); + + // Grab list of walls we're placing + + ArrayList walls = msg.getPlacementInfo(); + + // Character must be able to afford walls + + for (PlacementInfo wall : walls) { + placementCost += PlaceAssetMsg.getWallCost(wall.getBlueprintUUID()); + } + + // Early exit if not enough gold in character's inventory to place walls + + if (placementCost > goldItem.getNumOfItems()) { + PlaceAssetMsg.sendPlaceAssetError(origin, 28, ""); // Not enough gold + return false; + } + + placementCost = 0; // reset placement cost for fix bug with wall pieces somethings not taking gold out if forced an error. + + + // Overlap check and wall deed verifications + for (PlacementInfo wall : walls) { + + if (Blueprint.isMeshWallPiece(wall.getBlueprintUUID()) == false) { + PlaceAssetMsg.sendPlaceAssetError(origin, 48, ""); //"Assets (except walls) must be placed one at a time" + continue; + } + + // Ignore wall pieces not on the city grid + if (cityObject.isLocationOnCityGrid(wall.getLoc()) == false) { + PlaceAssetMsg.sendPlaceAssetError(origin, 1, "Asset " + cityObject.getName() + " not on citygrid"); + continue; + } + + // Does this wall collide with any other building? + + for (Building building : serverZone.zoneBuildingSet) { + + + //TODO Clean up collision with placementInfo. don't need to create the same placementinfo bounds for collision checks on each building. + if ((building.getBlueprintUUID() != 0) && (Bounds.collide(wall, building) == true)) { + + if (building.getRank() == -1) { + building.removeFromCache(); + WorldGrid.RemoveWorldObject(building); + WorldGrid.removeObject(building); + building.getParentZone().getParent().zoneBuildingSet.remove(building); + continue; + } + // remove gold from walls already placed before returning. + + PlaceAssetMsg.sendPlaceAssetError(origin, 3, building.getName()); //"Conflict between assets" + return false; + } + } + placementCost = PlaceAssetMsg.getWallCost(wall.getBlueprintUUID()); + + if (!itemMan.modifyInventoryGold(-placementCost)){ + ChatManager.chatSystemInfo(player, player.getFirstName() + " can't has free moneys! no for real.. Thor.. seriously... I didnt fix it because you getting laid isnt important enough for me."); + return false; + } + // Attempt to place wall piece + + wallPiece = createStructure(player, wall, serverZone); + + if (wallPiece == null) { + PlaceAssetMsg.sendPlaceAssetError(origin, 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + continue; + } + + // walls are auto protected + wallPiece.setProtectionState(ProtectionState.PROTECTED); + PlaceAssetMsg.sendPlaceAssetConfirmWall(origin,serverZone); + + } + + // Deduct gold from character's inventory + + + return true; + } + + private static void closePlaceAssetWindow(ClientConnection origin) { + + // Action type 4 is the server telling the client to + // close the asset placement window. + // This is believed to be a confirmation message to the client + PlaceAssetMsg pam = new PlaceAssetMsg(); + pam.setActionType(4); + Dispatch dispatch = Dispatch.borrow(origin.getPlayerCharacter(), pam); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + } + + // Method deletes one item from the player's inventory + // based on the mesh UUID the deed/seed spawns + + private static void removeDeedByMeshUUID(PlayerCharacter player, int meshUUID) { + + CharacterItemManager inventoryManager; + ArrayList itemList; + + inventoryManager = player.getCharItemManager(); + itemList = player.getInventory(); + + for (Item inventoryItem : itemList) { + if (inventoryItem.getItemBase().getUseID() == meshUUID) { + inventoryManager.delete(inventoryItem); + + inventoryManager.updateInventory(); + return; + } + + } + } + + // Method validates the location we have selected for our new city + + private static boolean validateTreeOfLifePlacement(PlayerCharacter playerCharacter, Realm serverRealm, Zone serverZone, + ClientConnection origin, PlaceAssetMsg msg) { + + PlacementInfo placementInfo = msg.getFirstPlacementInfo(); + + // Your guild already owns a tree + + if (playerCharacter.getGuild().getOwnedCity() != null) { + PlaceAssetMsg.sendPlaceAssetError(origin, 1, "Your guild already owns a tree!"); + return false; + } + + // Validate that the player is the leader of a guild + + if (GuildStatusController.isGuildLeader(playerCharacter.getGuildStatus()) == false) { + PlaceAssetMsg.sendPlaceAssetError(origin, 10, ""); // Must be a guild leader + return false; + } + + // Validate that the player is the leader of a guild + // that is not currently Sovereign *** BUG? Doesn't look right. isGuildLeader()? + + if ((playerCharacter.getGuild().getGuildState() != GuildState.Sworn + || playerCharacter.getGuild().getGuildState() != GuildState.Errant) == false) { + PlaceAssetMsg.sendPlaceAssetError(origin, 17, ""); // Your is not an errant or soverign guild + return false; + } + + // All trees must be placed within a continent. + + if (!serverZone.isContininent()) { + + PlaceAssetMsg.sendPlaceAssetError(origin, 69, ""); // Tree must be within a territory + return false; + } + + RealmType realmType = RealmType.getRealmTypeByUUID(serverRealm.getRealmID()); + + if ( + (realmType.equals(RealmType.MAELSTROM)) || + (realmType.equals(RealmType.OBLIVION))) { + PlaceAssetMsg.sendPlaceAssetError(origin, 57, playerCharacter.getName()); // No building may be placed within this territory + return false; + } + + // Cannot place a tree underwater + + if (HeightMap.isLocUnderwater(placementInfo.getLoc())) { + PlaceAssetMsg.sendPlaceAssetError(origin, 6, ""); // Cannot place underwater + return false; + } + + //Test city not too close to any other zone + + if (!ZoneManager.validTreePlacementLoc(serverZone, placementInfo.getLoc().x, placementInfo.getLoc().z)) { + PlaceAssetMsg.sendPlaceAssetError(origin, 39, ""); // Too close to another tree + return false; + } + + // Validate that Realm is not at it's city limit + + if (serverRealm.isRealmFull() == true) { + int numCities; + numCities = serverRealm.getNumCities(); + PlaceAssetMsg.sendPlaceAssetError(origin, 58, Integer.toString(numCities)); // This territory is full + return false; + } + + return true; + } + + private Building createStructure(PlayerCharacter playerCharacter, PlacementInfo buildingInfo, Zone currentZone) { + + Blueprint blueprint; + Building newMesh; + DateTime completionDate; + float vendorRotation; + float buildingRotation; + + blueprint = Blueprint.getBlueprint(buildingInfo.getBlueprintUUID()); + + if (blueprint == null) { + Logger.error("CreateStucture: DB returned null blueprint."); + return null; + } + + // All seige buildings build in 15 minutes + if ((blueprint.getBuildingGroup().equals(BuildingGroup.SIEGETENT)) + || (blueprint.getBuildingGroup().equals(BuildingGroup.BULWARK))) + completionDate = DateTime.now().plusMinutes(15); + else + completionDate = DateTime.now().plusHours(blueprint.getRankTime(1)); + + Vector3fImmutable localLoc = new Vector3fImmutable(ZoneManager.worldToLocal(buildingInfo.getLoc(), currentZone)); + + buildingRotation = buildingInfo.getRot().y; + vendorRotation = buildingInfo.getW(); + + // if W return is negative, this is a -90 rotation not a 90? + + newMesh = DbManager.BuildingQueries.CREATE_BUILDING( + currentZone.getObjectUUID(), playerCharacter.getObjectUUID(), blueprint.getName(), blueprint.getMeshForRank(0), + localLoc, 1.0f, blueprint.getMaxHealth(0), ProtectionState.NONE, 0, 0, + completionDate, blueprint.getMeshForRank(0), vendorRotation, buildingRotation); + + // Make sure we have a valid mesh + if (newMesh == null) { + Logger.error("CreateStucture: DB returned null object."); + return null; + } + + newMesh.setObjectTypeMask(MBServerStatics.MASK_BUILDING); + MaintenanceManager.setMaintDateTime(newMesh, LocalDateTime.now().plusDays(7)); + + WorldGrid.addObject(newMesh, playerCharacter); + return newMesh; + + } + + private boolean createShrine(PlayerCharacter player, PlacementInfo buildingInfo, Zone currentZone) { + + Blueprint blueprint; + Building newMesh; + Shrine newShrine; + City city; + ShrineType shrineType; + + if (player == null) + return false; + + blueprint = Blueprint.getBlueprint(buildingInfo.getBlueprintUUID()); + + if (blueprint == null) { + Logger.error("CreateShrine: DB returned null blueprint."); + return false; + } + + shrineType = Shrine.getShrineTypeByBlueprintUUID(blueprint.getBlueprintUUID()); + + city = City.getCity(currentZone.getPlayerCityUUID()); + + if (city == null) + return false; + + if (!city.isLocationOnCityGrid(buildingInfo.getLoc())) + return false; + + Vector3fImmutable localLoc = new Vector3fImmutable(ZoneManager.worldToLocal(buildingInfo.getLoc(), currentZone)); + + float buildingRotation = buildingInfo.getRot().y; + float vendorRotation = buildingInfo.getW(); + + + ArrayList shrineObjects = DbManager.ShrineQueries.CREATE_SHRINE( + currentZone.getObjectUUID(), player.getObjectUUID(), blueprint.getName(), blueprint.getMeshForRank(0), + localLoc, 1.0f, blueprint.getMaxHealth(0), ProtectionState.PROTECTED, 0, 0, + DateTime.now().plusHours(blueprint.getRankTime(1)), blueprint.getMeshForRank(0), vendorRotation, buildingRotation, shrineType.name()); + + if (shrineObjects == null) { + PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + return false; + } + + for (AbstractGameObject ago : shrineObjects) { + + switch (ago.getObjectType()) { + case Building: + newMesh = (Building) ago; + newMesh.runAfterLoad(); + newMesh.setObjectTypeMask(MBServerStatics.MASK_BUILDING); + MaintenanceManager.setMaintDateTime(newMesh, LocalDateTime.now().plusDays(7)); + WorldGrid.addObject(newMesh, player); + break; + case Shrine: + newShrine = (Shrine) ago; + newShrine.getShrineType().addShrineToServerList(newShrine); + break; + default: + PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + break; + } + } + + return true; + } + + private boolean createBarracks(PlayerCharacter player, PlacementInfo buildingInfo, Zone currentZone) { + + Blueprint blueprint; + Building newMesh; + Shrine newShrine; + City city; + + if (player == null) + return false; + + blueprint = Blueprint.getBlueprint(buildingInfo.getBlueprintUUID()); + + if (blueprint == null) { + Logger.error("CreateShrine: DB returned null blueprint."); + return false; + } + + city = City.getCity(currentZone.getPlayerCityUUID()); + + if (city == null) + return false; + + if (!city.isLocationOnCityGrid(buildingInfo.getLoc())) + return false; + + Vector3fImmutable localLoc = new Vector3fImmutable(ZoneManager.worldToLocal(buildingInfo.getLoc(), currentZone)); + + float buildingRotation = buildingInfo.getRot().y; + float vendorRotation = buildingInfo.getW(); + DateTime completionDate = DateTime.now().plusHours(blueprint.getRankTime(1)); + + + newMesh = DbManager.BuildingQueries.CREATE_BUILDING( + currentZone.getObjectUUID(), player.getObjectUUID(), blueprint.getName(), blueprint.getMeshForRank(0), + localLoc, 1.0f, blueprint.getMaxHealth(0), ProtectionState.PROTECTED, 0, 0, + completionDate, blueprint.getMeshForRank(0), vendorRotation, buildingRotation); + + // Make sure we have a valid mesh + if (newMesh == null) { + Logger.error("CreateStucture: DB returned null object."); + return false; + } + + newMesh.setObjectTypeMask(MBServerStatics.MASK_BUILDING); + MaintenanceManager.setMaintDateTime(newMesh, LocalDateTime.now().plusDays(7)); + WorldGrid.addObject(newMesh, player); + + return true; + } + + private boolean createWarehouse(PlayerCharacter player, PlacementInfo buildingInfo, Zone currentZone) { + + Blueprint blueprint; + Building newMesh = null; + ArrayList warehouseObjects; + + blueprint = Blueprint.getBlueprint(buildingInfo.getBlueprintUUID()); + + if (blueprint == null) { + Logger.error("CreateWarehouse: DB returned null blueprint."); + return false; + } + + Vector3fImmutable localLoc = new Vector3fImmutable(ZoneManager.worldToLocal(buildingInfo.getLoc(), currentZone)); + + float buildingRotation = buildingInfo.getRot().y; + float vendorRotation = buildingInfo.getW(); + + warehouseObjects = DbManager.WarehouseQueries.CREATE_WAREHOUSE( + currentZone.getObjectUUID(), player.getObjectUUID(), blueprint.getName(), blueprint.getMeshForRank(0), + localLoc, 1.0f, blueprint.getMaxHealth(0), ProtectionState.NONE, 0, 0, + DateTime.now().plusHours(blueprint.getRankTime(1)), blueprint.getMeshForRank(0), vendorRotation, buildingRotation); + + if (warehouseObjects == null) { + PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + return false; + } + + // Load the building into the simulation + + for (AbstractGameObject ago : warehouseObjects) { + + if (ago.getObjectType() == GameObjectType.Building) { + newMesh = (Building) ago; + newMesh.setObjectTypeMask(MBServerStatics.MASK_BUILDING); + MaintenanceManager.setMaintDateTime(newMesh, LocalDateTime.now().plusDays(7)); + WorldGrid.addObject(newMesh, player); + newMesh.runAfterLoad(); + } + else if (ago.getObjectType() == GameObjectType.Warehouse) { + Warehouse warehouse = (Warehouse) ago; + City city = City.getCity(currentZone.getPlayerCityUUID()); + if (city == null) + return true; + city.setWarehouseBuildingID(newMesh.getObjectUUID()); + Warehouse.warehouseByBuildingUUID.put(newMesh.getObjectUUID(), warehouse); + } + } + + return true; + } + + // Validates that player is able to place buildings + + private static boolean validateBuildingPlacement(Zone serverZone, PlaceAssetMsg msg, ClientConnection origin, PlayerCharacter player, PlacementInfo placementInfo) { + + RealmType currentRealm; + + // Retrieve the building details we're placing + + if (serverZone.isNPCCity() == true) { + PlaceAssetMsg.sendPlaceAssetError(origin, 15, ""); // Cannot place in a peace zone + return false; + } + + // Errant guilds cannot place assets + + if (player.getGuild().getGuildState() == GuildState.Errant) { + PlaceAssetMsg.sendPlaceAssetError(origin, 1, "Only soverign or sworn guilds may place assets."); + return false; + } + + // Cannot place a building underwater + + if (HeightMap.isLocUnderwater(placementInfo.getLoc())) { + PlaceAssetMsg.sendPlaceAssetError(origin, 6, ""); // Cannot place underwater + return false; + } + + // Players cannot place buildings in mob zones. + + if ((serverZone.isMacroZone() == true) + || (serverZone.getParent().isMacroZone() == true)) { + PlaceAssetMsg.sendPlaceAssetError(origin, 57, player.getName()); // No building may be placed within this territory + return false; + } + + currentRealm = RealmType.getRealmTypeByUUID(RealmMap.getRealmIDAtLocation(player.getLoc())); + + if ( + (currentRealm.equals(RealmType.MAELSTROM)) || + (currentRealm.equals(RealmType.OBLIVION))) { + PlaceAssetMsg.sendPlaceAssetError(origin, 57, player.getName()); // No building may be placed within this territory + return false; + } + + // Cannot place assets on a dead tree + + if ((serverZone.isPlayerCity()) + && (City.getCity(serverZone.getPlayerCityUUID()).getTOL().getRank() == -1)){ + PlaceAssetMsg.sendPlaceAssetError(origin, 1, "Cannot place asset on dead tree until world heals"); + return false; + } + + // Overlap check + + for (Building building : serverZone.zoneBuildingSet) { + + if ((building.getBlueprintUUID() != 0) && (Bounds.collide(placementInfo, building) == true)) { + + // Ignore and remove from simulation if we are placing over rubble + + if (building.getRank() == -1) { + + if ((building.getBlueprintUUID() != 0) + && (building.getBlueprint().getBuildingGroup() == BuildingGroup.SHRINE)){ + Shrine.RemoveShrineFromCacheByBuilding(building); + if (building.getCity() != null){ + + } + } + + building.removeFromCache(); + WorldGrid.RemoveWorldObject(building); + WorldGrid.removeObject(building); + building.getParentZone().zoneBuildingSet.remove(building); + continue; + } + + + PlaceAssetMsg.sendPlaceAssetError(origin, 3, ""); // Conflict between proposed assets + return false; + } + } + + return true; + } + + private static boolean validateCityBuildingPlacement(Zone serverZone, PlaceAssetMsg msg, ClientConnection origin, PlayerCharacter player, PlacementInfo buildingInfo) { + + // Peform shared common validation first + + if (validateBuildingPlacement(serverZone, msg, origin, player, buildingInfo) == false) + return false; + + // Must be a player city + + if (serverZone.isPlayerCity() == false) { + PlaceAssetMsg.sendPlaceAssetError(origin, 41, player.getName()); // Cannot place outisde a guild zone + return false; + } + + //Test zone has a city object + + City city = City.getCity(serverZone.getPlayerCityUUID()); + + if (city == null) { + PlaceAssetMsg.sendPlaceAssetError(origin, 52, ""); //"no city to associate asset with" + return false; + } + + // City assets must be placed on the city grid + + if (!city.isLocationOnCityGrid(buildingInfo.getLoc())) { + PlaceAssetMsg.sendPlaceAssetError(origin, 1, "Assset must be placed on a City Grid"); + return false; + } + + // Make sure it's not an errant tree + + if ( (city.getGuild() == null || city.getGuild().isErrant() == true)) { + PlaceAssetMsg.sendPlaceAssetError(origin, 18, ""); //"There are no guild trees to be found" + return false; + } + + //Test player is in correct guild to place buildings + + if (!player.isCSR) + if (player.getGuild().getObjectUUID() != city.getGuild().getObjectUUID()) { + PlaceAssetMsg.sendPlaceAssetError(origin, 9, ""); //You must be a guild member to place this asset + return false; + } + + return true; + } + +} diff --git a/src/engine/net/client/handlers/RecommendNationMsgHandler.java b/src/engine/net/client/handlers/RecommendNationMsgHandler.java new file mode 100644 index 00000000..8912dea4 --- /dev/null +++ b/src/engine/net/client/handlers/RecommendNationMsgHandler.java @@ -0,0 +1,90 @@ +package engine.net.client.handlers; + +import engine.Enum.AllianceType; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.client.ClientConnection; +import engine.net.client.msg.AllianceChangeMsg; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.RecommendNationMsg; +import engine.objects.Guild; +import engine.objects.PlayerCharacter; + +/* + * @Author: + * @Summary: Processes application protocol message which handles + * protecting and unprotecting city assets + */ +public class RecommendNationMsgHandler extends AbstractClientMsgHandler { + + public RecommendNationMsgHandler() { + super(RecommendNationMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + // Member variable declaration + + PlayerCharacter player; + RecommendNationMsg msg; + + + // Member variable assignment + + msg = (RecommendNationMsg) baseMsg; + + player = SessionManager.getPlayerCharacter(origin); + + if (player == null) + return true; + + + RecommendNationMsgHandler.RecommendNation(player.getGuild(), Guild.getGuild(msg.getGuildID()), msg, origin); + + + + + // dispatch = Dispatch.borrow(player, baseMsg); + // DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + return true; + + } + + private static void RecommendNation(Guild fromGuild, Guild toGuild, RecommendNationMsg msg, ClientConnection origin) { + + // Member variable declaration + Dispatch dispatch; + + // Member variable assignment + + if (fromGuild == null) + return; + + if (toGuild == null) + return; + + AllianceType allianceType; + if (msg.getAlly() == 1) + allianceType = AllianceType.RecommendedAlly; + else + allianceType = AllianceType.RecommendedEnemy; + + if (!fromGuild.addGuildToAlliance(new AllianceChangeMsg(origin.getPlayerCharacter(),fromGuild.getObjectUUID(), toGuild.getObjectUUID(), (byte)0, 0), allianceType, toGuild, origin.getPlayerCharacter())) + return; + String alliance = msg.getAlly() == 1? "ally" : "enemy"; + + ChatManager.chatGuildInfo(fromGuild, origin.getPlayerCharacter().getFirstName() + " has recommended " + toGuild.getName() + " as an " + alliance ); + + // dispatch = Dispatch.borrow(origin.getPlayerCharacter(), msg); + // DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + + } + + + +} diff --git a/src/engine/net/client/handlers/RemoveFriendHandler.java b/src/engine/net/client/handlers/RemoveFriendHandler.java new file mode 100644 index 00000000..f17df1f4 --- /dev/null +++ b/src/engine/net/client/handlers/RemoveFriendHandler.java @@ -0,0 +1,64 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum.DispatchChannel; +import engine.exception.MsgSendException; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.RemoveFriendMessage; +import engine.objects.PlayerCharacter; +import engine.objects.PlayerFriends; + + + +public class RemoveFriendHandler extends AbstractClientMsgHandler { + + public RemoveFriendHandler() { + super(RemoveFriendMessage.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, + ClientConnection origin) throws MsgSendException { + + PlayerCharacter player = origin.getPlayerCharacter(); + + if (player == null) + return true; + + + RemoveFriendMessage msg = (RemoveFriendMessage)baseMsg; + + HandleRemoveFriend(player,msg); + + return true; + } + + + + public static void HandleRemoveFriend(PlayerCharacter player, RemoveFriendMessage msg){ + + //No friends in list. Early exit. + PlayerFriends.RemoveFromFriends(player.getObjectUUID(), msg.friendID); + PlayerFriends.RemoveFromFriends(msg.friendID, player.getObjectUUID()); + + Dispatch dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + msg = new RemoveFriendMessage(msg.friendID); + + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + } + +} diff --git a/src/engine/net/client/handlers/RemoveFromGroupHandler.java b/src/engine/net/client/handlers/RemoveFromGroupHandler.java new file mode 100644 index 00000000..a7e7ede8 --- /dev/null +++ b/src/engine/net/client/handlers/RemoveFromGroupHandler.java @@ -0,0 +1,118 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.GroupManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.group.GroupUpdateMsg; +import engine.net.client.msg.group.RemoveFromGroupMsg; +import engine.objects.Group; +import engine.objects.PlayerCharacter; + +import java.util.Set; + +public class RemoveFromGroupHandler extends AbstractClientMsgHandler { + + public RemoveFromGroupHandler() { + super(RemoveFromGroupMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, + ClientConnection origin) throws MsgSendException { + + // Declar member variables + + PlayerCharacter source; + PlayerCharacter target; + RemoveFromGroupMsg msg; + Group group; + GroupUpdateMsg gim; + ClientConnection gcc; + Set groupMembers; + + // Assign member variables + + msg = (RemoveFromGroupMsg) baseMsg; + + source = SessionManager.getPlayerCharacter(origin); + + if (source == null) + return false; + + group = GroupManager.getGroup(source); + + if (group == null) + return false; + + if (group.getGroupLead() != source) // Only group lead can remove + return false; + + target = SessionManager.getPlayerCharacterByID(msg.getTargetID()); + + if (target == null) + return false; + + if (target == source) { // can't remove self, must quit + RemoveFromGroupMsg reply = new RemoveFromGroupMsg(); + reply.setResponse(1); + Dispatch dispatch = Dispatch.borrow(source, reply); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + return false; + } + + gcc = SessionManager.getClientConnection(target); + + if (gcc != null) { + + // Cleanup group window for player quiting + + groupMembers = group.getMembers(); + + for (PlayerCharacter groupMember : groupMembers) { + + if (groupMember == null) + continue; + + gim = new GroupUpdateMsg(); + gim.setGroup(group); + gim.setPlayer(target); + gim.setMessageType(3); + gim.setPlayer(groupMember); + Dispatch dispatch = Dispatch.borrow(target, gim); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + } + } + + // Remove from group and clean up everyone elses window + group.removeGroupMember(target); + GroupManager.removeFromGroups(target); + + gim = new GroupUpdateMsg(); + gim.setGroup(group); + gim.setMessageType(3); + gim.setPlayer(target); + group.sendUpdate(gim); + + String text = target.getFirstName() + " has left your group."; + ChatManager.chatGroupInfo(source, text); + + return true; + } + +} diff --git a/src/engine/net/client/handlers/RepairBuildingMsgHandler.java b/src/engine/net/client/handlers/RepairBuildingMsgHandler.java new file mode 100644 index 00000000..56085cf1 --- /dev/null +++ b/src/engine/net/client/handlers/RepairBuildingMsgHandler.java @@ -0,0 +1,139 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.BuildingGroup; +import engine.exception.MsgSendException; +import engine.gameManager.BuildingManager; +import engine.gameManager.SessionManager; +import engine.gameManager.ZoneManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.RepairBuildingMsg; +import engine.net.client.msg.UpdateObjectMsg; +import engine.objects.Building; +import engine.objects.City; +import engine.objects.PlayerCharacter; +import engine.objects.Zone; + +/* + * @Author: + * @Summary: Processes application protocol message which handles + * protecting and unprotecting city assets + */ +public class RepairBuildingMsgHandler extends AbstractClientMsgHandler { + + public RepairBuildingMsgHandler() { + super(RepairBuildingMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + // Member variable declaration + + PlayerCharacter player; + Building targetBuilding; + RepairBuildingMsg msg; + + + // Member variable assignment + + msg = (RepairBuildingMsg) baseMsg; + + player = SessionManager.getPlayerCharacter(origin); + + if (player == null) + return true; + + + + switch (msg.getType()) { + case 0: + targetBuilding = BuildingManager.getBuildingFromCache(msg.getBuildingID()); + RepairBuilding(targetBuilding, origin, msg); + break; + + // targetBuilding.createFurniture(item.getItemBase().getUseID(), 0, msg.getFurnitureLoc(), Vector3f.ZERO, 0, player); + + + } + + + + + // dispatch = Dispatch.borrow(player, baseMsg); + // DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + return true; + + } + + private static void RepairBuilding(Building targetBuilding, ClientConnection origin, RepairBuildingMsg msg) { + + // Member variable declaration + + Zone serverZone; + Dispatch dispatch; + + // Member variable assignment + + if (targetBuilding == null) + return; + + if (!targetBuilding.hasFunds(BuildingManager.GetRepairCost(targetBuilding))) + return; + + PlayerCharacter pc = origin.getPlayerCharacter(); + + serverZone = ZoneManager.findSmallestZone(pc.getLoc()); + + if (serverZone.getPlayerCityUUID() == 0 && targetBuilding.getBlueprint() != null && targetBuilding.getBlueprint().getBuildingGroup() != BuildingGroup.MINE) + return; + + + City city = City.GetCityFromCache(serverZone.getPlayerCityUUID()); + + if (city != null){ + if(city.getBane() != null && city.protectionEnforced == false) + return; + + } + + //cannot repair mines during 24/7 activity. + + if (targetBuilding.getBlueprint() != null && targetBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.MINE){ + return; + } + + + + + + int maxHP = (int) targetBuilding.getMaxHitPoints(); + int repairCost = BuildingManager.GetRepairCost(targetBuilding); + int missingHealth = (int) BuildingManager.GetMissingHealth(targetBuilding); + + if (!targetBuilding.transferGold(-repairCost,false)) + return; + + targetBuilding.modifyHealth(BuildingManager.GetMissingHealth(targetBuilding), null); + + UpdateObjectMsg uom = new UpdateObjectMsg(targetBuilding,3); + + dispatch = Dispatch.borrow(origin.getPlayerCharacter(), uom); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + + + RepairBuildingMsg rbm = new RepairBuildingMsg( targetBuilding.getObjectUUID(), maxHP, missingHealth, repairCost, targetBuilding.getStrongboxValue()); + + + dispatch = Dispatch.borrow(origin.getPlayerCharacter(), rbm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + + +} diff --git a/src/engine/net/client/handlers/RequestBallListHandler.java b/src/engine/net/client/handlers/RequestBallListHandler.java new file mode 100644 index 00000000..357a45e0 --- /dev/null +++ b/src/engine/net/client/handlers/RequestBallListHandler.java @@ -0,0 +1,47 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.exception.MsgSendException; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.RequestBallListMessage; +import engine.objects.PlayerCharacter; +public class RequestBallListHandler extends AbstractClientMsgHandler { + + public RequestBallListHandler() { + super(RequestBallListMessage.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, + ClientConnection origin) throws MsgSendException { + + PlayerCharacter player = origin.getPlayerCharacter(); + + if (player == null) + return true; + + + RequestBallListMessage msg = (RequestBallListMessage)baseMsg; + + HandleRequestBallList(player,msg); + + return true; + } + + + + public static void HandleRequestBallList(PlayerCharacter player, RequestBallListMessage msg){ + //currently not handled. + + } + +} diff --git a/src/engine/net/client/handlers/RequestEnterWorldHandler.java b/src/engine/net/client/handlers/RequestEnterWorldHandler.java new file mode 100644 index 00000000..4724439f --- /dev/null +++ b/src/engine/net/client/handlers/RequestEnterWorldHandler.java @@ -0,0 +1,162 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.DispatchChannel; +import engine.InterestManagement.InterestManager; +import engine.InterestManagement.WorldGrid; +import engine.db.archive.CharacterRecord; +import engine.db.archive.DataWarehouse; +import engine.db.archive.PvpRecord; +import engine.exception.MsgSendException; +import engine.gameManager.SessionManager; +import engine.math.Vector3fImmutable; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.*; +import engine.objects.AbstractWorldObject; +import engine.objects.Account; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; +import engine.session.Session; +import org.pmw.tinylog.Logger; + +/* + * @Author: + * @Summary: Processes application protocol message which requests + * that a character enters the game world from login screen + */ + +public class RequestEnterWorldHandler extends AbstractClientMsgHandler { + + public RequestEnterWorldHandler() { + super(RequestEnterWorldMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + RequestEnterWorldMsg msg; + + msg = (RequestEnterWorldMsg) baseMsg; + + Session session = SessionManager.getSession(origin); + + if (session == null) + return true; + + PlayerCharacter player = origin.getPlayerCharacter(); + + WorldGrid.RemoveWorldObject(player); + Dispatch dispatch; + + if (player == null) { + Logger.error("Unable to find player for session" + session.getSessionID()); + origin.kickToLogin(MBServerStatics.LOGINERROR_UNABLE_TO_LOGIN, "Player not found."); + return true; + } + + player.setEnteredWorld(false); + + Account acc = SessionManager.getAccount(origin); + + if (acc.status.ordinal() < MBServerStatics.worldAccessLevel.ordinal() || MBServerStatics.blockLogin) { + origin.disconnect(); + return true; + } + + // Brand new character. Send the city select screen + + if (player.getLevel() == 1 && player.getBindBuildingID() == -1) { + SelectCityMsg scm = new SelectCityMsg(player, true); + dispatch = Dispatch.borrow(player, scm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + } + + player.resetRegenUpdateTime(); + + // Map Data + + try { + WorldDataMsg wdm = new WorldDataMsg(); + dispatch = Dispatch.borrow(player, wdm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + } catch (Exception e) { + // TODO Auto-generated catch block + Logger.error("WORLDDATAMESSAGE" + e.getMessage()); + } + + // Realm Data + + try { + WorldRealmMsg wrm = new WorldRealmMsg(); + dispatch = Dispatch.borrow(player, wrm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + } catch (Exception e) { + // TODO Auto-generated catch block + Logger.error("REALMMESSAGE" + e.getMessage()); + } + + // Object Data + WorldObjectMsg wom = new WorldObjectMsg(session, true); + dispatch = Dispatch.borrow(player, wom); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + + player.getTimestamps().put("EnterWorld", System.currentTimeMillis()); + + if (player.getLoc().equals(Vector3fImmutable.ZERO) || System.currentTimeMillis() > player.getTimeStamp("logout") + (15 * 60 * 1000)) { + player.stopMovement(player.getBindLoc()); + player.setSafeMode(); + player.updateLocation(); + player.setRegion(AbstractWorldObject.GetRegionByWorldObject(player)); + } + + player.setTimeStamp("logout", 0); + player.respawnLock.writeLock().lock(); + try{ + if (!player.isAlive()){ + Logger.info("respawning player on enter world."); + player.respawn(true, true,true); + } + + }catch (Exception e){ + Logger.error(e); + }finally{ + player.respawnLock.writeLock().unlock(); + } + + + player.resetDataAtLogin(); + + InterestManager.INTERESTMANAGER.HandleLoadForEnterWorld(player); + + // If this is a brand new character... + // when they enter world is a great time to write their + // character record to the data warehouse. + + if (player.getHash() == null) { + + if (DataWarehouse.recordExists(Enum.DataRecordType.CHARACTER, player.getObjectUUID()) == false) { + CharacterRecord characterRecord = CharacterRecord.borrow(player); + DataWarehouse.pushToWarehouse(characterRecord); + } + player.setHash(); + } + + // + // We will load the kill/death lists here as data is only pertinent + // to characters actually logged into the game. + // + + player.pvpKills = PvpRecord.getCharacterPvPHistory(player.getObjectUUID(), Enum.PvpHistoryType.KILLS); + player.pvpDeaths = PvpRecord.getCharacterPvPHistory(player.getObjectUUID(), Enum.PvpHistoryType.DEATHS); + + SendOwnPlayerMsg sopm = new SendOwnPlayerMsg(SessionManager.getSession(origin)); + dispatch = Dispatch.borrow(player, sopm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + + return true; + } + + } diff --git a/src/engine/net/client/handlers/RequestGuildListHandler.java b/src/engine/net/client/handlers/RequestGuildListHandler.java new file mode 100644 index 00000000..5700cc0b --- /dev/null +++ b/src/engine/net/client/handlers/RequestGuildListHandler.java @@ -0,0 +1,55 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + + +import engine.Enum; +import engine.exception.MsgSendException; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.guild.ReqGuildListMsg; +import engine.net.client.msg.guild.SendGuildEntryMsg; +import engine.objects.GuildStatusController; +import engine.objects.PlayerCharacter; + + +public class RequestGuildListHandler extends AbstractClientMsgHandler { + + public RequestGuildListHandler() { + super(ReqGuildListMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + Dispatch dispatch; + + // get PlayerCharacter of person accepting invite + PlayerCharacter pc = SessionManager.getPlayerCharacter( + origin); + if (pc == null) + return true; + + if (GuildStatusController.isGuildLeader(pc.getGuildStatus()) == false){ + ErrorPopupMsg.sendErrorMsg(pc, "You do not have such authority!"); + } + SendGuildEntryMsg msg = new SendGuildEntryMsg(pc); + + + dispatch = Dispatch.borrow(pc, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + return true; + } + +} diff --git a/src/engine/net/client/handlers/SendBallEntryHandler.java b/src/engine/net/client/handlers/SendBallEntryHandler.java new file mode 100644 index 00000000..33ca1f9a --- /dev/null +++ b/src/engine/net/client/handlers/SendBallEntryHandler.java @@ -0,0 +1,47 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.exception.MsgSendException; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.SendBallEntryMessage; +import engine.objects.PlayerCharacter; +public class SendBallEntryHandler extends AbstractClientMsgHandler { + + public SendBallEntryHandler() { + super(SendBallEntryMessage.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, + ClientConnection origin) throws MsgSendException { + + PlayerCharacter player = origin.getPlayerCharacter(); + + if (player == null) + return true; + + + SendBallEntryMessage msg = (SendBallEntryMessage)baseMsg; + + HandleAddBall(player,msg); + + return true; + } + + + + public static void HandleAddBall(PlayerCharacter player, SendBallEntryMessage msg){ + //currently not handled. + + } + +} diff --git a/src/engine/net/client/handlers/SwearInGuildHandler.java b/src/engine/net/client/handlers/SwearInGuildHandler.java new file mode 100644 index 00000000..a27029fa --- /dev/null +++ b/src/engine/net/client/handlers/SwearInGuildHandler.java @@ -0,0 +1,138 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.Enum.GuildState; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.gameManager.GuildManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.guild.SendGuildEntryMsg; +import engine.net.client.msg.guild.SwearInGuildMsg; +import engine.objects.City; +import engine.objects.Guild; +import engine.objects.GuildStatusController; +import engine.objects.PlayerCharacter; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; + +public class SwearInGuildHandler extends AbstractClientMsgHandler { + + public SwearInGuildHandler() { + super(SwearInGuildMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + PlayerCharacter player; + SwearInGuildMsg swearInMsg; + Guild targetGuild; + Guild nation; + Dispatch dispatch; + + swearInMsg = (SwearInGuildMsg) baseMsg; + player = SessionManager.getPlayerCharacter(origin); + + if (player == null) + return true; + + targetGuild = (Guild) DbManager.getObject(GameObjectType.Guild, swearInMsg.getGuildUUID()); + + if (targetGuild == null) { + ErrorPopupMsg.sendErrorMsg(player, "A Serious error has occured. Please post details for to ensure transaction integrity"); + return true; + } + + nation = player.getGuild(); + + if (nation == null) { + ErrorPopupMsg.sendErrorMsg(player, "You do not belong to a guild!"); + return true; + } + + try { + if (!nation.isNation()) { + ErrorPopupMsg.sendErrorMsg(player, "Your guild is not a nation!"); + return true; + } + if (!nation.getSubGuildList().contains(targetGuild)) { + ErrorPopupMsg.sendErrorMsg(player, "Your do not have such authority!"); + return true; + } + + if (!Guild.canSwearIn(targetGuild)) { + ErrorPopupMsg.sendErrorMsg(player, targetGuild.getGuildState().name() + "cannot be sworn in"); + return true; + } + + if (GuildStatusController.isGuildLeader(player.getGuildStatus()) == false){ + ErrorPopupMsg.sendErrorMsg(player, "Your do not have such authority!"); + return true; + } + + if (!DbManager.GuildQueries.UPDATE_PARENT(targetGuild.getObjectUUID(), nation.getObjectUUID())) { + ErrorPopupMsg.sendErrorMsg(player, "A Serious error has occured. Please post details for to ensure transaction integrity"); + return true; + } + + switch (targetGuild.getGuildState()) { + case Petitioner: + GuildManager.updateAllGuildBinds(targetGuild, nation.getOwnedCity()); + break; + case Protectorate: + break; + default: + //shouldn't get here. + break; + } + + //update Guild state. + targetGuild.setNation(nation); + GuildManager.updateAllGuildTags(targetGuild); + targetGuild.upgradeGuildState(false); + + if (nation.getGuildState() == GuildState.Sovereign) + nation.upgradeGuildState(true); + + SendGuildEntryMsg msg = new SendGuildEntryMsg(player); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + City.lastCityUpdate = System.currentTimeMillis(); + + ArrayList guildMembers = SessionManager.getActivePCsInGuildID(nation.getObjectUUID()); + + for (PlayerCharacter member : guildMembers) { + ChatManager.chatGuildInfo(member, "Your Guild is now a Nation!"); + } + + ArrayList swornMembers = SessionManager.getActivePCsInGuildID(targetGuild.getObjectUUID()); + + for (PlayerCharacter member : swornMembers) { + ChatManager.chatGuildInfo(member, "Your Guild has sword fealty to " + nation.getName() + '.'); + } + } catch (Exception e) { + Logger.error( e.getMessage()); + return true; + } + + return true; + } + +} diff --git a/src/engine/net/client/handlers/SwearInHandler.java b/src/engine/net/client/handlers/SwearInHandler.java new file mode 100644 index 00000000..fca29d69 --- /dev/null +++ b/src/engine/net/client/handlers/SwearInHandler.java @@ -0,0 +1,79 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.net.client.handlers; + +import engine.Enum; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.guild.GuildInfoMsg; +import engine.net.client.msg.guild.GuildListMsg; +import engine.net.client.msg.guild.SwearInMsg; +import engine.objects.GuildStatusController; +import engine.objects.PlayerCharacter; + +public class SwearInHandler extends AbstractClientMsgHandler { + + public SwearInHandler() { + super(SwearInMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + SwearInMsg msg = (SwearInMsg) baseMsg; + Dispatch dispatch; + + // get source player + PlayerCharacter source = SessionManager.getPlayerCharacter(origin); + + if (source == null) + return true; + + // get target player + PlayerCharacter target = SessionManager.getPlayerCharacterByID(msg.getTargetID()); + + if (target == null) { + ChatManager.chatGuildError(source, + "No such character found!"); + return true; + } + + if(source.getGuild() != target.getGuild()) { + ChatManager.chatGuildError(source, + "That player is not a member of " + source.getGuild().getName()); + return true; + } + + // Verify source has authority to swear in + if (GuildStatusController.isInnerCouncil(source.getGuildStatus()) == false) { + ErrorPopupMsg.sendErrorMsg(source, "Your do not have such authority!"); + return true; + } + + // Swear target in and send message to guild + target.setFullMember(true); + target.incVer(); + + ChatManager.chatGuildInfo(source,target.getFirstName() + " has been sworn in as a full member!"); + + dispatch = Dispatch.borrow(source, new GuildListMsg(source.getGuild())); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + DispatchMessage.sendToAllInRange(target, new GuildInfoMsg(target, target.getGuild(), 2)); + + return true; + } + +} diff --git a/src/engine/net/client/handlers/TaxCityMsgHandler.java b/src/engine/net/client/handlers/TaxCityMsgHandler.java new file mode 100644 index 00000000..e03a6528 --- /dev/null +++ b/src/engine/net/client/handlers/TaxCityMsgHandler.java @@ -0,0 +1,154 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.InterestManagement.RealmMap; +import engine.exception.MsgSendException; +import engine.gameManager.BuildingManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.TaxCityMsg; +import engine.net.client.msg.ViewResourcesMessage; +import engine.objects.*; + +/* + * @Author: + * @Summary: Processes application protocol message which handles + * protecting and unprotecting city assets + */ +public class TaxCityMsgHandler extends AbstractClientMsgHandler { + + public TaxCityMsgHandler() { + super(TaxCityMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + // Member variable declaration + + PlayerCharacter player; + TaxCityMsg msg; + + player = origin.getPlayerCharacter(); + + + msg = (TaxCityMsg) baseMsg; + + ViewTaxes(msg,player); + + + + return true; + + } + + private static boolean ViewTaxes(TaxCityMsg msg, PlayerCharacter player) { + + // Member variable declaration + Building building = BuildingManager.getBuildingFromCache(msg.getGuildID()); + Guild playerGuild = player.getGuild(); + + if (building == null){ + ErrorPopupMsg.sendErrorMsg(player, "Not a valid Building!"); + return true; + } + + City city = building.getCity(); + if (city == null){ + ErrorPopupMsg.sendErrorMsg(player, "This building does not belong to a city."); + return true; + } + + if (city.getWarehouse() == null){ + ErrorPopupMsg.sendErrorMsg(player, "This city does not have a warehouse!"); + return true; + } + + + if (playerGuild == null || playerGuild.isErrant()){ + ErrorPopupMsg.sendErrorMsg(player, "You must belong to a guild to do that!"); + return true; + } + + if (playerGuild.getOwnedCity() == null){ + ErrorPopupMsg.sendErrorMsg(player, "Your Guild needs to own a city!"); + return true; + } + + if (playerGuild.getOwnedCity().getWarehouse() == null){ + ErrorPopupMsg.sendErrorMsg(player, "Your Guild needs to own a warehouse!"); + return true; + } + + if (playerGuild.getOwnedCity().getTOL() == null){ + ErrorPopupMsg.sendErrorMsg(player, "Cannot find Tree of Life for your city!"); + return true; + } + +// if (playerGuild.getOwnedCity().getTOL().getRank() != 8){ +// ErrorPopupMsg.sendErrorMsg(player, "Your City needs to Own a realm!"); +// return true; +// } + + if (playerGuild.getOwnedCity().getRealm() == null){ + ErrorPopupMsg.sendErrorMsg(player, "Cannot find realm for your city!"); + return true; + } + Realm targetRealm = RealmMap.getRealmForCity(city); + + if (targetRealm == null){ + ErrorPopupMsg.sendErrorMsg(player, "Cannot find realm for city you are attempting to tax!"); + return true; + } + +// if (targetRealm.getRulingCity() == null){ +// ErrorPopupMsg.sendErrorMsg(player, "Realm Does not have a ruling city!"); +// return true; +// } + +// if (targetRealm.getRulingCity().getObjectUUID() != playerGuild.getOwnedCity().getObjectUUID()){ +// ErrorPopupMsg.sendErrorMsg(player, "Your guild does not rule this realm!"); +// return true; +// } + +// if (playerGuild.getOwnedCity().getObjectUUID() == city.getObjectUUID()){ +// ErrorPopupMsg.sendErrorMsg(player, "You cannot tax your own city!"); +// return true; +// } + + + + + if (!GuildStatusController.isTaxCollector(player.getGuildStatus())){ + ErrorPopupMsg.sendErrorMsg(player, "You Must be a tax Collector!"); + return true; + } + + +// if (!city.isAfterTaxPeriod(DateTime.now(), player)) +// return true; + + + + ViewResourcesMessage vrm = new ViewResourcesMessage(player); + vrm.setGuild(building.getGuild()); + vrm.setWarehouseBuilding(BuildingManager.getBuildingFromCache(building.getCity().getWarehouse().getBuildingUID())); + vrm.configure(); + Dispatch dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + dispatch = Dispatch.borrow(player, vrm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return true; + + + + } + + + + + +} diff --git a/src/engine/net/client/handlers/TaxResourcesMsgHandler.java b/src/engine/net/client/handlers/TaxResourcesMsgHandler.java new file mode 100644 index 00000000..2e4e0e90 --- /dev/null +++ b/src/engine/net/client/handlers/TaxResourcesMsgHandler.java @@ -0,0 +1,73 @@ +package engine.net.client.handlers; + +import engine.exception.MsgSendException; +import engine.gameManager.BuildingManager; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.TaxResourcesMsg; +import engine.objects.Building; +import engine.objects.City; +import engine.objects.PlayerCharacter; + +/* + * @Author: + * @Summary: Processes application protocol message which handles + * protecting and unprotecting city assets + */ +public class TaxResourcesMsgHandler extends AbstractClientMsgHandler { + + public TaxResourcesMsgHandler() { + super(TaxResourcesMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + // Member variable declaration + + PlayerCharacter player; + TaxResourcesMsg msg; + + player = origin.getPlayerCharacter(); + if (player == null) + return true; + + + msg = (TaxResourcesMsg) baseMsg; + + TaxWarehouse(msg,player); + + + + return true; + + } + + private static boolean TaxWarehouse(TaxResourcesMsg msg, PlayerCharacter player) { + + // Member variable declaration + Building building = BuildingManager.getBuildingFromCache(msg.getBuildingID()); + + + if (building == null){ + ErrorPopupMsg.sendErrorMsg(player, "Not a valid Building!"); + return true; + } + + City city = building.getCity(); + if (city == null){ + ErrorPopupMsg.sendErrorMsg(player, "This building does not belong to a city."); + return true; + } + + city.TaxWarehouse(msg, player); + + + return true; + + + } + + +} diff --git a/src/engine/net/client/handlers/ToggleGroupSplitHandler.java b/src/engine/net/client/handlers/ToggleGroupSplitHandler.java new file mode 100644 index 00000000..39eefc2d --- /dev/null +++ b/src/engine/net/client/handlers/ToggleGroupSplitHandler.java @@ -0,0 +1,71 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.GroupManager; +import engine.gameManager.SessionManager; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.group.GroupUpdateMsg; +import engine.net.client.msg.group.ToggleGroupSplitMsg; +import engine.objects.Group; +import engine.objects.PlayerCharacter; + +public class ToggleGroupSplitHandler extends AbstractClientMsgHandler { + + public ToggleGroupSplitHandler() { + super(ToggleGroupSplitMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, + ClientConnection origin) throws MsgSendException { + + // Member variable declaration + + PlayerCharacter source; + Group group; + boolean split; + + source = SessionManager.getPlayerCharacter(origin); + + if (source == null) + return false; + + group = GroupManager.getGroup(source); + + if (group == null) + return false; + + if (group.getGroupLead() != source) // Only group lead can toggle + return false; + + split = group.toggleSplitGold(); + + // update split button + GroupUpdateMsg gum = new GroupUpdateMsg(); + gum.setGroup(group); + gum.setMessageType(6); + + group.sendUpdate(gum); + + // Send split message + + if (split) + ChatManager.chatGroupInfo(source, "Treasure is now being split."); + else + ChatManager.chatGroupInfo(source, "Treasure is no longer being split."); + + return false; + } + +} diff --git a/src/engine/net/client/handlers/TransferAssetMsgHandler.java b/src/engine/net/client/handlers/TransferAssetMsgHandler.java new file mode 100644 index 00000000..e0be0a85 --- /dev/null +++ b/src/engine/net/client/handlers/TransferAssetMsgHandler.java @@ -0,0 +1,99 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.exception.MsgSendException; +import engine.gameManager.BuildingManager; +import engine.gameManager.ChatManager; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.TransferAssetMsg; +import engine.objects.Blueprint; +import engine.objects.Building; +import engine.objects.PlayerCharacter; + +/* + * @Author: + * @Summary: Processes application protocol message which transers + * assets between characters. + */ + +public class TransferAssetMsgHandler extends AbstractClientMsgHandler { + + public TransferAssetMsgHandler() { + super(TransferAssetMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + TransferAssetMsg transferAssetMsg = (TransferAssetMsg) baseMsg; + + int Buildingid = transferAssetMsg.getObjectID(); + int BuildingType = transferAssetMsg.getObjectType(); //ToDue Later + int TargetID = transferAssetMsg.getTargetID(); + int TargetType = transferAssetMsg.getTargetType(); //ToDue later + + Building building = BuildingManager.getBuildingFromCache(Buildingid); + PlayerCharacter newOwner = PlayerCharacter.getFromCache(TargetID); + PlayerCharacter player = origin.getPlayerCharacter(); + + if (player == null || building == null || newOwner == null) + return true; + + Blueprint blueprint = building.getBlueprint(); + + if (blueprint == null) + return true; + + if (building.getBlueprint().getBuildingGroup() == Enum.BuildingGroup.MINE) { + ErrorPopupMsg.sendErrorMsg(player, "You cannot transfer a mine!"); + return true; + } + + // Players cannot transfer shrines + + if ((building.getBlueprint().getBuildingGroup() == Enum.BuildingGroup.SHRINE)) { + ErrorPopupMsg.sendErrorMsg(player, "Cannot for to transfer shrine!"); + return true; + } + + if (Blueprint.isMeshWallPiece(building.getBlueprintUUID())) { + ErrorPopupMsg.sendErrorMsg(player, "Cannot for to transfer fortress asset!"); + return true; + } + + if ((building.getBlueprint().getBuildingGroup() == Enum.BuildingGroup.BARRACK)) { + ErrorPopupMsg.sendErrorMsg(player, "Cannot for to transfer fortress asset!"); + return true; + } + + if ((building.getBlueprint().getBuildingGroup() == Enum.BuildingGroup.BULWARK)) { + ErrorPopupMsg.sendErrorMsg(player, "Cannot for to transfer siege asset!"); + return true; + } + + if ((building.getBlueprint().getBuildingGroup() == Enum.BuildingGroup.SIEGETENT)) { + ErrorPopupMsg.sendErrorMsg(player, "Cannot for to transfer siege asset!"); + return true; + } + + if ((building.getBlueprint().getBuildingGroup() == Enum.BuildingGroup.BANESTONE)) { + ErrorPopupMsg.sendErrorMsg(player, "Cannot for to transfer banestone!"); + return true; + } + if (building.getOwnerUUID() != player.getObjectUUID()) { + ChatManager.chatSystemError(player, "You do not own this asset."); + return true; + } + + if (building.getOwnerUUID() == newOwner.getObjectUUID()) { + ChatManager.chatSystemError(player, "You already own this asset."); + return true; + } + + building.setOwner(newOwner); + return true; + } + +} \ No newline at end of file diff --git a/src/engine/net/client/handlers/TransferGoldToFromBuildingMsgHandler.java b/src/engine/net/client/handlers/TransferGoldToFromBuildingMsgHandler.java new file mode 100644 index 00000000..e5cc9aed --- /dev/null +++ b/src/engine/net/client/handlers/TransferGoldToFromBuildingMsgHandler.java @@ -0,0 +1,115 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.DispatchChannel; +import engine.exception.MsgSendException; +import engine.gameManager.BuildingManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.PlaceAssetMsg; +import engine.net.client.msg.TransferGoldToFromBuildingMsg; +import engine.net.client.msg.UpdateGoldMsg; +import engine.objects.Building; +import engine.objects.CharacterItemManager; +import engine.objects.Item; +import engine.objects.PlayerCharacter; +import org.pmw.tinylog.Logger; + +/* + * @Author: + * @Summary: Processes application protocol message which transfers + * gold between a building's strongbox and a player character. + */ + +public class TransferGoldToFromBuildingMsgHandler extends AbstractClientMsgHandler { + + public TransferGoldToFromBuildingMsgHandler() { + super(TransferGoldToFromBuildingMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter player; + Building building; + CharacterItemManager itemMan; + Item goldItem; + TransferGoldToFromBuildingMsg msg; + Dispatch dispatch; + + player = SessionManager.getPlayerCharacter(origin); + + if (player == null) + return true; + + msg = (TransferGoldToFromBuildingMsg) baseMsg; + + building = BuildingManager.getBuildingFromCache(msg.getObjectID()); + + if (building == null) + return true; + + if (msg.getDirection() == 2){ + + if(!ManageCityAssetMsgHandler.playerCanManageNotFriends(player, building)) + return true; + if (building.setReserve(msg.getUnknown01(),player)){ + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + } + + return true; + } + + // if (building.getTimeStamp(MBServerStatics.STRONGBOX_DELAY_STRING) > System.currentTimeMillis()){ + // ErrorPopupMsg.sendErrorMsg(player, MBServerStatics.STRONGBOX_DELAY_OUTPUT); + // return true; + // } + + //building.getTimestamps().put(MBServerStatics.STRONGBOX_DELAY_STRING, System.currentTimeMillis() + MBServerStatics.ONE_MINUTE); + + itemMan = player.getCharItemManager(); + + goldItem = itemMan.getGoldInventory(); + + if (goldItem == null) { + Logger.error("Could not access gold item"); + return true; + } + + + // Update in-game gold values for player and building + + + try { + + + if (!itemMan.transferGoldToFromBuilding(msg.getAmount(), building)) + return true; + + + UpdateGoldMsg ugm = new UpdateGoldMsg(player); + ugm.configure(); + dispatch = Dispatch.borrow(player, ugm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + // Refresh the player's inventory if it's currently open + + // Refresh the tree's window to update strongbox + + + msg.setAmount(building.getStrongboxValue()); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + } catch (Exception e) { + PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + } + + return true; + } + +} diff --git a/src/engine/net/client/handlers/UpdateFriendStatusHandler.java b/src/engine/net/client/handlers/UpdateFriendStatusHandler.java new file mode 100644 index 00000000..cba23748 --- /dev/null +++ b/src/engine/net/client/handlers/UpdateFriendStatusHandler.java @@ -0,0 +1,84 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.handlers; + +import engine.Enum.DispatchChannel; +import engine.Enum.FriendStatus; +import engine.exception.MsgSendException; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.UpdateFriendStatusMessage; +import engine.objects.PlayerCharacter; +import engine.objects.PlayerFriends; +import org.pmw.tinylog.Logger; + +import java.util.HashSet; + +public class UpdateFriendStatusHandler extends AbstractClientMsgHandler { + + public UpdateFriendStatusHandler() { + super(UpdateFriendStatusMessage.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, + ClientConnection origin) throws MsgSendException { + + PlayerCharacter player = origin.getPlayerCharacter(); + + if (player == null) + return true; + + + UpdateFriendStatusMessage msg = (UpdateFriendStatusMessage)baseMsg; + + + HandleUpdateFriend(player,msg); + + return true; + } + + + //change to Request + public static void HandleUpdateFriend(PlayerCharacter player, UpdateFriendStatusMessage msg){ + FriendStatus friendStatus = FriendStatus.Available; + + try { + friendStatus = FriendStatus.values()[msg.statusType]; + }catch (Exception e){ + Logger.error(e); + } + player.friendStatus = friendStatus; + SendUpdateToFriends(player); + } + + public static void SendUpdateToFriends(PlayerCharacter player){ + + HashSet friends = PlayerFriends.PlayerFriendsMap.get(player.getObjectUUID()); + + if (friends == null) + return; + + UpdateFriendStatusMessage outMsg = new UpdateFriendStatusMessage(player); + + for (int friendID : friends){ + PlayerCharacter playerFriend = SessionManager.getPlayerCharacterByID(friendID); + if (playerFriend == null) + return; + Dispatch dispatch = Dispatch.borrow(playerFriend, outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + } + + + } +} diff --git a/src/engine/net/client/handlers/UpgradeAssetMsgHandler.java b/src/engine/net/client/handlers/UpgradeAssetMsgHandler.java new file mode 100644 index 00000000..3f3d0c9c --- /dev/null +++ b/src/engine/net/client/handlers/UpgradeAssetMsgHandler.java @@ -0,0 +1,154 @@ +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.exception.MsgSendException; +import engine.gameManager.BuildingManager; +import engine.gameManager.DbManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.*; +import engine.objects.Building; +import engine.objects.PlayerCharacter; +import org.pmw.tinylog.Logger; + +import java.time.LocalDateTime; + +import static engine.net.client.msg.ErrorPopupMsg.sendErrorPopup; + +/* + * + * @Summary: Processes application protocol message where a + * client requests that a building be upgraded. + */ +public class UpgradeAssetMsgHandler extends AbstractClientMsgHandler { + + // Constructor + public UpgradeAssetMsgHandler() { + + super(UpgradeAssetMessage.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + // Member variable declaration + + UpgradeAssetMessage msg; + ManageCityAssetsMsg outMsg; + PlayerCharacter player; + int buildingUUID; + Building buildingToRank; + LocalDateTime dateToUpgrade; + int nextRank; + int rankCost; + Dispatch dispatch; + + // Assign member variables + + msg = (UpgradeAssetMessage) baseMsg; + + // Grab pointer to the requesting player + + player = SessionManager.getPlayerCharacter(origin); + + // Grab pointer to the building from the cache + + buildingUUID = msg.getBuildingUUID(); + + buildingToRank = (Building) DbManager.getObject(GameObjectType.Building, buildingUUID); + + // Early exit if building not in cache. + + if (buildingToRank == null) { + Logger.error("Attempt to upgrade null building by " + player.getName()); + return true; + } + + // Early exit for building that is already ranking + + if (buildingToRank.isRanking()) { + Logger.error("Attempt to upgrade a building already ranking by " + player.getName()); + return true; + } + + // Calculate and set time/cost to upgrade + + nextRank = (buildingToRank.getRank() + 1); + + if (buildingToRank.getBlueprint() == null) + return true; + if (buildingToRank.getBlueprint().getMaxRank() < nextRank || nextRank == 8){ + ErrorPopupMsg.sendErrorMsg(player, "Building is already at it's Max rank."); + return true; + } + + rankCost = buildingToRank.getBlueprint().getRankCost(nextRank); + + // SEND NOT ENOUGH GOLD ERROR + + if (!buildingToRank.hasFunds(rankCost)){ + ErrorPopupMsg.sendErrorPopup(player, 127); // Not enough gold in strongbox + return true; + } + + if (rankCost > buildingToRank.getStrongboxValue()) { + sendErrorPopup(player, 127); + return true; + } + + // Validation appears good. Let's now process the upgrade + + try { + if (buildingToRank.getCity() != null){ + buildingToRank.getCity().transactionLock.writeLock().lock(); + try{ + if (!buildingToRank.transferGold(-rankCost,false)) { + sendErrorPopup(player, 127); + return true; + } + }catch(Exception e){ + Logger.error(e); + }finally{ + buildingToRank.getCity().transactionLock.writeLock().unlock(); + } + }else + if (!buildingToRank.transferGold(-rankCost,false)) { + sendErrorPopup(player, 127); + return true; + } + + dateToUpgrade = LocalDateTime.now().plusHours(buildingToRank.getBlueprint().getRankTime(nextRank)); + + BuildingManager.setUpgradeDateTime(buildingToRank, dateToUpgrade, 0); + + // Schedule upgrade job + + BuildingManager.submitUpgradeJob(buildingToRank); + + // Refresh the client's manage asset window + // *** Refactor : We have some of these unknowns + + outMsg = new ManageCityAssetsMsg(player, buildingToRank); + + // Action TYPE + outMsg.actionType = 3; + outMsg.setTargetType(buildingToRank.getObjectType().ordinal()); + outMsg.setTargetID(buildingToRank.getObjectUUID()); + outMsg.setTargetType3(buildingToRank.getObjectType().ordinal()); + outMsg.setTargetID3(buildingToRank.getObjectUUID()); + outMsg.setAssetName1(buildingToRank.getName()); + outMsg.setUnknown54(1); + + dispatch = Dispatch.borrow(player, outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + } catch (Exception e) { + PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + } + + return true; + } +} diff --git a/src/engine/net/client/msg/AbandonAssetMsg.java b/src/engine/net/client/msg/AbandonAssetMsg.java new file mode 100644 index 00000000..38bfb67e --- /dev/null +++ b/src/engine/net/client/msg/AbandonAssetMsg.java @@ -0,0 +1,75 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class AbandonAssetMsg extends ClientNetMsg { + + private int pad = 0; + private int objectType; + private int objectUUID; + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public AbandonAssetMsg(AbstractConnection origin, ByteBufferReader reader){ + super(Protocol.ABANDONASSET, origin, reader); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.pad = reader.getInt(); + this.objectType = reader.getInt(); + this.objectUUID = reader.getInt(); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + writer.putInt(this.pad); + writer.putInt(this.objectType); + writer.putInt(this.objectUUID); + } + + public int getObjectType() { + return objectType; + } + + public void setObjectType(int value) { + this.objectType = value; + } + + public void setPad(int value) { + this.pad = value; + } + + public int getUUID() { + return objectUUID; + + } + + public int getPad() { + return pad; + } +} diff --git a/src/engine/net/client/msg/AcceptFriendMsg.java b/src/engine/net/client/msg/AcceptFriendMsg.java new file mode 100644 index 00000000..c0344689 --- /dev/null +++ b/src/engine/net/client/msg/AcceptFriendMsg.java @@ -0,0 +1,65 @@ +/* +HashSet playerFriendSet = PlayerFriendsMap.get(playerUID); + playerFriendSet.add(friendUID); * Copyright 2013 MagicBane Emulator Project + * All Rights Reserved + */ +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.PlayerCharacter; + + +public class AcceptFriendMsg extends ClientNetMsg { + + public String sourceName; + public String friendName; + + /** + * This is the general purpose constructor. + */ + public AcceptFriendMsg(PlayerCharacter pc) { + super(Protocol.FRIENDACCEPT); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public AcceptFriendMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.FRIENDACCEPT, origin, reader); + } + + /** + * Copy constructor + */ + public AcceptFriendMsg(AcceptFriendMsg msg) { + super(Protocol.FRIENDACCEPT); + } + + + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + //Do we even want to try this? + this.sourceName = reader.getString(); //This is source name.. this friends list must never been updated since launch, not using IDS. + this.friendName = reader.getString(); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putString(this.sourceName); + writer.putString(this.friendName); + } +} diff --git a/src/engine/net/client/msg/AcceptTradeRequestMsg.java b/src/engine/net/client/msg/AcceptTradeRequestMsg.java new file mode 100644 index 00000000..2e2f3344 --- /dev/null +++ b/src/engine/net/client/msg/AcceptTradeRequestMsg.java @@ -0,0 +1,133 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractGameObject; + +/** + * Accept trade request + * + * @author Eighty + */ +public class AcceptTradeRequestMsg extends ClientNetMsg { + + private int unknown01; //pad? + private int playerType; + private int playerID; + private int targetType; + private int targetID; + + + /** + * This is the general purpose constructor + */ + public AcceptTradeRequestMsg(int unknown01, AbstractGameObject player, AbstractGameObject target) { + super(Protocol.REQUESTTRADEOK); + this.unknown01 = unknown01; + this.playerType = player.getObjectType().ordinal(); + this.playerID =player.getObjectUUID(); + this.targetType = target.getObjectType().ordinal(); + this.targetID =target.getObjectUUID(); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public AcceptTradeRequestMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.REQUESTTRADEOK, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + unknown01 = reader.getInt(); + playerType = reader.getInt(); + playerID = reader.getInt(); + targetType = reader.getInt(); + targetID = reader.getInt(); + + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + writer.putInt(unknown01); + writer.putInt(playerType); + writer.putInt(playerID); + writer.putInt(targetType); + writer.putInt(targetID); + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + + + + public int getPlayerType() { + return playerType; + } + + public void setPlayerType(int playerType) { + this.playerType = playerType; + } + + public int getPlayerID() { + return playerID; + } + + public void setPlayerID(int playerID) { + this.playerID = playerID; + } + + public int getTargetType() { + return targetType; + } + + public void setTargetType(int targetType) { + this.targetType = targetType; + } + + public int getTargetID() { + return targetID; + } + + public void setTargetID(int targetID) { + this.targetID = targetID; + } + +} diff --git a/src/engine/net/client/msg/AckBankWindowOpenedMsg.java b/src/engine/net/client/msg/AckBankWindowOpenedMsg.java new file mode 100644 index 00000000..c33d0a29 --- /dev/null +++ b/src/engine/net/client/msg/AckBankWindowOpenedMsg.java @@ -0,0 +1,90 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.PlayerCharacter; + +/** + * Bank window opened + * + * @author Burfo + */ +public class AckBankWindowOpenedMsg extends ClientNetMsg { + + private int playerType; + private int playerID; + private long unknown01; // possibly NPC ID? + private long unknown02; + + /** + * This is the general purpose constructor. + */ + public AckBankWindowOpenedMsg(PlayerCharacter pc, long unknown01, long unknown02) { + super(Protocol.COSTTOOPENBANK); + this.playerType = pc.getObjectType().ordinal(); + this.playerID = pc.getObjectUUID(); + this.unknown01 = unknown01; + this.unknown02 = unknown02; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public AckBankWindowOpenedMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.COSTTOOPENBANK, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + playerType = reader.getInt(); + playerID = reader.getInt(); + unknown01 = reader.getLong(); + unknown02 = reader.getLong(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + writer.putInt(playerType); + writer.putInt(playerID); + writer.putLong(unknown01); + writer.putLong(unknown02); + } + public int getPlayerID() { + return playerID; + } + + public void setPlayerID(int playerID) { + this.playerID = playerID; + } + + public int getPlayerType() { + return playerType; + } + + public void setPlayerType(int playerType) { + this.playerType = playerType; + } + +} diff --git a/src/engine/net/client/msg/ActivateNPCMessage.java b/src/engine/net/client/msg/ActivateNPCMessage.java new file mode 100644 index 00000000..af423ff3 --- /dev/null +++ b/src/engine/net/client/msg/ActivateNPCMessage.java @@ -0,0 +1,159 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.AbstractNetMsg; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Item; + +import java.util.ArrayList; + +public class ActivateNPCMessage extends ClientNetMsg { + + private ArrayList ItemList; + private int unknown01; + private int unknown02; + private int buildingUUID; + private int unknown03; + private int unknown04; + private int unknown05; + private int size; + + /** + * This is the general purpose constructor. + */ + public ActivateNPCMessage() { + this(new ArrayList<>()); + } + + /** + * This is the general purpose constructor. + * + */ + public ActivateNPCMessage(ArrayList items) { + super(Protocol.ACTIVATENPC); + this.unknown01 = 0; + this.size = 0; + this.ItemList = items; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ActivateNPCMessage(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ACTIVATENPC, origin, reader); + } + + /** + * @see AbstractNetMsg#getPowerOfTwoBufferSize() + */ + @Override + protected int getPowerOfTwoBufferSize() { + //Larger size for historically larger opcodes + return (16); // 2^16 == 64k + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + for (int i = 0; i < 6; i++) { + writer.putInt(0); + } + writer.putInt(this.size); + for (Item item : this.ItemList) { + writer.putInt(item.getObjectType().ordinal()); + writer.putInt(item.getObjectUUID()); + + } + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + unknown01 = reader.getInt(); + unknown02 = reader.getInt(); + reader.getInt(); // Object Type Padding + buildingUUID = reader.getInt(); + unknown03 = reader.getInt(); + unknown04 = reader.getInt(); + unknown05 = reader.getInt(); + + } + + // TODO fix ArrayList Accessability. + + public int size() { + return this.ItemList.size(); + } + + public int getUnknown01() { + return unknown01; + } + + public int getUnknown02() { + return unknown02; + } + + public int getUnknown03() { + return unknown03; + } + + public int getUnknown04() { + return unknown04; + } + + public int getUnknown05() { + return unknown05; + } + + public int buildingUUID() { + return buildingUUID; + } + + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + public void setSize(int size) { + this.size = size; + } + + public void setUnknown02(int unknown02) { + this.unknown02 = unknown02; + } + + public void setUnknown03(int unknown03) { + this.unknown03 = unknown03; + } + + public void setUnknown04(int unknown04) { + this.unknown04 = unknown04; + } + + public void setUnknown05(int unknown05) { + this.unknown05 = unknown05; + } + + public void setItemList(ArrayList value) { + this.ItemList = value; + } + +} diff --git a/src/engine/net/client/msg/AddFriendMessage.java b/src/engine/net/client/msg/AddFriendMessage.java new file mode 100644 index 00000000..9d8d33f1 --- /dev/null +++ b/src/engine/net/client/msg/AddFriendMessage.java @@ -0,0 +1,67 @@ +/* +HashSet playerFriendSet = PlayerFriendsMap.get(playerUID); + playerFriendSet.add(friendUID); * Copyright 2013 MagicBane Emulator Project + * All Rights Reserved + */ +package engine.net.client.msg; + + +import engine.Enum.GameObjectType; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.PlayerCharacter; + + +public class AddFriendMessage extends ClientNetMsg { + + public PlayerCharacter friend; + + /** + * This is the general purpose constructor. + */ + public AddFriendMessage(PlayerCharacter pc) { + super(Protocol.ADDFRIEND); + friend = pc; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public AddFriendMessage(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ADDFRIEND, origin, reader); + } + + /** + * Copy constructor + */ + public AddFriendMessage(AddFriendMessage msg) { + super(Protocol.ADDFRIEND); + } + + + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + //message is serialize only, no need for deserialize. + @Override + protected void _deserialize(ByteBufferReader reader) { + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(GameObjectType.PlayerCharacter.ordinal()); + writer.putInt(friend.getObjectUUID()); + writer.putString(friend.getName()); + writer.putInt(0); //possibly available, busy, away? + writer.putInt(0); + } +} diff --git a/src/engine/net/client/msg/AddGoldToTradeWindowMsg.java b/src/engine/net/client/msg/AddGoldToTradeWindowMsg.java new file mode 100644 index 00000000..462d6334 --- /dev/null +++ b/src/engine/net/client/msg/AddGoldToTradeWindowMsg.java @@ -0,0 +1,97 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class AddGoldToTradeWindowMsg extends ClientNetMsg { + + private int unknown01; + private long playerCompID; + private int amount; + + /** + * This is the general purpose constructor + */ + public AddGoldToTradeWindowMsg(int unknown01, long playerCompID, int amount) { + super(Protocol.TRADEADDGOLD); + this.unknown01 = unknown01; + this.playerCompID = playerCompID; + this.amount = amount; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public AddGoldToTradeWindowMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.TRADEADDGOLD, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + unknown01 = reader.getInt(); + playerCompID = reader.getLong(); + amount = reader.getInt(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + writer.putInt(unknown01); + writer.putLong(playerCompID); + writer.putInt(amount); + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the amount + */ + public int getAmount() { + return amount; + } + + /** + * @param amount the amount to set + */ + public void setAmount(int amount) { + this.amount = amount; + } + +} diff --git a/src/engine/net/client/msg/AddItemToTradeWindowMsg.java b/src/engine/net/client/msg/AddItemToTradeWindowMsg.java new file mode 100644 index 00000000..5d420d70 --- /dev/null +++ b/src/engine/net/client/msg/AddItemToTradeWindowMsg.java @@ -0,0 +1,120 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractGameObject; + +public class AddItemToTradeWindowMsg extends ClientNetMsg { + + private int unknown01; + private int playerType; + private int playerID; + private int itemID; + private int itemType; + + + /** + * This is the general purpose constructor + */ + public AddItemToTradeWindowMsg(int unknown01, AbstractGameObject player, AbstractGameObject item) { + super(Protocol.TRADEADDOBJECT); + this.unknown01 = unknown01; + this.playerType = player.getObjectType().ordinal(); + this.playerID = player.getObjectUUID(); + this.itemType = item.getObjectType().ordinal(); + this.itemID = item.getObjectUUID(); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public AddItemToTradeWindowMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.TRADEADDOBJECT, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + unknown01 = reader.getInt(); + playerType = reader.getInt(); + playerID = reader.getInt(); + itemType = reader.getInt(); + itemID = reader.getInt(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + writer.putInt(unknown01); + writer.putInt(playerType); + writer.putInt(playerID); + writer.putInt(itemType); + writer.putInt(itemID); + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + + + + + public int getPlayerType() { + return playerType; + } + + public void setPlayerType(int playerType) { + this.playerType = playerType; + } + + public int getPlayerID() { + return playerID; + } + + public void setPlayerID(int playerID) { + this.playerID = playerID; + } + + public int getItemID() { + return itemID; + } + + public void setItemID(int itemID) { + this.itemID = itemID; + } + +} diff --git a/src/engine/net/client/msg/AllianceChangeMsg.java b/src/engine/net/client/msg/AllianceChangeMsg.java new file mode 100644 index 00000000..a1023b46 --- /dev/null +++ b/src/engine/net/client/msg/AllianceChangeMsg.java @@ -0,0 +1,144 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.PlayerCharacter; + + + +public class AllianceChangeMsg extends ClientNetMsg { + public final static int MAKE_ENEMY = 4; + public final static int MAKE_ALLY = 6; + public final static int REMOVE = 7; + public final static byte INFO_SUCCESS = 0; + public final static byte ERROR_NOT_RECOMMENDED = 1; + public final static byte ERROR_NOT_SAME_GUILD = 2; + public final static byte ERROR_NOT_AUTHORIZED = 4; + public final static byte ERROR_NOT_SAME_FACTION = 7; + public final static byte ERROR_TOO_MANY = 13; + public final static byte ERROR_TO0_EARLY = 14; + private byte msgType; + private int sourceGuildID; + private int targetGuildID; + private int secondsToWait; + private boolean ally; + + + public AllianceChangeMsg(PlayerCharacter player, int sourceGuildID,int targetGuildID, byte msgType, int secondsToWait) { + super(Protocol.ALLIANCECHANGE); + this.sourceGuildID = sourceGuildID; + this.targetGuildID = targetGuildID; + this.msgType = msgType; + this.secondsToWait = secondsToWait; + + } + + public AllianceChangeMsg() { + super(Protocol.ALLIANCECHANGE); + } + + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public AllianceChangeMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ALLIANCECHANGE, origin, reader); + } + //CALL THIS AFTER SANITY CHECKS AND BEFORE UPDATING HEALTH/GOLD. + + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.msgType = reader.get(); + + switch (this.msgType){ + case 1: + case 2: + case 3: + case MAKE_ENEMY: + case MAKE_ALLY: + case REMOVE: + case 5: + reader.getInt(); //source guild type; + this.sourceGuildID = reader.getInt(); + reader.getInt(); + this.targetGuildID = reader.getInt(); + break; + + + } + + + + + } + + + // Precache and configure this message before we serialize it + + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + writer.put(this.msgType); + if (this.msgType == ERROR_TO0_EARLY) + writer.putInt(this.secondsToWait); + writer.putInt(GameObjectType.Guild.ordinal()); + writer.putInt(this.sourceGuildID); + writer.putInt(GameObjectType.Guild.ordinal()); + writer.putInt(this.targetGuildID); + + + + } + + + public int getSecondsToWait() { + return secondsToWait; + } + + public void setSecondsToWait(int secondsToWait) { + this.secondsToWait = secondsToWait; + } + + public int getMsgType() { + return msgType; + } + + public int getSourceGuildID() { + return sourceGuildID; + } + + public int getTargetGuildID() { + return targetGuildID; + } + + public boolean isAlly() { + return ally; + } + + public void setMsgType(byte msgType) { + this.msgType = msgType; + } +} diff --git a/src/engine/net/client/msg/AllyEnemyListMsg.java b/src/engine/net/client/msg/AllyEnemyListMsg.java new file mode 100644 index 00000000..aa47bb9a --- /dev/null +++ b/src/engine/net/client/msg/AllyEnemyListMsg.java @@ -0,0 +1,122 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Guild; +import engine.objects.GuildAlliances; +import engine.objects.GuildTag; +import engine.objects.PlayerCharacter; + + + +public class AllyEnemyListMsg extends ClientNetMsg { + + + private int guildID; + + + public AllyEnemyListMsg(PlayerCharacter player) { + super(Protocol.ALLYENEMYLIST); + this.guildID = player.getGuildUUID(); + + } + + public AllyEnemyListMsg() { + super(Protocol.ALLYENEMYLIST); + } + + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public AllyEnemyListMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ALLYENEMYLIST, origin, reader); + } + //CALL THIS AFTER SANITY CHECKS AND BEFORE UPDATING HEALTH/GOLD. + + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); + this.guildID = reader.getInt(); + } + + + // Precache and configure this message before we serialize it + + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + + writer.putInt(GameObjectType.Guild.ordinal()); + writer.putInt(this.guildID); + + Guild guild = Guild.getGuild(this.guildID); + + writer.putInt(guild.getAllyList().size()); + + for (Guild ally: guild.getAllyList()){ + writer.putInt(GameObjectType.Guild.ordinal());//guildType + writer.putInt(ally.getObjectUUID());//GuildID + writer.putString(ally.getName()); + GuildTag._serializeForDisplay(ally.getGuildTag(),writer); + writer.put((byte)0); + } + + + writer.putInt(guild.getEnemyList().size()); + + for (Guild enemy: guild.getEnemyList()){ + writer.putInt(GameObjectType.Guild.ordinal());//guildType + writer.putInt(enemy.getObjectUUID());//GuildID + writer.putString(enemy.getName()); + GuildTag._serializeForDisplay(enemy.getGuildTag(),writer); + writer.put((byte)1); + } + + + writer.putInt(guild.getRecommendList().size()); + for (Guild recommended: guild.getRecommendList()){ + + GuildAlliances guildAlliance = guild.guildAlliances.get(recommended.getObjectUUID()); + writer.putInt(GameObjectType.Guild.ordinal());//guildType + writer.putInt(recommended.getObjectUUID());//GuildID + writer.putString(recommended.getName()); + GuildTag._serializeForDisplay(recommended.getGuildTag(),writer); + writer.put((byte)1); // ? + writer.putString(guildAlliance.getRecommender()); // recommender name. + writer.put((byte) (guildAlliance.isAlly()?1:0)); //ally 1 enemy 0 + + } + + writer.put((byte)1); + + } + + public int getGuildID() { + return guildID; + } + +} diff --git a/src/engine/net/client/msg/ApplyBuildingEffectMsg.java b/src/engine/net/client/msg/ApplyBuildingEffectMsg.java new file mode 100644 index 00000000..0aa953e0 --- /dev/null +++ b/src/engine/net/client/msg/ApplyBuildingEffectMsg.java @@ -0,0 +1,110 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class ApplyBuildingEffectMsg extends ClientNetMsg { + + protected int unknown01; + protected int unknown02; + protected int buildingType; + protected int buildingID; + protected int unknown03; + + /** + * This is the general purpose constructor. + */ + public ApplyBuildingEffectMsg() { + super(Protocol.VISUALUPDATE); + } + + public ApplyBuildingEffectMsg(int unknown01, int unknown02, int buildingType, int buildingID, int unknown03) { + super(Protocol.VISUALUPDATE); + this.unknown01 = unknown01; + this.unknown02 = unknown02; + this.buildingType = buildingType; + this.buildingID = buildingID; + this.unknown03 = unknown03; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ApplyBuildingEffectMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.VISUALUPDATE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + if (this.unknown02 == 0){ + writer.putInt(this.unknown03); + writer.putInt(this.buildingType); + writer.putInt(this.buildingID); + writer.putInt(0); + return; + } + writer.putInt(this.buildingType); + writer.putInt(this.buildingID); + writer.putInt(this.unknown03); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + } + + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + public void setUnknown02(int unknown02) { + this.unknown02 = unknown02; + } + + public void setBuildingID(int buildingID) { + this.buildingID = buildingID; + } + + public void setUnknown03(int unknown03) { + this.unknown03 = unknown03; + } + + public int getUnknown01() { + return unknown01; + } + + public int getUnknown02() { + return unknown02; + } + + public int getBuildingID() { + return buildingID; + } + + public int getUnknown03() { + return unknown03; + } +} diff --git a/src/engine/net/client/msg/ApplyEffectMsg.java b/src/engine/net/client/msg/ApplyEffectMsg.java new file mode 100644 index 00000000..1644c011 --- /dev/null +++ b/src/engine/net/client/msg/ApplyEffectMsg.java @@ -0,0 +1,264 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractWorldObject; + +public class ApplyEffectMsg extends ClientNetMsg { + + protected int numTrains; + protected int effectID; + protected int sourceType; + protected int sourceID; + protected int targetType; + protected int targetID; + + protected int unknown02; + protected int unknown03; + protected int duration; + protected int unknown05; + protected byte unknown06; + + protected int powerUsedID; + + protected String powerUsedName; + private int effectSourceType = 0; + private int effectSourceID = 0; + + /** + * This is the general purpose constructor. + */ + public ApplyEffectMsg() { + super(Protocol.POWERACTION); + this.numTrains = 0; + this.effectID = 0; + this.sourceType = 0; + this.sourceID = 0; + this.targetType = 0; + this.targetID = 0; + + this.unknown02 = 0; + this.unknown03 = 0; + this.duration = 0; + this.unknown05 = 0; + this.unknown06 = (byte) 0; + + this.powerUsedID = 0; + this.powerUsedName = ""; + } + + /** + * This is the general purpose constructor. + */ + public ApplyEffectMsg(AbstractWorldObject source, AbstractWorldObject target, int numTrains, int effectID, int duration, + int powerUsedID, String powerUsedName) { + super(Protocol.POWERACTION); + this.numTrains = numTrains; + this.effectID = effectID; + + if (source != null) { + this.sourceType = source.getObjectType().ordinal(); + this.sourceID = source.getObjectUUID(); + } else { + this.sourceType = 0; + this.sourceID = 0; + } + + if (target != null) { + this.targetType = target.getObjectType().ordinal(); + this.targetID = target.getObjectUUID(); + } else { + this.targetType = 0; + this.targetID = 0; + } + this.unknown02 = 0; + this.unknown03 = 0; + this.duration = duration; + this.unknown05 = 0; + this.unknown06 = (byte) 0; + + this.powerUsedID = powerUsedID; + this.powerUsedName = powerUsedName; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ApplyEffectMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.POWERACTION, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.numTrains); + writer.putInt(this.effectID); + + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.putInt(this.targetType); + writer.putInt(this.targetID); + + writer.putInt(this.unknown02); + writer.putInt(this.unknown03); + writer.putInt(this.duration); + writer.putInt(this.unknown05); + writer.put(this.unknown06); + + if (this.unknown06 == (byte) 1) { + writer.putInt(this.effectSourceType); + writer.putInt(this.effectSourceID); + } + + else { + writer.putInt(this.powerUsedID); + } + + writer.putString(this.powerUsedName); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.numTrains = reader.getInt(); + this.effectID = reader.getInt(); + + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + + this.unknown02 = reader.getInt(); + this.unknown03 = reader.getInt(); + this.duration = reader.getInt(); + this.unknown05 = reader.getInt(); + this.unknown06 = reader.get(); + + this.powerUsedID = reader.getInt(); + + this.powerUsedName = reader.getString(); + } + + public int getNumTrains() { + return this.numTrains; + } + + public int getEffectID() { + return this.effectID; + } + + public int getSourceType() { + return this.sourceType; + } + + public int getSourceID() { + return this.sourceID; + } + + public int getTargetType() { + return this.targetType; + } + + public int getTargetID() { + return this.targetID; + } + + public int getUnknown02() { + return this.unknown02; + } + + public int getUnknown03() { + return this.unknown03; + } + + public int getDuration() { + return this.duration; + } + + public int getUnknown05() { + return this.unknown05; + } + + public byte getUnknown06() { + return this.unknown06; + } + + public void setNumTrains(int value) { + this.numTrains = value; + } + + public void setEffectID(int value) { + this.effectID = value; + } + + public void setSourceType(int value) { + this.sourceType = value; + } + + public void setSourceID(int value) { + this.sourceID = value; + } + + public void setTargetType(int value) { + this.targetType = value; + } + + public void setTargetID(int value) { + this.targetID = value; + } + + public void setUnknown02(int value) { + this.unknown02 = value; + } + + public void setUnknown03(int value) { + this.unknown03 = value; + } + + public void setDuration(int value) { + this.duration = value; + } + + public void setUnknown05(int value) { + this.unknown05 = value; + } + + public void setUnknown06(byte value) { + this.unknown06 = value; + } + + public void setPowerUsedID(int value) { + this.powerUsedID = value; + } + + public void setPowerUsedName(String value) { + this.powerUsedName = value; + } + + public void setEffectSourceType(int effectSourceType) { + this.effectSourceType = effectSourceType; + } + + public void setEffectSourceID(int effectSourceID) { + this.effectSourceID = effectSourceID; + } +} diff --git a/src/engine/net/client/msg/ApplyRuneMsg.java b/src/engine/net/client/msg/ApplyRuneMsg.java new file mode 100644 index 00000000..5850e67e --- /dev/null +++ b/src/engine/net/client/msg/ApplyRuneMsg.java @@ -0,0 +1,393 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum; +import engine.gameManager.DbManager; +import engine.net.*; +import engine.net.client.ClientConnection; +import engine.net.client.Protocol; +import engine.objects.CharacterRune; +import engine.objects.PlayerCharacter; +import engine.objects.RuneBase; +import engine.objects.RuneBaseAttribute; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; + +public class ApplyRuneMsg extends ClientNetMsg { + + private int targetType; + private int targetID; + private int removeRuneBase; + private int runeBase; + private int runeType; + private int runeID; + private Boolean isPromo; + + /** + * This is the general purpose constructor. + */ + public ApplyRuneMsg(int targetType, int targetID, int runeBase, int runeType, int runeID, Boolean isPromo) { + super(Protocol.SETRUNE); + this.targetType = targetType; + this.targetID = targetID; + this.runeBase = runeBase; + this.runeType = runeType; + this.runeID = runeID; + this.isPromo = isPromo; + this.removeRuneBase = 0; + } + + public ApplyRuneMsg(int targetType, int targetID, int removeRuneBase) { + super(Protocol.SETRUNE); + this.targetType = targetType; + this.targetID = targetID; + this.runeBase = 0; + this.runeType = 0; + this.runeID = 0; + this.isPromo = false; + this.removeRuneBase = removeRuneBase; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ApplyRuneMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.SETRUNE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.targetType); + writer.putInt(this.targetID); + writer.putInt(0); + writer.putInt(this.removeRuneBase); + writer.putInt(0); + writer.putInt(this.runeBase); + writer.putInt(this.runeType); + writer.putInt(this.runeID); + if (this.isPromo) { + writer.put((byte) 1); + } else { + writer.put((byte) 0); + } + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + reader.getInt(); + this.removeRuneBase = reader.getInt(); + reader.getInt(); + this.runeBase = reader.getInt(); + this.runeType = reader.getInt(); + this.runeID = reader.getInt(); + this.isPromo = (reader.get() == 1) ? true : false; + } + + public int getTargetType() { + return targetType; + } + + public void setTargetType(int value) { + this.targetType = value; + } + + public int getTargetID() { + return targetID; + } + + public void setTargetID(int value) { + this.targetID = value; + } + + public int getRuneID() { + return runeID; + } + + public void setRuneID(int value) { + this.runeID = value; + } + + public static boolean applyRune(int runeID, ClientConnection origin, PlayerCharacter playerCharacter) { + + RuneBase rb = RuneBase.getRuneBase(runeID); + Dispatch dispatch; + + if (playerCharacter == null || origin == null || rb == null) { + return false; + } + + //Check race is met + ConcurrentHashMap races = rb.getRace(); + if (races.size() > 0) { + int raceID = playerCharacter.getRaceID(); + boolean valid = false; + for (int validID : races.keySet()) { + if (validID == raceID) { + valid = true; + break; + } + } + if (!valid) { + return false; + } + } + + //Check base class is met + ConcurrentHashMap baseClasses = rb.getBaseClass(); + if (baseClasses.size() > 0) { + int baseClassID = playerCharacter.getBaseClassID(); + boolean valid = false; + for (int validID : baseClasses.keySet()) { + if (validID == baseClassID) { + valid = true; + break; + } + } + if (!valid) { + return false; + } + } + + //Check promotion class is met + ConcurrentHashMap promotionClasses = rb.getPromotionClass(); + if (promotionClasses.size() > 0) { + int promotionClassID = playerCharacter.getPromotionClassID(); + boolean valid = false; + for (int validID : promotionClasses.keySet()) { + if (validID == promotionClassID) { + valid = true; + break; + } + } + if (!valid) { + return false; + } + } + + //Check disciplines are met + ArrayList runes = playerCharacter.getRunes(); + ConcurrentHashMap disciplines = rb.getDiscipline(); + if (disciplines.size() > 0) { + for (CharacterRune cr : runes) { + int runeBaseID = cr.getRuneBaseID(); + for (Integer prohID : disciplines.keySet()) { + if (runeBaseID == prohID) { + return false; //Prohibited rune + } + } + } + } + + int discCount = 0; + for (CharacterRune cr : runes) { + int runeBaseID = cr.getRuneBaseID(); + //count number of discipline runes + if (runeBaseID > 3000 && runeBaseID < 3049) { + discCount++; + } + //see if rune is already applied + if (runeBaseID == runeID) { + return false; + } + } + + //Check level is met + if (playerCharacter.getLevel() < rb.getLevelRequired()) { + return false; + } + + int strTotal = 0; + int dexTotal = 0; + int conTotal = 0; + int intTotal = 0; + int spiTotal = 0; + int cost = 0; + + //Check any attributes are met + ArrayList attrs = rb.getAttrs(); + + if (rb.getAttrs() != null) + for (RuneBaseAttribute rba : attrs) { + int attrID = rba.getAttributeID(); + int mod = rba.getModValue(); + switch (attrID) { + case MBServerStatics.RUNE_COST_ATTRIBUTE_ID: + if (mod > playerCharacter.getUnusedStatPoints()) { + return false; + } + cost = mod; + break; + case MBServerStatics.RUNE_STR_MIN_NEEDED_ATTRIBUTE_ID: + if ((int) playerCharacter.statStrBase < mod) { + return false; + } + strTotal = mod; + break; + case MBServerStatics.RUNE_DEX_MIN_NEEDED_ATTRIBUTE_ID: + if ((int) playerCharacter.statDexBase < mod) { + return false; + } + dexTotal = mod; + break; + case MBServerStatics.RUNE_CON_MIN_NEEDED_ATTRIBUTE_ID: + if ((int) playerCharacter.statConBase < mod) { + return false; + } + conTotal = mod; + break; + case MBServerStatics.RUNE_INT_MIN_NEEDED_ATTRIBUTE_ID: + if ((int) playerCharacter.statIntBase < mod) { + return false; + } + intTotal = mod; + break; + case MBServerStatics.RUNE_SPI_MIN_NEEDED_ATTRIBUTE_ID: + if ((int) playerCharacter.statSpiBase < mod) { + return false; + } + spiTotal = mod; + break; + case MBServerStatics.RUNE_STR_ATTRIBUTE_ID: + strTotal += mod; + break; + case MBServerStatics.RUNE_DEX_ATTRIBUTE_ID: + dexTotal += mod; + break; + case MBServerStatics.RUNE_CON_ATTRIBUTE_ID: + conTotal += mod; + break; + case MBServerStatics.RUNE_INT_ATTRIBUTE_ID: + intTotal += mod; + break; + case MBServerStatics.RUNE_SPI_ATTRIBUTE_ID: + spiTotal += mod; + break; + } + } + + //Check if max number runes already reached + if (runes.size() > 12) { + return false; + } + + //if discipline, check number applied + if (isDiscipline(runeID)) { + if (playerCharacter.getLevel() < 70) { + if (discCount > 2) { + return false; + } + } else { + if (discCount > 3) { + return false; + } + } + } + + //Everything succeeded. Let's apply the rune + //Attempt add rune to database + CharacterRune runeWithoutID = new CharacterRune(rb, playerCharacter.getObjectUUID()); + CharacterRune cr; + try { + cr = DbManager.CharacterRuneQueries.ADD_CHARACTER_RUNE(runeWithoutID); + } catch (Exception e) { + cr = null; + Logger.error(e); + } + if (cr == null) { + return false; + } + + //remove any overridden runes from player + ArrayList overwrite = rb.getOverwrite(); + CharacterRune toRemove = null; + if (overwrite.size() > 0) { + for (int overwriteID : overwrite) { + toRemove = playerCharacter.removeRune(overwriteID); + } + } + + //add rune to player + playerCharacter.addRune(cr); + + // recalculate all bonuses/formulas/skills/powers + playerCharacter.recalculate(); + + //if overwriting a stat rune, add any amount granted from previous rune. + if (toRemove != null) { + RuneBase rbs = toRemove.getRuneBase(); + if (rbs != null && rbs.getObjectUUID() > 249999 && rbs.getObjectUUID() < 250045) { + //add any additional stats to match old amount + int dif = strTotal - (int) playerCharacter.statStrBase; + if (dif > 0 && strTotal < (int) playerCharacter.statStrMax) { + playerCharacter.addStr(dif); + } + dif = dexTotal - (int) playerCharacter.statDexBase; + if (dif > 0 && dexTotal < (int) playerCharacter.statDexMax) { + playerCharacter.addDex(dif); + } + dif = conTotal - (int) playerCharacter.statConBase; + if (dif > 0 && conTotal < (int) playerCharacter.statConMax) { + playerCharacter.addCon(dif); + } + dif = intTotal - (int) playerCharacter.statIntBase; + if (dif > 0 && intTotal < (int) playerCharacter.statIntMax) { + playerCharacter.addInt(dif); + } + dif = spiTotal - (int) playerCharacter.statSpiBase; + if (dif > 0 && spiTotal < (int) playerCharacter.statSpiMax) { + playerCharacter.addSpi(dif); + } + + // recalculate all bonuses/formulas/skills/powers + playerCharacter.recalculate(); + } + } + + if (cost > 0) { + ModifyStatMsg msm = new ModifyStatMsg((0 - cost), 0, 3); + dispatch = Dispatch.borrow(playerCharacter, msm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + //send apply rune message to client + ApplyRuneMsg arm = new ApplyRuneMsg(playerCharacter.getObjectType().ordinal(), playerCharacter.getObjectUUID(), runeID, cr.getObjectType().ordinal(), cr.getObjectUUID(), false); + dispatch = Dispatch.borrow(playerCharacter, arm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + + //alert them of success + ErrorPopupMsg.sendErrorPopup(playerCharacter, 160); + + //reapply bonuses + playerCharacter.applyBonuses(); + + return true; + } + + public static boolean isDiscipline(int runeID) { + + return runeID > 3000 && runeID < 3050; + } +} diff --git a/src/engine/net/client/msg/ArcLoginNotifyMsg.java b/src/engine/net/client/msg/ArcLoginNotifyMsg.java new file mode 100644 index 00000000..57266e55 --- /dev/null +++ b/src/engine/net/client/msg/ArcLoginNotifyMsg.java @@ -0,0 +1,148 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class ArcLoginNotifyMsg extends ClientNetMsg { + + private int unknown01; + private int unknown02; + private int unknown03; + private short unknown04; + private byte unknown05; + + /** + * This is the general purpose constructor. + */ + public ArcLoginNotifyMsg() { + super(Protocol.ARCLOGINNOTIFY); + this.unknown01 = 0x40A5BDB0; + this.unknown02 = 0x342AA9F0; + this.unknown03 = 0; + this.unknown04 = (short) 0; + this.unknown05 = (byte) 0; + + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ArcLoginNotifyMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ARCLOGINNOTIFY, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + writer.putInt(this.unknown03); + writer.putShort(this.unknown04); + writer.put(this.unknown05); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + this.unknown03 = reader.getInt(); + this.unknown04 = reader.getShort(); + this.unknown05 = reader.get(); + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the unknown02 + */ + public int getUnknown02() { + return unknown02; + } + + /** + * @param unknown02 + * the unknown02 to set + */ + public void setUnknown02(int unknown02) { + this.unknown02 = unknown02; + } + + /** + * @return the unknown03 + */ + public int getUnknown03() { + return unknown03; + } + + /** + * @param unknown03 + * the unknown03 to set + */ + public void setUnknown03(int unknown03) { + this.unknown03 = unknown03; + } + + /** + * @return the unknown04 + */ + public short getUnknown04() { + return unknown04; + } + + /** + * @param unknown04 + * the unknown04 to set + */ + public void setUnknown04(short unknown04) { + this.unknown04 = unknown04; + } + + /** + * @return the unknown05 + */ + public byte getUnknown05() { + return unknown05; + } + + /** + * @param unknown05 + * the unknown05 to set + */ + public void setUnknown05(byte unknown05) { + this.unknown05 = unknown05; + } + +} diff --git a/src/engine/net/client/msg/ArcMineChangeProductionMsg.java b/src/engine/net/client/msg/ArcMineChangeProductionMsg.java new file mode 100644 index 00000000..f6f8a3aa --- /dev/null +++ b/src/engine/net/client/msg/ArcMineChangeProductionMsg.java @@ -0,0 +1,60 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class ArcMineChangeProductionMsg extends ClientNetMsg { + + private int mineID; + private int resourceHash; + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ArcMineChangeProductionMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ARCMINECHANGEPRODUCTION, origin, reader); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.mineID = reader.getInt(); + this.resourceHash = reader.getInt(); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + writer.putInt(this.mineID); + writer.putInt(this.resourceHash); + } + + public int getMineID() { + return this.mineID; + } + + public int getResourceHash() { + return this.resourceHash; + } + +} diff --git a/src/engine/net/client/msg/ArcMineWindowAvailableTimeMsg.java b/src/engine/net/client/msg/ArcMineWindowAvailableTimeMsg.java new file mode 100644 index 00000000..ac95d4ef --- /dev/null +++ b/src/engine/net/client/msg/ArcMineWindowAvailableTimeMsg.java @@ -0,0 +1,110 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Building; +import engine.objects.Guild; +import engine.server.MBServerStatics; +import org.joda.time.DateTime; +import org.joda.time.Seconds; + +public class ArcMineWindowAvailableTimeMsg extends ClientNetMsg { + + private int buildingUUID; + private Building treeOfLife; + + private int currentMineHour; + private Seconds secondsLeft; + private DateTime lateTime; + private int late; + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ArcMineWindowAvailableTimeMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ARCMINEWINDOWAVAILABLETIME, origin, reader); + } + + public ArcMineWindowAvailableTimeMsg(Building treeOfLife, int timeLeft) { + super(Protocol.ARCMINEWINDOWAVAILABLETIME); + this.treeOfLife = treeOfLife; + this.buildingUUID = treeOfLife.getObjectUUID(); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); // Object type padding (We know it's a building) + this.buildingUUID = reader.getInt(); + reader.getInt(); + + } + + // Configures and pre-caches values for this message + // so everything is already available during serialisation. + + public void configure() { + + Guild guild; + guild = this.treeOfLife.getGuild(); + + if (guild != null) + currentMineHour = guild.getMineTime(); + + late = MBServerStatics.MINE_LATE_WINDOW; + lateTime = DateTime.now(); + + if (late == 0) + lateTime = lateTime.plusDays(1); + + lateTime = lateTime.hourOfDay().setCopy(late); + + late = ((late > 23) ? (late - 24) : late); + secondsLeft = Seconds.secondsBetween(DateTime.now(), lateTime); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + + writer.putInt(MBServerStatics.MINE_EARLY_WINDOW); //15); + writer.putInt(late); + writer.putInt(currentMineHour); + + writer.putInt(this.treeOfLife.getObjectType().ordinal()); + writer.putInt(this.treeOfLife.getObjectUUID()); + + writer.putInt((int)secondsLeft.getSeconds()); + } + + public int getBuildingUUID() { + return buildingUUID; + } + + public void setBuildingUUID(int buildingUUID) { + this.buildingUUID = buildingUUID; + } +} diff --git a/src/engine/net/client/msg/ArcMineWindowChangeMsg.java b/src/engine/net/client/msg/ArcMineWindowChangeMsg.java new file mode 100644 index 00000000..a6da69fe --- /dev/null +++ b/src/engine/net/client/msg/ArcMineWindowChangeMsg.java @@ -0,0 +1,70 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class ArcMineWindowChangeMsg extends ClientNetMsg { + + private int time; + private int buildingType; + private int buildingID; + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ArcMineWindowChangeMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ARCMINEWINDOWCHANGE, origin, reader); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.time = reader.getInt(); + this.buildingType = reader.getInt(); + this.buildingID = reader.getInt(); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + writer.putInt(this.time); + writer.putInt(this.buildingType); + writer.putInt(this.buildingID); + } + + public int getTime() { + return this.time; + } + + public int getBuildingID() { + return this.buildingID; + } + + public void setTime(int value) { + this.time = value; + } + + public void setBuildingID(int value) { + this.buildingID = value; + } +} diff --git a/src/engine/net/client/msg/ArcOwnedMinesListMsg.java b/src/engine/net/client/msg/ArcOwnedMinesListMsg.java new file mode 100644 index 00000000..a78b9cd1 --- /dev/null +++ b/src/engine/net/client/msg/ArcOwnedMinesListMsg.java @@ -0,0 +1,59 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Mine; + +import java.util.ArrayList; + +public class ArcOwnedMinesListMsg extends ClientNetMsg { + + ArrayList mineList = new ArrayList<>(); + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ArcOwnedMinesListMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ARCOWNEDMINESLIST, origin, reader); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + writer.putInt(mineList.size()); + for (Mine mine : mineList) { + mine.serializeForMineProduction(writer); + } + } + + public void setMineList(ArrayList mineList) { + this.mineList = mineList; + } + +} diff --git a/src/engine/net/client/msg/ArcSiegeSpireMsg.java b/src/engine/net/client/msg/ArcSiegeSpireMsg.java new file mode 100644 index 00000000..817e038c --- /dev/null +++ b/src/engine/net/client/msg/ArcSiegeSpireMsg.java @@ -0,0 +1,60 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class ArcSiegeSpireMsg extends ClientNetMsg { + + private int buildingUUID; + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ArcSiegeSpireMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ARCSIEGESPIRE, origin, reader); + } + + public ArcSiegeSpireMsg() { + super(Protocol.ARCSIEGESPIRE); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); // object type padding + this.buildingUUID = reader.getInt(); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + + } + + public int getBuildingUUID() { + return buildingUUID; + } + +} diff --git a/src/engine/net/client/msg/ArcViewAssetTransactionsMsg.java b/src/engine/net/client/msg/ArcViewAssetTransactionsMsg.java new file mode 100644 index 00000000..df827a11 --- /dev/null +++ b/src/engine/net/client/msg/ArcViewAssetTransactionsMsg.java @@ -0,0 +1,150 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.Enum.TransactionType; +import engine.exception.SerializationException; +import engine.gameManager.BuildingManager; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.*; + +import java.util.ArrayList; + +public class ArcViewAssetTransactionsMsg extends ClientNetMsg { + + private int warehouseID; + private Warehouse warehouse; + private int transactionID; + private ArrayList transactions; + Building warehouseBuilding; + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ArcViewAssetTransactionsMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ARCVIEWASSETTRANSACTIONS, origin, reader); + } + + public ArcViewAssetTransactionsMsg(Warehouse warehouse, ArcViewAssetTransactionsMsg msg) { + super(Protocol.ARCVIEWASSETTRANSACTIONS); + this.warehouseID = msg.warehouseID; + this.transactionID = msg.transactionID; + this.warehouse = warehouse; + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.transactionID = reader.getInt(); //some odd type? + this.warehouseID = reader.getInt(); + reader.getInt(); + } + + // Method pre-caches and configures values so they are + // available before we attempt serialization + + public void configure() { + + warehouseBuilding = BuildingManager.getBuilding(this.warehouse.getBuildingUID()); + transactions = new ArrayList<>(50); + + if (this.warehouse.getTransactions().size() > 150){ + transactions.addAll(this.warehouse.getTransactions().subList(this.warehouse.getTransactions().size() - 150, this.warehouse.getTransactions().size())); + }else + transactions = this.warehouse.getTransactions(); + + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + + writer.putInt(this.transactionID); + writer.putInt(this.warehouse.getBuildingUID()); + writer.putInt(transactions.size()); //list Size + + for (Transaction transaction:transactions){ + String name = "No Name"; + switch (transaction.getTargetType()){ + case Building: + Building building = BuildingManager.getBuildingFromCache(transaction.getTargetUUID()); + if (building != null) + name = building.getName(); + Mine mine = Mine.getMineFromTower(transaction.getTargetUUID()); + // + if (mine != null) + name = mine.getZoneName(); + + if (transaction.getTransactionType().equals(TransactionType.TAXRESOURCE) || transaction.getTransactionType().equals(TransactionType.TAXRESOURCEDEPOSIT)){ + City city = building.getCity(); + + if (city != null) + name = city.getCityName(); + } + + break; + case PlayerCharacter: + PlayerCharacter pc = PlayerCharacter.getPlayerCharacter(transaction.getTargetUUID()); + if (pc != null) + name = pc.getCombinedName(); + break; + case NPC: + NPC npc = NPC.getFromCache(transaction.getTargetUUID()); + if (npc != null){ + + if (npc.getBuilding() != null) + name = npc.getBuilding().getName(); + else + name = npc.getName(); + } + + + default: + break; + } + writer.putInt(transaction.getTargetType().ordinal()); //Type + writer.putInt(transaction.getTargetUUID()); //ID + writer.putString(name); //Name of depositer/withdrawler or mine name + writer.putInt(GameObjectType.Building.ordinal()); //Type + writer.putInt(warehouse.getBuildingUID()); //ID + writer.putString(warehouseBuilding.getName()); //warehouse + writer.putInt(transaction.getTransactionType().getID()); //79,80 withdrew, 81 mine produced, 82 deposit + writer.putInt(transaction.getAmount()); //amount + writer.putString(transaction.getResource().name().toLowerCase()); //item type + writer.putDateTime(transaction.getDate()); + } + + + //writer.putString("balls"); + } + + @Override + protected int getPowerOfTwoBufferSize() { + return (16); // 2^14 == 16384 + } + + public int getWarehouseID() { + return warehouseID; + } + public int getTransactionID() { + return transactionID; + } +} diff --git a/src/engine/net/client/msg/AssetSupportMsg.java b/src/engine/net/client/msg/AssetSupportMsg.java new file mode 100644 index 00000000..bf0864ab --- /dev/null +++ b/src/engine/net/client/msg/AssetSupportMsg.java @@ -0,0 +1,334 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.Enum; +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import org.pmw.tinylog.Logger; + + +public class AssetSupportMsg extends ClientNetMsg { + + private int npcType; + private int npcID; + private int buildingType; + private int buildingID; + private int messageType; + private int pad = 0; + private int objectType; + private int objectUUID; + private int protectedBuildingType; + private int protectedBuildingID; + private int profitTax; + private int weeklyTax; + private byte enforceKOS; + private Enum.SupportMsgType supportMsgType; + public static int confirmProtect; + + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public AssetSupportMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ASSETSUPPORT, origin, reader); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + + this.messageType = reader.getInt(); + this.supportMsgType = Enum.SupportMsgType.typeLookup.get(this.messageType); + + if (this.supportMsgType == null) { + this.supportMsgType = Enum.SupportMsgType.NONE; + Logger.error("No enumeration for support type" + this.messageType); + } + + switch (supportMsgType) { + + case PROTECT: + this.buildingType = reader.getInt(); + this.buildingID = reader.getInt(); + this.npcType = reader.getInt(); + this.npcID = reader.getInt(); + reader.getInt(); + reader.getInt(); + this.protectedBuildingType = reader.getInt(); + this.protectedBuildingID = reader.getInt(); + reader.getInt(); + this.weeklyTax = reader.getInt(); + this.profitTax = reader.getInt(); + this.enforceKOS = reader.get(); + reader.get(); + reader.getInt(); + reader.getInt(); + break; + + case UNPROTECT: + this.buildingType = reader.getInt(); + this.buildingID = reader.getInt(); + this.npcType = reader.getInt(); + this.npcID = reader.getInt(); + this.protectedBuildingType = reader.getInt(); + this.protectedBuildingID = reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + break; + case VIEWUNPROTECTED: + this.buildingType = reader.getInt(); + this.buildingID = reader.getInt(); + this.npcType = reader.getInt(); + this.npcID = reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + break; + case REMOVETAX: + reader.getInt(); + this.buildingID = reader.getInt(); + + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.get(); + reader.get(); + reader.get(); + reader.getInt(); + reader.getInt(); + break; + + case ACCEPTTAX: + reader.getInt(); + this.buildingID = reader.getInt(); + + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + + reader.get(); + reader.get(); + reader.get(); + break; + } + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + + writer.putInt(this.messageType); + this.supportMsgType = Enum.SupportMsgType.typeLookup.get(this.messageType); + + if (this.supportMsgType == null) { + this.supportMsgType = Enum.SupportMsgType.NONE; + Logger.error("No enumeration for support type" + this.messageType); + } + + switch (this.supportMsgType) { + + case PROTECT: + writer.putInt(this.buildingType); + writer.putInt(this.buildingID); + writer.putInt(npcType); + writer.putInt(npcID); + writer.putInt(0); + writer.putInt(0); + writer.putInt(this.protectedBuildingType); + writer.putInt(this.protectedBuildingID); + writer.putInt(0); + writer.putInt(this.weeklyTax); + writer.putInt(this.profitTax); + writer.put(this.enforceKOS); + writer.put((byte) 0); + writer.putInt(0); + writer.putInt(0); + break; + case CONFIRMPROTECT: + writer.putInt(this.buildingType); + writer.putInt(this.buildingID); + writer.putInt(this.npcType); + writer.putInt(this.npcID); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.put((byte)0); + writer.put((byte)0); + writer.put((byte)0); + + + break; + case UNPROTECT: + writer.putInt(this.buildingType); + writer.putInt(this.buildingID); + writer.putInt(npcType); + writer.putInt(npcID); + writer.putInt(this.protectedBuildingType); + writer.putInt(this.protectedBuildingID); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + break; + case VIEWUNPROTECTED: + writer.putInt(this.buildingType); + writer.putInt(this.buildingID); + writer.putInt(npcType); + writer.putInt(npcID); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + break; + } + } + + public int getObjectType() { + return objectType; + } + + public void setObjectType(int value) { + this.objectType = value; + } + + public void setPad(int value) { + this.pad = value; + } + + public int getUUID() { + return objectUUID; + } + + public int getPad() { + return pad; + } + + + public int getMessageType() { + return messageType; + } + + public void setMessageType(int messageType) { + this.messageType = messageType; + } + + + public int getNpcType() { + return npcType; + } + + public void setNpcType(int npcType) { + this.npcType = npcType; + } + + public int getNpcID() { + return npcID; + } + + public void setNpcID(int npcID) { + this.npcID = npcID; + } + + public int getBuildingType() { + return buildingType; + } + + public void setBuildingType(int buildingType) { + this.buildingType = buildingType; + } + + public int getBuildingID() { + return buildingID; + } + + public void setBuildingID(int buildingID) { + this.buildingID = buildingID; + } + + public int getProtectedBuildingType() { + return protectedBuildingType; + } + + public void setProtectedBuildingType(int protectedBuildingType) { + this.protectedBuildingType = protectedBuildingType; + } + + public int getProtectedBuildingID() { + return protectedBuildingID; + } + + public void setProtectedBuildingID(int protectedBuildingID) { + this.protectedBuildingID = protectedBuildingID; + } + + public int getWeeklyTax() { + return weeklyTax; + } + + public void setWeeklyTax(int weeklyTax) { + this.weeklyTax = weeklyTax; + } + + public int getProfitTax() { + return profitTax; + } + + public void setProfitTax(int profitTax) { + this.profitTax = profitTax; + } + + public byte getEnforceKOS() { + return enforceKOS; + } + + public void setEnforceKOS(byte enforceKOS) { + this.enforceKOS = enforceKOS; + } +} diff --git a/src/engine/net/client/msg/AttackCmdMsg.java b/src/engine/net/client/msg/AttackCmdMsg.java new file mode 100644 index 00000000..ed5b398c --- /dev/null +++ b/src/engine/net/client/msg/AttackCmdMsg.java @@ -0,0 +1,113 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class AttackCmdMsg extends ClientNetMsg { + + private int sourceType; + private int sourceID; + private int targetType; + private int targetID; + + /** + * This is the general purpose constructor. + */ + public AttackCmdMsg(int sourceType, int sourceID, int targetType, int targetID) { + super(Protocol.REQUESTMELEEATTACK); + this.sourceType = sourceType; + this.sourceID = sourceID; + this.targetType = targetType; + this.targetID = targetID; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public AttackCmdMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.REQUESTMELEEATTACK, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.putInt(this.targetType); + writer.putInt(this.targetID); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + } + + /** + * @return the sourceType + */ + public int getSourceType() { + return sourceType; + } + + /** + * @return the sourceID + */ + public int getSourceID() { + return sourceID; + } + + /** + * @return the targetType + */ + public int getTargetType() { + return targetType; + } + + /** + * @return the targetID + */ + public int getTargetID() { + return targetID; + } + + public void setSourceType(int value) { + this.sourceType = value; + } + + public void setSourceID(int value) { + this.sourceID = value; + } + + public void setTargetType(int value) { + this.targetType = value; + } + + public void setTargetID(int value) { + this.targetID = value; + } + +} diff --git a/src/engine/net/client/msg/BuyFromNPCMsg.java b/src/engine/net/client/msg/BuyFromNPCMsg.java new file mode 100644 index 00000000..de63bd55 --- /dev/null +++ b/src/engine/net/client/msg/BuyFromNPCMsg.java @@ -0,0 +1,139 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Item; + +public class BuyFromNPCMsg extends ClientNetMsg { + + private int npcType; + private int npcID; + private int itemType; + private int itemID; + private byte unknown01; + private Item item; + + /** + * This is the general purpose constructor + */ + public BuyFromNPCMsg() { + super(Protocol.BUYFROMNPC); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public BuyFromNPCMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.BUYFROMNPC, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + this.npcType = reader.getInt(); + this.npcID = reader.getInt(); + this.itemType = reader.getInt(); + this.itemID = reader.getInt(); + this.unknown01 = reader.get(); + reader.get(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + + writer.putInt(this.npcType); + writer.putInt(this.npcID); + + if (this.item != null){ + writer.putInt(this.item.getObjectType().ordinal()); + writer.putInt(this.item.getObjectUUID()); + }else{ + writer.putInt(this.itemType); + writer.putInt(this.itemID); + } + + writer.put(this.unknown01); + if (item != null) { + writer.put((byte) 1); + Item.serializeForClientMsgWithoutSlot(item,writer); + } else { + writer.put((byte) 0); + } + } + + @Override + protected int getPowerOfTwoBufferSize() { + return (16); // 2^14 == 16384 + } + public int getNPCType() { + return this.npcType; + } + + public int getNPCID() { + return this.npcID; + } + + public int getItemType() { + return this.itemType; + } + + public int getItemID() { + return this.itemID; + } + + public byte getUnknown01() { + return this.unknown01; + } + + public void setNPCType(int value) { + this.npcType = value; + } + + public void setNPCID(int value) { + this.npcID = value; + } + + public void setItemType(int value) { + this.itemType = value; + } + + public void setItemID(int value) { + this.itemID = value; + } + + public void setUnknown01(byte value) { + this.unknown01 = value; + } + + public Item getItem() { + return item; + } + + public void setItem(Item item) { + this.item = item; + } +} diff --git a/src/engine/net/client/msg/BuyFromNPCWindowMsg.java b/src/engine/net/client/msg/BuyFromNPCWindowMsg.java new file mode 100644 index 00000000..b54d6919 --- /dev/null +++ b/src/engine/net/client/msg/BuyFromNPCWindowMsg.java @@ -0,0 +1,269 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.ItemType; +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.AbstractNetMsg; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.ClientConnection; +import engine.net.client.Protocol; +import engine.objects.*; + +import java.util.ArrayList; + +public class BuyFromNPCWindowMsg extends ClientNetMsg { + + private int unknown01; + private int npcType; + private int npcID; + private float unknown02; + private byte unknown03; + private int unknown04; + + /** + * This is the general purpose constructor + */ + public BuyFromNPCWindowMsg(int unknown01, int npcType, int npcID, + float unknown02, byte unknown03, int unknown04) { + super(Protocol.SHOPLIST); + this.unknown01 = unknown01; + this.npcType = npcType; + this.npcID = npcID; + this.unknown02 = unknown02; + this.unknown03 = unknown03; + this.unknown04 = unknown04; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public BuyFromNPCWindowMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.SHOPLIST, origin, reader); + } + + /** + * @see AbstractNetMsg#getPowerOfTwoBufferSize() + */ + @Override + protected int getPowerOfTwoBufferSize() { + // Larger size for historically larger opcodes + return (16); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + unknown01 = reader.getInt(); + npcType = reader.getInt(); + npcID = reader.getInt(); + unknown02 = reader.getFloat(); + unknown03 = reader.get(); + unknown04 = reader.getInt(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + + ClientConnection clientConnection = (ClientConnection) this.getOrigin(); + PlayerCharacter player = null; + + if (clientConnection != null) + player = clientConnection.getPlayerCharacter(); + + float sellPercent = 1; + + NPC npc = NPC.getFromCache(npcID); + CharacterItemManager man = null; + ArrayList inventory = null; + ArrayList sellInventory = null; + + if (npc != null) { + man = npc.getCharItemManager(); + Contract contract = npc.getContract(); + if (player != null){ + float barget = player.getBargain(); + float profit = npc.getSellPercent(player) - barget; + + if (profit < 1) + profit = 1; + + sellPercent = 1 * profit; + } + + else sellPercent = 1 * npc.getSellPercent(); + + if (contract != null) + sellInventory = contract.getSellInventory(); + } + + if (man != null) + inventory = man.getInventory(); + + writer.putInt(unknown01); + writer.putInt(npcType); + writer.putInt(npcID); + + writer.putFloat(sellPercent); //npc sell markup + + int size = 0; + + if (inventory != null) + size += inventory.size(); + + if (sellInventory != null) + size += sellInventory.size(); + + if (size == 0) { + writer.put((byte) 0); + writer.putInt(0); + return; + } + + writer.put((byte) 1); + + int ownerID = npc.getObjectUUID(); + int indexPosition = writer.position(); + writer.putInt(0); //placeholder for item cnt + int total = 0; + + //add generic sell inventory from contract + if (sellInventory != null) { + + for (MobEquipment mobEquipment : sellInventory) { + try { + MobEquipment.serializeForVendor(mobEquipment,writer, sellPercent); + } catch (SerializationException se) { + continue; + } + ++total; + } + } + + //add npc inventory + if (inventory != null) { + for (Item item : inventory) { + if (item.getOwnerID() != ownerID) + continue; + if (item.getItemBase().getType().equals(ItemType.GOLD)) { + if (item.getNumOfItems() == 0) + continue; + } + Item.serializeForClientMsgForVendorWithoutSlot(item,writer, sellPercent); + ++total; + } + } + writer.putIntAt(total, indexPosition); + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the unknown02 + */ + public float getUnknown02() { + return unknown02; + } + + /** + * @param unknown02 + * the unknown02 to set + */ + public void setUnknown02(float unknown02) { + this.unknown02 = unknown02; + } + + /** + * @return the unknown03 + */ + public byte getUnknown03() { + return unknown03; + } + + /** + * @param unknown03 + * the unknown03 to set + */ + public void setUnknown03(byte unknown03) { + this.unknown03 = unknown03; + } + + /** + * @return the unknown04 + */ + public int getUnknown04() { + return unknown04; + } + + /** + * @param unknown04 + * the unknown04 to set + */ + public void setUnknown04(int unknown04) { + this.unknown04 = unknown04; + } + + /** + * @return the npcType + */ + public int getNpcType() { + return npcType; + } + + /** + * @param npcType + * the npcType to set + */ + public void setNpcType(int npcType) { + this.npcType = npcType; + } + + /** + * @return the npcID + */ + public int getNpcID() { + return npcID; + } + + /** + * @param npcID + * the npcID to set + */ + public void setNpcID(int npcID) { + this.npcID = npcID; + } + +} diff --git a/src/engine/net/client/msg/ChangeAltitudeMsg.java b/src/engine/net/client/msg/ChangeAltitudeMsg.java new file mode 100644 index 00000000..bb527b15 --- /dev/null +++ b/src/engine/net/client/msg/ChangeAltitudeMsg.java @@ -0,0 +1,146 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.PlayerCharacter; + +public class ChangeAltitudeMsg extends ClientNetMsg { + + private int sourceType; + private int sourceID; + private boolean up; + private float startAlt; + private float targetAlt; + private float amountToMove; + private byte unknown01 = (byte) 0; + + /** + * This is the general purpose constructor. + */ + public ChangeAltitudeMsg() { + super(Protocol.CHANGEALTITUDE); + } + + public ChangeAltitudeMsg(int sourceType, int sourceID, boolean up, float startAlt, float targetAlt, float amountToMove) { + super(Protocol.CHANGEALTITUDE); + this.sourceType = sourceType; + this.sourceID = sourceID; + this.startAlt = startAlt; + this.targetAlt = targetAlt; + this.amountToMove = amountToMove; + this.up = up; + } + + public static ChangeAltitudeMsg GroundPlayerMsg(PlayerCharacter pc){ + + ChangeAltitudeMsg msg = new ChangeAltitudeMsg(pc.getObjectType().ordinal(),pc.getObjectUUID(),false,pc.getAltitude(),0,pc.getAltitude()); + return msg; + + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ChangeAltitudeMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CHANGEALTITUDE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.put((this.up ? (byte) 1 : (byte) 0)); + writer.putFloat(this.startAlt); + writer.putFloat(this.targetAlt); + writer.putFloat(this.amountToMove); + writer.put((byte)0); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.up = (reader.get() == (byte) 1); + this.startAlt = reader.getFloat(); + this.targetAlt = reader.getFloat(); + this.amountToMove = reader.getFloat(); + this.unknown01 = reader.get(); + } + + public int getSourceType() { + return this.sourceType; + } + + public int getSourceID() { + return this.sourceID; + } + + public boolean up() { + return this.up; + } + + public float getStartAlt() { + return this.startAlt; + } + + public float getTargetAlt() { + return this.targetAlt; + } + + public float getAmountToMove() { + return this.amountToMove; + } + + public byte getUnknown01() { + return this.unknown01; + } + + public void setSourceType(int value) { + this.sourceType = value; + } + + public void setSourceID(int value) { + this.sourceID = value; + } + + public void setUp(boolean value) { + this.up = value; + } + + public void setStartAlt(float value) { + this.startAlt = value; + } + + public void setTargetAlt(float value) { + this.targetAlt = value; + } + + public void setAmountToMove(float value) { + this.amountToMove = value; + } + + public void setUnknown01(byte value) { + this.unknown01 = value; + } +} diff --git a/src/engine/net/client/msg/ChangeGuildLeaderMsg.java b/src/engine/net/client/msg/ChangeGuildLeaderMsg.java new file mode 100644 index 00000000..3491b9d6 --- /dev/null +++ b/src/engine/net/client/msg/ChangeGuildLeaderMsg.java @@ -0,0 +1,72 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class ChangeGuildLeaderMsg extends ClientNetMsg { + + private int targetID; + + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ChangeGuildLeaderMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CHANGEGUILDLEADER, origin, reader); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + targetID = reader.getInt(); + reader.getInt(); + reader.get(); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(GameObjectType.PlayerCharacter.ordinal()); + writer.putInt(targetID); + writer.put((byte)100); + writer.putInt(0); + + + } + + public int getTargetID() { + return targetID; + } + + public void setTargetID(int targetID) { + this.targetID = targetID; + } +} diff --git a/src/engine/net/client/msg/ChatFilterMsg.java b/src/engine/net/client/msg/ChatFilterMsg.java new file mode 100644 index 00000000..593fb262 --- /dev/null +++ b/src/engine/net/client/msg/ChatFilterMsg.java @@ -0,0 +1,73 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class ChatFilterMsg extends ClientNetMsg { + + private int channel; + private byte mute; + + /** + * This is the general purpose constructor. + * + * @param channel + */ + public ChatFilterMsg(int channel) { + super(Protocol.CHANNELMUTE); + this.channel = channel; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ChatFilterMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CHANNELMUTE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.channel); + writer.put(this.mute); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.channel = reader.getInt(); + this.mute = reader.get(); + } + + public int getChannel() { + return this.channel; + } + + public byte getMute() { + return mute; + } + + public void setMute(byte mute) { + this.mute = mute; + } +} diff --git a/src/engine/net/client/msg/CityAssetMsg.java b/src/engine/net/client/msg/CityAssetMsg.java new file mode 100644 index 00000000..515794a1 --- /dev/null +++ b/src/engine/net/client/msg/CityAssetMsg.java @@ -0,0 +1,219 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.Enum.ProtectionState; +import engine.exception.SerializationException; +import engine.gameManager.BuildingManager; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Building; +import engine.objects.City; +import engine.objects.Zone; +import org.pmw.tinylog.Logger; + +import java.util.HashSet; +import java.util.Set; + +public class CityAssetMsg extends ClientNetMsg { + + Set allCityAssets; + Set canProtectAssets; + private int type; + private int buildingID; + private int size; + private int pad = 0; + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public CityAssetMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CITYASSET, origin, reader); + } + + public CityAssetMsg() { + super(Protocol.CITYASSET); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.type = reader.getInt(); + reader.getInt(); + this.buildingID = reader.getInt(); + reader.getInt(); + + } + + @Override + protected int getPowerOfTwoBufferSize() { + // Larger size for historically larger opcodes + return (12); + } + + + // Precache and configure this message before we serialize it + + public void configure() { + + Building tol; + Zone zone; + City city; + + canProtectAssets = new HashSet<>(); + + tol = BuildingManager.getBuildingFromCache(this.buildingID); + + if (tol == null) { + Logger.debug("TOL is null"); + return; + } + + zone = tol.getParentZone(); + + if (zone == null) { + Logger.debug("Zone is null"); + return; + } + + city = City.getCity(zone.getPlayerCityUUID()); + + if (city == null) { + Logger.debug( "Failed to load city data for Tree of life."); + return; + } + + allCityAssets = zone.zoneBuildingSet; + + for (Building building : allCityAssets) { + + if (building.getBlueprint() == null) + continue; + + // Protected assets do not show up on list + + if (building.assetIsProtected() == true) + continue; + + if (building.getProtectionState() == ProtectionState.PENDING) + continue; + + // Shouldn't need this but just in case + + if (building.getBlueprint().isWallPiece()) + continue; + + if (building.getBlueprint().getBuildingGroup().equals(Enum.BuildingGroup.TOL)) + continue; + + if (building.getBlueprint().getBuildingGroup().equals(Enum.BuildingGroup.BANESTONE)) + continue; + + if (building.getBlueprint().getBuildingGroup().equals(Enum.BuildingGroup.SHRINE)) + continue; + + if (!city.isLocationOnCityGrid(building.getLoc())) + continue; + + canProtectAssets.add(building); + + } + + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + + String buildingName; + + writer.putInt(0); + writer.putInt(0); + + writer.putInt(GameObjectType.Building.ordinal()); + writer.putInt(this.buildingID); + + writer.putInt(canProtectAssets.size()); + + for (Building cityBuilding : canProtectAssets) { + + buildingName = cityBuilding.getName(); + + if (buildingName.isEmpty() && cityBuilding.getBlueprint() != null) { + buildingName = cityBuilding.getBlueprint().getName(); + } + + writer.putInt(cityBuilding.getObjectType().ordinal()); + writer.putInt(cityBuilding.getObjectUUID()); + + writer.putString(buildingName); + writer.putString(cityBuilding.getGuild().getName()); + writer.putInt(20);// \/ Temp \/ + writer.putInt(cityBuilding.getRank()); + + if (cityBuilding.getBlueprint() != null) { + writer.putInt(cityBuilding.getBlueprint().getIcon()); + } + else { + writer.putInt(0); + } + writer.putInt(7); //TODO identify these Guild tags?? + writer.putInt(17); + writer.putInt(14); + writer.putInt(14); + writer.putInt(98);// /\ Temp /\ + + } + } + + public int getPad() { + return pad; + } + + public void setPad(int value) { + this.pad = value; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public int getSize() { + return size; + } + + public void setSize(int size) { + this.size = size; + } + + public int getBuildingID() { + return buildingID; + } + + public void setBuildingID(int buildingID) { + this.buildingID = buildingID; + } + +} diff --git a/src/engine/net/client/msg/CityChoiceMsg.java b/src/engine/net/client/msg/CityChoiceMsg.java new file mode 100644 index 00000000..5c43439c --- /dev/null +++ b/src/engine/net/client/msg/CityChoiceMsg.java @@ -0,0 +1,107 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.AbstractNetMsg; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.PlayerCharacter; + + +public class CityChoiceMsg extends ClientNetMsg { + + private PlayerCharacter pc; + private boolean isTeleport; + private int msgType; + private int cityID; + + /** + * This is the general purpose constructor. + */ + public CityChoiceMsg(PlayerCharacter pc, boolean isTeleport) { + super(Protocol.CITYCHOICE); + this.pc = pc; + this.isTeleport = isTeleport; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public CityChoiceMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CITYCHOICE, origin, reader); + } + + /** + * Copy constructor + */ + public CityChoiceMsg(CityChoiceMsg msg) { + super(Protocol.CITYCHOICE); + this.pc = msg.pc; + this.isTeleport = msg.isTeleport; + } + + /** + * @see AbstractNetMsg#getPowerOfTwoBufferSize() + */ + @Override + protected int getPowerOfTwoBufferSize() { + // Larger size for historically larger opcodes + return (12); + } + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + //Do we even want to try this? + this.msgType = reader.getInt(); + reader.getInt(); + this.cityID = reader.getInt(); + reader.getInt(); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + } + + public PlayerCharacter pc() { + return this.pc; + } + + public boolean isTeleport() { + return this.isTeleport; + } + + public void setPC(PlayerCharacter pc) { + this.pc = pc; + } + + public void setIsTeleport(boolean value) { + this.isTeleport = value; + } + + public int getMsgType() { + return msgType; + } + + public int getCityID() { + return cityID; + } +} diff --git a/src/engine/net/client/msg/CityZoneMsg.java b/src/engine/net/client/msg/CityZoneMsg.java new file mode 100644 index 00000000..41ea295e --- /dev/null +++ b/src/engine/net/client/msg/CityZoneMsg.java @@ -0,0 +1,156 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Zone; + +public class CityZoneMsg extends ClientNetMsg { + + private int messageType; //1 or 2 + private int zoneType; + private int zoneID; + private float locX; + private float locY; + private float locZ; + private String name; + private float radiusX; + private float radiusZ; + private int unknown01 = 0; + + /** + * This is the general purpose constructor. + */ + + public CityZoneMsg(int messageType, float locX, float locY, float locZ, String name, Zone zone, float radiusX, float radiusZ) { + super(Protocol.CITYZONE); + this.messageType = messageType; //only 1 or 2 used on message type + this.zoneType = GameObjectType.Zone.ordinal(); + this.zoneID = zone.getObjectUUID(); + this.locX = locX; + this.locY = locY; + this.locZ = locZ; + this.name = name; + this.radiusX = radiusX; + this.radiusZ = radiusZ; + this.unknown01 = 0; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public CityZoneMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.CITYZONE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + + writer.putInt(this.messageType); + + + //MSGTYPE 3 SERIALIZATION + if (this.messageType == 3){ + writer.putInt(1); + writer.putInt(this.zoneType); + writer.putInt(this.zoneID); + writer.putString("RuinedCity"); + writer.putString("Ruined"); + writer.putFloat(this.locX); + writer.putFloat(this.locY); + writer.putFloat(this.locZ); + writer.putInt(0); + return; + } + + + + //END SERIALIZIONTYPE 3 + + // writer.putInt(this.messageType); + if (this.messageType == 1){ + writer.putInt(this.zoneType); + writer.putInt(this.zoneID); + } + + writer.putFloat(this.locX); + writer.putFloat(this.locY); + writer.putFloat(this.locZ); + writer.putString(this.name); + if (this.messageType == 1) { + writer.putFloat(this.radiusX); + writer.putFloat(this.radiusZ); + } + writer.putInt(this.unknown01); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + // this.locX = reader.getFloat(); + // this.locY = reader.getFloat(); + // this.locZ = reader.getFloat(); + // this.name = reader.getString(); + // this.unknown01 = reader.getInt(); + } + + public float getLocX() { + return this.locX; + } + + public float getLocY() { + return this.locY; + } + + public float getLocZ() { + return this.locZ; + } + + public String getName() { + return this.name; + } + + public int getUnknown01() { + return this.unknown01; + } + + public void setLocX(float value) { + this.locX = value; + } + + public void setLocY(float value) { + this.locY = value; + } + + public void setLocZ(float value) { + this.locZ = value; + } + + public void setName(String value) { + this.name = value; + } + + public void setUnknown01(int value) { + this.unknown01 = value; + } + + +} diff --git a/src/engine/net/client/msg/ClaimAssetMsg.java b/src/engine/net/client/msg/ClaimAssetMsg.java new file mode 100644 index 00000000..16b77bfe --- /dev/null +++ b/src/engine/net/client/msg/ClaimAssetMsg.java @@ -0,0 +1,71 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class ClaimAssetMsg extends ClientNetMsg { + private int pad; + private int objectType; + private int objectUUID; + + public ClaimAssetMsg(int objectType, int objectUUID) { + super(Protocol.CLAIMASSET); + this.pad = 0; + this.objectType = objectType; + this.objectUUID = objectUUID; + } + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ClaimAssetMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CLAIMASSET, origin, reader); + } + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.pad = reader.getInt(); + this.objectType = reader.getInt(); + this.objectUUID = reader.getInt(); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + writer.putInt(this.pad); + writer.putInt(this.objectType); + writer.putInt(this.objectUUID); + } + + public int getObjectType() { + return objectType; + } + + public void setObjectType(int value) { + this.objectType = value; + } + + public int getUUID() { + return objectUUID; + } +} diff --git a/src/engine/net/client/msg/ClaimGuildTreeMsg.java b/src/engine/net/client/msg/ClaimGuildTreeMsg.java new file mode 100644 index 00000000..7661b6ef --- /dev/null +++ b/src/engine/net/client/msg/ClaimGuildTreeMsg.java @@ -0,0 +1,376 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +/** + * Open manage city asset window + * + * @author Eighty + */ +public class ClaimGuildTreeMsg extends ClientNetMsg { + + // 2 = manage this asset. 20 = manage entire city + + private int messageType; + + private int targetType; + private int targetID; + + private int charter; + private int bgc1; + private int bgc2; + private int symbolColor; + private int bgDesign; + private int symbol; + private int unknown07; + private int unknown08; + private int unknown09; + private int unknown10; + private int unknown11; + + private String CityName; + private String OwnerName; + private String GuildName; + private int unknown12; + private byte UnkByte01; + private int unknown13; + private int unknown14; + private int unknown15; + private int unknown16; + private int unknown17; + private int unknown18; + + private byte UnkByte02; + private byte UnkByte03; + private byte UnkByte04; + private byte UnkByte05; + + private int unknown19; //Arraylist motto length? + private String motto; //motto Length 60 max? + public static final int RENAME_TREE = 2; + public static final int OPEN_CITY = 4; + public static final int CLOSE_CITY = 5; + private String treeName; + +// private int unknown01; + + + + + + + + + + /** + * This is the general purpose constructor + */ + public ClaimGuildTreeMsg() { + super(Protocol.CLAIMGUILDTREE); + this.messageType = 0; + this.targetType=0; + this.targetID = 0; + + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public ClaimGuildTreeMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.CLAIMGUILDTREE, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + this.messageType = reader.getInt(); + switch (this.messageType){ + case OPEN_CITY: + case CLOSE_CITY: + targetType = reader.getInt(); + targetID = reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + break; + case RENAME_TREE: + targetType = reader.getInt(); + targetID = reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + this.treeName = reader.getString(); + break; + default: + targetType = reader.getInt(); + targetID = reader.getInt(); + reader.getInt(); + reader.getInt(); + this.treeName = reader.getString(); + break; + } + + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer){ + writer.putInt(this.messageType); + writer.putInt(this.targetType); + writer.putInt(this.targetID); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + if (this.messageType == RENAME_TREE) + writer.putString(this.treeName); + } + + /** + * @return the charter + */ + public int getcharter() { + return charter; + } + + + + + public int getbgc1() { + return bgc1; + } + public int getbgc2() { + return bgc2; + } + public int getsymbolColor() { + return symbolColor; + } + public int getbgDesign() { + return bgDesign; + } + public int getsymbol() { + return symbol; + } + public int getUnknown07() { + return unknown07; + } + public int getUnknown08() { + return unknown08; + } + public int getUnknown09() { + return unknown09; + } + public int getUnknown10() { + return unknown10; + } + public int getUnknown11() { + return unknown11; + } + public int getUnknown12() { + return unknown12; + } + public int getUnknown13() { + return unknown13; + } + public int getUnknown14() { + return unknown14; + } + public int getUnknown15() { + return unknown15; + } + public int getUnknown16() { + return unknown16; + } + public int getUnknown17() { + return unknown17; + } + public int getUnknown18() { + return unknown18; + } + public int getUnknown19() { + return unknown19; + } + + + + + + + + + + public String getOwnerName() { + return OwnerName; + } + + public String getCityName() { + return CityName; + } + + public String getGuildName() { + return GuildName; + } + public void setcharter(int charter) { + this.charter = charter; + } + public void setbgc1 (int bgc1) { + this.bgc1 = bgc1; + } + public void setbgc2 (int bgc2) { + this.bgc2 = bgc2; + } + public void setsymbolColor (int symbolColor) { + this.symbolColor = symbolColor; + } + public void setbgDesign (int bgDesign) { + this.bgDesign = bgDesign; + } + public void setsymbol (int symbol) { + this.symbol = symbol; + } + public void setUnknown07 (int unknown07) { + this.unknown07 = unknown07; + } + public void setUnknown08 (int unknown08) { + this.unknown08 = unknown08; + } + public void setUnknown09 (int unknown09) { + this.unknown09 = unknown09; + } + public void setUnknown10 (int unknown10) { + this.unknown10 = unknown10; + } + public void setUnknown11 (int unknown11) { + this.unknown11 = unknown11; + } + public void setUnknown12 (int unknown12) { + this.unknown12 = unknown12; + } + public void setUnknown13 (int unknown13) { + this.unknown13 = unknown13; + } + public void setUnknown14 (int unknown14) { + this.unknown14 = unknown14; + } + public void setUnknown15 (int unknown15) { + this.unknown15 = unknown15; + } + public void setUnknown16 (int unknown16) { + this.unknown16 = unknown16; + } + public void setUnknown17 (int unknown17) { + this.unknown17 = unknown17; + } + public void setUnknown18 (int unknown18) { + this.unknown18 = unknown18; + } + public void setUnknown19 (int unknown19) { + this.unknown19 = unknown19; + } + + + + + public void setUnkByte01 (byte UnkByte01) { + this.UnkByte01 = UnkByte01; + } + public void setUnkByte02 (byte UnkByte02) { + this.UnkByte02 = UnkByte02; + } + public void setUnkByte03 (byte UnkByte03) { + this.UnkByte03 = UnkByte03; + } + public void setUnkByte04 (byte UnkByte04) { + this.UnkByte04 = UnkByte04; + } + + + public void setOwnerName(String OwnerName) { + this.OwnerName = OwnerName; + } + + public void setCityName(String CityName) { + this.CityName = CityName; + } + + public void setGuildName(String GuildName) { + this.GuildName = GuildName; + } + + + + public void setMotto(String motto) { + this.motto = motto; + } + + public String getMotto() { + return motto; + } + + + + public void setUnkByte05(byte unkByte05) { + UnkByte05 = unkByte05; + } + + public byte getUnkByte05() { + return UnkByte05; + } + + public void setMessageType(int value) { + this.messageType = value; + } + + public int getMessageType() { + return messageType; + } + + public int getTargetID() { + return targetID; + } + + public void setTargetID(int targetID) { + this.targetID = targetID; + } + + public int getTargetType() { + return targetType; + } + + public void setTargetType(int targetType) { + this.targetType = targetType; + } + + + public String getTreeName() { + return treeName; + } + +} + +//Debug Info +//Run: Failed to make object TEMPLATE:135700 INSTANCE:1717987027141... (t=50.46) (r=7/4/2011 11:56:39) +//C:\ArcanePrime\Main_Branch\Shadowbane\Source\ArcObjectLoader.cpp(466):ERROR: ArcObjectLoader::Run: Failed to make object TEMPLATE:108760 INSTANCE:1717987027161... (t=50.46) (r=7/4/2011 11:56:39) +//C:\ArcanePrime\Main_Branch\Shadowbane\Source\ArcObjectLoader.cpp(466):ERROR: ArcObjectLoader::Run: Failed to make object TEMPLATE:108760 INSTANCE:1717987027177... (t=50.67) (r=7/4/2011 11:56:39) +//C:\ArcanePrime\Main_Branch\Shadowbane\Source\ArcObjectLoader.cpp(466):ERROR: ArcObjectLoader::Run: Failed to make object TEMPLATE:60040 INSTANCE:1717987027344... (t=50.87) (r=7/4/2011 11:56:39) +//C:\ArcanePrime\Main_Branch\Shadowbane\Source\ArcObjectLoader.cpp(466):ERROR: ArcObjectLoader::Run: Failed to make object TEMPLATE:3 INSTANCE:1717987027164... (t=50.88) (r=7/4/2011 11:56:39) + diff --git a/src/engine/net/client/msg/ClientNetMsg.java b/src/engine/net/client/msg/ClientNetMsg.java new file mode 100644 index 00000000..22611e05 --- /dev/null +++ b/src/engine/net/client/msg/ClientNetMsg.java @@ -0,0 +1,73 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.AbstractNetMsg; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public abstract class ClientNetMsg extends AbstractNetMsg { + + /** + * This is the general purpose constructor. + */ + protected ClientNetMsg(Protocol protocolMsg) { + super(protocolMsg); + } + + protected ClientNetMsg(Protocol protocolMsg, ClientNetMsg msg) { + super(protocolMsg, msg); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + protected ClientNetMsg(Protocol protocolMsg, AbstractConnection origin, + ByteBufferReader reader) { + super(protocolMsg, origin, reader); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected abstract void _deserialize(ByteBufferReader reader) + ; + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected abstract void _serialize(ByteBufferWriter writer) + throws SerializationException; + + /* + * return the header size of 4 for ClientMsgs + */ + @Override + protected int getHeaderSize() { + return 4; + } + + /* + * Write in the header for ClientMsgs + */ + @Override + protected void writeHeaderAt(int startPos, ByteBufferWriter writer) { + writer.putIntAt(this.getProtocolMsg().opcode, startPos); + } +} diff --git a/src/engine/net/client/msg/CloseTradeWindowMsg.java b/src/engine/net/client/msg/CloseTradeWindowMsg.java new file mode 100644 index 00000000..d93d0f12 --- /dev/null +++ b/src/engine/net/client/msg/CloseTradeWindowMsg.java @@ -0,0 +1,88 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractGameObject; + +/** + * Commit to trade + * + * @author Eighty + */ +public class CloseTradeWindowMsg extends ClientNetMsg { + + private int playerType; + private int playerID; + private int unknown01; + + /** + * This is the general purpose constructor + */ + public CloseTradeWindowMsg(AbstractGameObject player, int unknown01) { + super(Protocol.TRADECLOSE); + this.playerType = player.getObjectType().ordinal(); + this.playerID = player.getObjectUUID(); + this.unknown01 = unknown01; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public CloseTradeWindowMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.TRADECLOSE, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + playerType = reader.getInt(); + playerID = reader.getInt(); + unknown01 = reader.getInt(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + writer.putInt(playerType); + writer.putInt(playerID); + + writer.putInt(unknown01); + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + + +} diff --git a/src/engine/net/client/msg/CommitToTradeMsg.java b/src/engine/net/client/msg/CommitToTradeMsg.java new file mode 100644 index 00000000..cb46281e --- /dev/null +++ b/src/engine/net/client/msg/CommitToTradeMsg.java @@ -0,0 +1,95 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +/** + * Commit to trade + * + * @author Eighty + */ +public class CommitToTradeMsg extends ClientNetMsg { + + private int unknown01; + private long playerCompID; + + /** + * This is the general purpose constructor + */ + public CommitToTradeMsg(int unknown01, long playerCompID) { + super(Protocol.TRADECONFIRM); + this.unknown01 = unknown01; + this.playerCompID = playerCompID; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public CommitToTradeMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.TRADECONFIRM, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + unknown01 = reader.getInt(); + playerCompID = reader.getLong(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + writer.putInt(unknown01); + writer.putLong(playerCompID); + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the playerCompID + */ + public long getPlayerCompID() { + return playerCompID; + } + + /** + * @param playerCompID the playerCompID to set + */ + public void setPlayerCompID(long playerCompID) { + this.playerCompID = playerCompID; + } + +} diff --git a/src/engine/net/client/msg/ConfirmPromoteMsg.java b/src/engine/net/client/msg/ConfirmPromoteMsg.java new file mode 100644 index 00000000..7532ff8c --- /dev/null +++ b/src/engine/net/client/msg/ConfirmPromoteMsg.java @@ -0,0 +1,55 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class ConfirmPromoteMsg extends ClientNetMsg { + + + + /** + * This is the general purpose constructor. + * + * @param channel + */ + public ConfirmPromoteMsg(int channel) { + super(Protocol.CONFIRMPROMOTE); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ConfirmPromoteMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CONFIRMPROMOTE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + } +} diff --git a/src/engine/net/client/msg/DeclineFriendMsg.java b/src/engine/net/client/msg/DeclineFriendMsg.java new file mode 100644 index 00000000..e0d2c6ce --- /dev/null +++ b/src/engine/net/client/msg/DeclineFriendMsg.java @@ -0,0 +1,65 @@ +/* +HashSet playerFriendSet = PlayerFriendsMap.get(playerUID); + playerFriendSet.add(friendUID); * Copyright 2013 MagicBane Emulator Project + * All Rights Reserved + */ +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.PlayerCharacter; + + +public class DeclineFriendMsg extends ClientNetMsg { + + public String sourceName; + public String friendName; + + /** + * This is the general purpose constructor. + */ + public DeclineFriendMsg(PlayerCharacter pc) { + super(Protocol.FRIENDDECLINE); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public DeclineFriendMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.FRIENDDECLINE, origin, reader); + } + + /** + * Copy constructor + */ + public DeclineFriendMsg(DeclineFriendMsg msg) { + super(Protocol.FRIENDDECLINE); + } + + + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + //Do we even want to try this? + this.sourceName = reader.getString(); //This is source name.. this friends list must never been updated since launch, not using IDS. + this.friendName = reader.getString(); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putString(this.sourceName); + writer.putString(this.friendName); + } +} diff --git a/src/engine/net/client/msg/DeleteItemMsg.java b/src/engine/net/client/msg/DeleteItemMsg.java new file mode 100644 index 00000000..ca273e19 --- /dev/null +++ b/src/engine/net/client/msg/DeleteItemMsg.java @@ -0,0 +1,79 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class DeleteItemMsg extends ClientNetMsg { + + private int type; + private int objectUUID; + private int unknown1, unknown2; + + public DeleteItemMsg(int type, int objectUUID) { + super(Protocol.DELETEOBJECT); + this.type = type; + this.objectUUID = objectUUID; + this.unknown1 = 0; + this.unknown2 = 0; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public DeleteItemMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.DELETEOBJECT, origin, reader); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.type = reader.getInt(); + this.objectUUID = reader.getInt(); + this.unknown1 = reader.getInt(); + this.unknown2 = reader.getInt(); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + writer.putInt(this.type); + writer.putInt(this.objectUUID); + writer.putInt(this.unknown1); + writer.putInt(this.unknown2); + } + + /** + * @return the type + */ + public int getType() { + return type; + } + + /** + * @return the objectUUID + */ + public int getUUID() { + return objectUUID; + } + +} diff --git a/src/engine/net/client/msg/DestroyBuildingMsg.java b/src/engine/net/client/msg/DestroyBuildingMsg.java new file mode 100644 index 00000000..844b2371 --- /dev/null +++ b/src/engine/net/client/msg/DestroyBuildingMsg.java @@ -0,0 +1,72 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class DestroyBuildingMsg extends ClientNetMsg { + private int pad = 0; + private int objectType; + private int objectUUID; + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public DestroyBuildingMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.DESTROYBUILDING, origin, reader); + } + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.pad = reader.getInt(); + this.objectType = reader.getInt(); + this.objectUUID = reader.getInt();; + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + writer.putInt(this.pad); + writer.putInt(this.objectType); + writer.putInt(this.objectUUID); + } + + public int getObjectType() { + return objectType; + } + + public void setObjectType(int value) { + this.objectType = value; + } + + public void setPad(int value) { + this.pad = value; + } + + public int getUUID() { + return objectUUID; + } + + public int getPad() { + return pad; + } +} diff --git a/src/engine/net/client/msg/DoorTryOpenMsg.java b/src/engine/net/client/msg/DoorTryOpenMsg.java new file mode 100644 index 00000000..6fb0fc36 --- /dev/null +++ b/src/engine/net/client/msg/DoorTryOpenMsg.java @@ -0,0 +1,158 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class DoorTryOpenMsg extends ClientNetMsg { + + private int unk1; + private int doorID; + private int buildingUUID; + private int playerUUID; + private byte toggle; + + /** + * This is the general purpose constructor. + */ + public DoorTryOpenMsg(int doorID, int targetID, int senderID, byte toggle) { + super(Protocol.DOORTRYOPEN); + this.unk1 = 0; + this.doorID = doorID; + this.buildingUUID = targetID; + this.playerUUID = senderID; + this.toggle = toggle; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public DoorTryOpenMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.DOORTRYOPEN, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + if (this.unk1 == 2) { + writer.putInt(2); + for (int i=0;i<6;i++) + writer.putInt(0); + writer.put((byte)0); + } else { + writer.putInt(0); //0 or 2. If 2 all other variables are 0 + writer.putInt(0); + writer.putInt(doorID); + writer.putInt(GameObjectType.Building.ordinal()); + writer.putInt(buildingUUID); + writer.putInt(GameObjectType.PlayerCharacter.ordinal()); + writer.putInt(playerUUID); + writer.put(toggle); + } + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.unk1 = reader.getInt(); + reader.getInt(); + this.doorID = reader.getInt(); + reader.getInt(); // type padding + this.buildingUUID = reader.getInt(); + reader.getInt(); // type padding + this.playerUUID = reader.getInt(); + this.toggle = reader.get(); + } + + /** + * @return the unknown1 + */ + public int getDoorID() { + return doorID; + } + + /** + * @return the buildingUUID + */ + public int getBuildingUUID() { + return buildingUUID; + } + + /** + * @return the playerUUID + */ + public int playerUUID() { + return playerUUID; + } + + /** + * @return the toggle + */ + public byte getToggle() { + return toggle; + } + + public int getUnk1() { + return this.unk1; + } + + public void setUnk1(int value) { + this.unk1 = value; + } +} diff --git a/src/engine/net/client/msg/DropGoldMsg.java b/src/engine/net/client/msg/DropGoldMsg.java new file mode 100644 index 00000000..f16b8c41 --- /dev/null +++ b/src/engine/net/client/msg/DropGoldMsg.java @@ -0,0 +1,146 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class DropGoldMsg extends ClientNetMsg { + + private int unknown01; + private int unknown02; + private int unknown03; + private short unknown04; + private byte unknown05; + + /** + * This is the general purpose constructor. + */ + public DropGoldMsg() { + super(Protocol.DROPGOLD); + this.unknown01 = 0x40A5BDB0; + this.unknown02 = 0x342AA9F0; + this.unknown03 = 0; + this.unknown04 = (short) 0; + this.unknown05 = (byte) 0; + + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public DropGoldMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.DROPGOLD, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + + reader.getInt(); + reader.get(); + + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the unknown02 + */ + public int getUnknown02() { + return unknown02; + } + + /** + * @param unknown02 + * the unknown02 to set + */ + public void setUnknown02(int unknown02) { + this.unknown02 = unknown02; + } + + /** + * @return the unknown03 + */ + public int getUnknown03() { + return unknown03; + } + + /** + * @param unknown03 + * the unknown03 to set + */ + public void setUnknown03(int unknown03) { + this.unknown03 = unknown03; + } + + /** + * @return the unknown04 + */ + public short getUnknown04() { + return unknown04; + } + + /** + * @param unknown04 + * the unknown04 to set + */ + public void setUnknown04(short unknown04) { + this.unknown04 = unknown04; + } + + /** + * @return the unknown05 + */ + public byte getUnknown05() { + return unknown05; + } + + /** + * @param unknown05 + * the unknown05 to set + */ + public void setUnknown05(byte unknown05) { + this.unknown05 = unknown05; + } + +} diff --git a/src/engine/net/client/msg/EnterWorldReceivedMsg.java b/src/engine/net/client/msg/EnterWorldReceivedMsg.java new file mode 100644 index 00000000..2845378c --- /dev/null +++ b/src/engine/net/client/msg/EnterWorldReceivedMsg.java @@ -0,0 +1,58 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class EnterWorldReceivedMsg extends ClientNetMsg { + + /** + * This is the general purpose constructor. + */ + public EnterWorldReceivedMsg() { + super(Protocol.READYTOENTER); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public EnterWorldReceivedMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.READYTOENTER, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + return; + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + return; + } + + @Override + protected int getPowerOfTwoBufferSize() { + return 18; //65536 bytes for enter world. + } +} diff --git a/src/engine/net/client/msg/ErrorPopupMsg.java b/src/engine/net/client/msg/ErrorPopupMsg.java new file mode 100644 index 00000000..f9bac8f8 --- /dev/null +++ b/src/engine/net/client/msg/ErrorPopupMsg.java @@ -0,0 +1,316 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum; +import engine.net.*; +import engine.net.client.Protocol; +import engine.objects.PlayerCharacter; + +public class ErrorPopupMsg extends ClientNetMsg { + + //1: Sorry, but that individual is not a banker + //2: Sorry, but you must be closer to the banker to access your account + //3: Sorry, but you have insufficient funds to access your acount + //4: The shop is closed + //5: You must come closer to shop + //6: You do not have enough money to purchase that + //7: You cannot carry that item + //8: The banker cannot carry that item + //9: You do not have that much gold to drop + //10: That item cannot be dropped + //11: You cannot drop what you do not have + //12: Sorry, but this container is locked + //13: Sorry, but this container is barred + //14: You must come closer to me + //15: You no longer have that item to sell + //16: I won't buy that kind of item + //17: I cannot afford that item + //18: You can't really afford that + //19: This item is gone from inventory + //20: Your resurection has been declined + //21: I cannot carry that weight + //22: You just dropped that item on the ground + //23: This corpse has no experience to return + //24: This player is not in world + //25: This player is not online + //26: You selected an invalid location + //27: You are dead. Try again when you are not so...well, dead + //28: Your target is dead and cannot be summoned + //29: Your summons has been declined + //30: That person cannot carry that item + //31: An unexpected error has occurred and the trade is being canceled + //32: You must choose a promotion class before gaining your next level. Speak to a class trainer + //33: Promotion failed + //34: Come back when you've gained more experience + //35: This hireling failed to buy the item + //36: I don't buy items + //37: I don't sell items + //38: You appear to be in a building normally + //39: There does not appear to be a building where you are + //40: I don't swear guilds + //41: I server no sovereign + //42: Your guild is not errant + //43: Members of your guild are too high in level + //44: You cannot afford this service + //45: Failure to swear guild + //46: Cannot swear under ruins + //47: Your guild is the wrong type to swear to this guild + //48: Hireling could not hire + //49: I do not hire + //50: This pet is gone + //51: You have successfully promoted to a new class + //52: You have successfully added a new discipline + //53: You no longer meet the level requirement to stay in this guild + //54: That item is too advanced + //55: All production slots are taken + //56: That enchantment is too advanced + //57: That formula is too advanced + //58: The formula is beyond the means of this facility + //59: This hireling does not have this formula + //60: Hireling does not possess that item! + //61: Hireling does not work with such items + //62: You may only trade with items in your inventory + //63: You must be within your building to do that + //64: You are too far from the building to do that + //65: I cannot enthrall creatures + //66: I cannot repledge you + //67: I cannot teleport you + //68: You have no valid thralls + //69: You have a thrall that I do not understand + //70: I cannot afford your thrall + //71: There are no cities to which you can repledge + //72: There are no cities to which you can teleport + //73: You are too low level to repledge + //74: You are too low level to teleport + //75: You must leave your current guild before you can repledge + //76: Failure to repledge + //77: Failure to teleport + //78: You do not meet the qualifications to join that city + //79: There are too many furniture items in this asset + //80: Attempting to add furniture to bad location + //81: This deed codes for a bad furniture prop + //82: Object is not an appropriate furniture deed + //83: Unable to find corresponding furniture on asset + //84: The chose game world is not valid <- will kick out of world + //85: The game world is temporarily unavailable <- will kick out of world + //86: The runegate is unnaffected by this power + //87: You are not powerful enough to activate this gate + //88: This runegate is already on + //89: You cannot unbanish this one until the timestamp expires + //90: You cannot trade while either you or the target is invisible + //91: You cannot trade while in combat mode + //92: That person is already engaged in a trade + //93: You must be closer to trade + //94: The trade was successful + //95: The trade was not successful + //96: The trade has failed because one of you would exceed your gold limit + //97: You must be closer to open that + //98: You cannot loot while flying + //99: Your target is not dead + //100: You cannot loot a trainer + //101: You cannot loot a shopkeeper + //102: You cannot loot a banker + //103: You cannot add this individual to the condemn list + //104: You cannot add the owner's guild to the condemn list + //105: You cannot add the owner's nation to the condemn list + //106: Failure to add to the condemn list + //107: Unable to find desired group + //108: Group is at maximum membership + //109: Failed to add item to hireling + //110: Failer to remove item from hireling + //111: This item cannot be removed from inventory + //112: Your account has no characters + //113: Failure to start support + //114: Cannot add another support + //115: This type of asset cannot receive protection + //116: This asset already has protection upon it + //117: Failure to remove support + //118: Failure to complete support + //119: Failure to reject support + //120: This asset is not a banecircle + //121: You are not a CSR who can advance banecircle stage + //122: Failure to advance banecircle stage + //123: You do not have the authority within your guild to modify this banecircle + //124: Banecircle cannot advance once in final stage + //125: Failure to repair Asset + //126: Asset does not require repair + //127: No gold in asset strongbox + //128: Insufficient funds for even one point of repair + //129: You cannot bond where you are killed-on-sight + //130: You cannot join where you are killed-on-sight + //131: You do not meet the level required for this SWORN guild + //132: You are already a member of this guild + //133: Your banishment from this guild has not yet been lifted + //134: Your QUIT status from this guild has not yet expired + //135: Character is considered BANISHED by guild leadership + //136: Your class is not allowed to teleport here + //137: You have no affiliation with this tree + //138: You can never join this type of tree + //139: You do not meet the safehold level requirement + //140: Ruined trees are invalid + //141: Unclaimed trees are invalid + //142: You are the wrong race for this city + //143: You are the wrong class for this city + //144: You are the wrong sex for this city + //145: You are too low level for this city + //146: You do not meet the level requirements for this city + //147: Tree must be rank 5 to open city + //148: Unable to find a matching petition to complete guild creation + //149: Guild name fails profanity check + //150: Guild motto fails profanity check + //151: Guild name is not unique + //152: Guild crest is not unique + //153: Guild crest is reserved + //154: All three crest colors cannot be the same + //155: Please choose another name + //156: You cannot bank and trade at the same time + //157: You must not move or engage in combat for 10 seconds before stuck will work + //158: Your gold has been dropped on the ground + //159: Merchant cannot purchase item without exceeding his reserve + //160: Rune succesfully applied + //161: You cannot apply that rune + //162: You rely too heavily on that rune to remove it + //163: This shrine does not take offerings of that type + //164: This hireling cannot grant boons + //165: This hireling cannot display the leaderboard + //166: There is no more favor in this shrine to loot + //167: There are no more resources in this warehouse to loot + //168: This boon is only for guild members belonging to this shrine + //169: You do not meet the race/class requirements for this boon + //170: This shrine is no longer capable of granting boons + //171: This asset cannot be destroyed during times of war + //172: This shrine has no favor + //173: You must be the leader of a guild to receive a blessing + //174: This siege spire cannot be toggled yet. Please try again later + //175: You cannot teleport into that zone at the moment + //176: Only guild leaders can claim a territory + //177: Your nation has already reached the maximum number of capitals + //178: This territory is already claimed + //179: Only landed guilds may claim a territory + //180: This territory cannot be ruled by anyone + //181: Your tree must be rank 7 before claiming a territory + //182: This realm is in turmoil and cannot be claimed yet + //183: You cannot rule a guild under a different faction then your parent guild + //184: Insufficient gold or resources to upgrade to capital + //185: You must seek the blessing of the three sages before you can rule + //186: Your tree is not inside a territory! + //187: This realm is in turmoil and cannot yet be claimed! + //188: You must have a warehouse to become a capital + //189: You are not the owner of this building + //190: This building cannot be upgraded further + //191: You don't have the required funds + //192: This building is already upgrading + //193: Production denied: This building must be protected to gain access to warehouse resources.. + //194: The operation failed because you reached your gold limit + //195: That player is currently busy completing the last trade. Try again in a few moments + //196: You are currently busy completing the last trade. Try again in a few moments + //197: You cannot join a guild whose nation has a guild that is currently involved in a siege + //198: You cannot repledge while you are involved in a siege + //199: You already have this boon + //200: Your vault cannot contain that item + //201: You can't put that much gold there + //202: You can't carry that much gold + //203: You don't have that much gold to transfer + //204: That item is not in the vault + //205: That item is not in the inventory + //206: This building can hold no more gold + private int message; + private String custom = ""; + + /** + * This is the general purpose constructor. + */ + public ErrorPopupMsg(int message) { + super(Protocol.STANDARDALERT); + this.message = message; + } + + public ErrorPopupMsg(int message, String custom) { + super(Protocol.STANDARDALERT); + this.message = message; + this.custom = custom; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ErrorPopupMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.STANDARDALERT, origin, reader); + } + + /** + * Copy constructor + */ + public ErrorPopupMsg(ErrorPopupMsg msg) { + super(Protocol.STANDARDALERT); + this.message = msg.message; + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.message = reader.getInt(); + reader.getInt(); + reader.getInt(); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.message); + writer.putString(this.custom); + writer.putInt(0); + } + + /** + * @return unknown01 + */ + + // Popup Window with no title and arbitrary text. + // Find an Enum for generic ERROR or way to set perhaps? + + public static void sendErrorMsg(PlayerCharacter player, String errorMessage) { + + if (player == null) + return; + + + ErrorPopupMsg popupMessage; + Dispatch errorDispatch; + + popupMessage = new ErrorPopupMsg(300, errorMessage); + + errorDispatch = Dispatch.borrow(player, popupMessage); + DispatchMessage.dispatchMsgDispatch(errorDispatch, Enum.DispatchChannel.SECONDARY); + + } + + public static void sendErrorPopup(PlayerCharacter player, int popupID) { + + ErrorPopupMsg errorPopup; + Dispatch errorDispatch; + + if (player == null) + return; + + errorPopup = new ErrorPopupMsg(popupID); + + errorDispatch = Dispatch.borrow(player, errorPopup); + DispatchMessage.dispatchMsgDispatch(errorDispatch, Enum.DispatchChannel.SECONDARY); + } +} diff --git a/src/engine/net/client/msg/FriendRequestMsg.java b/src/engine/net/client/msg/FriendRequestMsg.java new file mode 100644 index 00000000..6c2c1a2f --- /dev/null +++ b/src/engine/net/client/msg/FriendRequestMsg.java @@ -0,0 +1,65 @@ +/* +HashSet playerFriendSet = PlayerFriendsMap.get(playerUID); + playerFriendSet.add(friendUID); * Copyright 2013 MagicBane Emulator Project + * All Rights Reserved + */ +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.PlayerCharacter; + + +public class FriendRequestMsg extends ClientNetMsg { + + public String sourceName; + public String friendName; + + /** + * This is the general purpose constructor. + */ + public FriendRequestMsg(PlayerCharacter pc) { + super(Protocol.PLAYERFRIENDS); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public FriendRequestMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.PLAYERFRIENDS, origin, reader); + } + + /** + * Copy constructor + */ + public FriendRequestMsg(FriendRequestMsg msg) { + super(Protocol.PLAYERFRIENDS); + } + + + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + //Do we even want to try this? + this.sourceName = reader.getString(); //This is source name.. this friends list must never been updated since launch, not using IDS. + this.friendName = reader.getString(); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putString(this.sourceName); + writer.putString(this.friendName); + } +} diff --git a/src/engine/net/client/msg/FurnitureMsg.java b/src/engine/net/client/msg/FurnitureMsg.java new file mode 100644 index 00000000..e496e75b --- /dev/null +++ b/src/engine/net/client/msg/FurnitureMsg.java @@ -0,0 +1,294 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.exception.SerializationException; +import engine.math.Vector3fImmutable; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class FurnitureMsg extends ClientNetMsg { + + + private int type; + private int buildingID; + private int size; + private int pad = 0; + private int itemID; + private Vector3fImmutable furnitureLoc; + private int floor; + private float rot; + private float w; + + + + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public FurnitureMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.FURNITURE, origin, reader); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.type = reader.getInt(); + reader.getInt(); + this.buildingID = reader.getInt(); + if (this.type == 3){ + reader.getInt(); + reader.getInt(); + reader.getInt(); + this.itemID = reader.getInt(); + this.furnitureLoc = reader.getVector3fImmutable(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + return; + + + } + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + + + } + + @Override + protected int getPowerOfTwoBufferSize() { + // Larger size for historically larger opcodes + return (15); + } + + + // Precache and configure this message before we serialize it + + public void configure() { + + + + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + if (this.type == 3 || this.type == 4){ + writer.putInt(this.type); + writer.putInt(GameObjectType.Building.ordinal()); + writer.putInt(this.buildingID); + writer.putInt(0); + writer.putInt(0); + writer.putInt(GameObjectType.Item.ordinal()); + writer.putInt(62280); + writer.putVector3f(this.furnitureLoc); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + return; + } + + + writer.putInt(2); //Type + writer.putInt(GameObjectType.Building.ordinal()); + writer.putInt(this.buildingID); + writer.putInt(GameObjectType.Building.ordinal()); + writer.putInt(this.buildingID); + writer.put((byte)1); + writer.putInt(1); + writer.putInt(GameObjectType.Item.ordinal()); + writer.putInt(62280); + writer.putInt(0); + + writer.putInt(1); + + writer.putInt(0); + writer.putInt(362003); + writer.putInt(GameObjectType.Item.ordinal()); + writer.putInt(62280); + + writer.putInt(GameObjectType.Building.ordinal()); + writer.putInt(this.buildingID); + + writer.putInt(0); + writer.putInt(0); + + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + + writer.put((byte)0); + + // writer.putInt(0); + //writer.putInt(0); + +// writer.putInt(1); +// writer.putInt(GameObjectType.Building.ordinal()); +// writer.putInt(62281); +// writer.putInt(0); +// writer.putInt(362003); +// writer.putInt(0); +// writer.putInt(0); +// writer.putInt(0); +// writer.putInt(0); +// writer.putInt(0); +// writer.putInt(0); +// writer.putInt(0); +// writer.putInt(0); +// writer.put((byte)1); +// writer.putInt(1); +// writer.putInt(GameObjectType.Building.ordinal()); +// writer.putInt(62281); +// writer.putInt(0); +// writer.putInt(362003); +// writer.putInt(0); +// writer.putInt(0); +// writer.putInt(0); +// writer.putInt(0); +// writer.putInt(0); +// writer.putInt(0); +// writer.putInt(0); +// writer.putInt(0); +// writer.put((byte)0); + + + + + // else{ + // writer.putInt(building.getFurnitureList().size()); + // for (int furnitureID: building.getFurnitureList()){ + // Building furniture = Building.getBuildingFromCache(furnitureID); + // writer.putInt(GameObjectType.Building.ordinal()); + // writer.putInt(furniture.getObjectUUID()); + // writer.putInt(0); + // writer.putInt(0); + // writer.putInt(0); + // writer.putInt(0); + // writer.putInt(0); + // writer.putInt(0); + // writer.putInt(0); + // writer.putInt(0); + // writer.putInt(0); + // writer.putInt(0); + // writer.put((byte)0); + // } + + //} + + // else{ + // writer.putInt(building.getFurnitureList().size()); + // for (int furnitureID: building.getFurnitureList()){ + // Building furniture = Building.getBuildingFromCache(furnitureID); + // writer.putInt(GameObjectType.Building.ordinal()); + // writer.putInt(furniture.getObjectUUID()); + // writer.putInt(0); + // writer.putInt(0); + // writer.putInt(0); + // writer.putInt(0); + // writer.putInt(0); + // writer.putInt(0); + // writer.putInt(0); + // writer.putInt(0); + // writer.putInt(0); + // writer.putInt(0); + // writer.put((byte)0); + // } + // + // } + } + + public int getPad() { + return pad; + } + + public void setPad(int value) { + this.pad = value; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public int getSize() { + return size; + } + + public void setSize(int size) { + this.size = size; + } + + public int getBuildingID() { + return buildingID; + } + + public void setBuildingID(int buildingID) { + this.buildingID = buildingID; + } + + public int getItemID() { + return itemID; + } + + public void setItemID(int itemID) { + this.itemID = itemID; + } + + public Vector3fImmutable getFurnitureLoc() { + return furnitureLoc; + } + + public void setFurnitureLoc(Vector3fImmutable furnitureLoc) { + this.furnitureLoc = furnitureLoc; + } + + public int getFloor() { + return floor; + } + + public void setFloor(int floor) { + this.floor = floor; + } + + public float getRot() { + return rot; + } + + public void setRot(float rot) { + this.rot = rot; + } + + public float getw() { + return w; + } + + public void setw(float w) { + this.w = w; + } + +} diff --git a/src/engine/net/client/msg/GrantExperienceMsg.java b/src/engine/net/client/msg/GrantExperienceMsg.java new file mode 100644 index 00000000..d9799a37 --- /dev/null +++ b/src/engine/net/client/msg/GrantExperienceMsg.java @@ -0,0 +1,100 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.PlayerCharacter; + +public class GrantExperienceMsg extends ClientNetMsg { + + private int xpGranted; + private int objectType; + private int objectID; + + /** + * This is the general purpose constructor. + */ + public GrantExperienceMsg(PlayerCharacter pc, int xpGranted) { + super(Protocol.EXPERIENCE); + this.xpGranted = xpGranted; + this.objectType = pc.getObjectType().ordinal(); + this.objectID = pc.getObjectUUID(); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public GrantExperienceMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.EXPERIENCE, origin, reader); + } + + /** + * Copy constructor + */ + public GrantExperienceMsg(GrantExperienceMsg msg) { + super(Protocol.EXPERIENCE); + this.xpGranted = msg.xpGranted; + this.objectType = msg.objectType; + this.objectID = msg.objectID; + } + + /** + * Deserializes the subclass specific items from the supplied + * ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.get(); + this.xpGranted = reader.getInt(); + this.objectType = reader.getInt(); + this.objectID = reader.getInt(); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.xpGranted); + writer.putInt(this.objectType); + writer.putInt(this.objectID); + } + + public int getXPGranted() { + return this.xpGranted; + } + + public int getObjectType() { + return this.objectType; + } + + public int getObjectID() { + return this.objectID; + } + + public void setXPGranted(int value) { + this.xpGranted = value; + } + + public void setObjectType(int value) { + this.objectType = value; + } + + public void setObjectID(int value) { + this.objectID = value; + } +} diff --git a/src/engine/net/client/msg/GuildTreeStatusMsg.java b/src/engine/net/client/msg/GuildTreeStatusMsg.java new file mode 100644 index 00000000..9a6c2666 --- /dev/null +++ b/src/engine/net/client/msg/GuildTreeStatusMsg.java @@ -0,0 +1,195 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.*; + +import java.time.LocalDateTime; + +public class GuildTreeStatusMsg extends ClientNetMsg { + + // 2 = manage this asset. 20 = manage entire city + private int targetType; + private int targetID; + private String CityName; + private String OwnerName; + private String GuildName; + + private String motto; //motto Length 60 max? + private Building treeOfLife; + private PlayerCharacter player; + private City city; + private Zone cityZone; + private GuildTag cityGuildTag; + private GuildTag cityNationTag; + private java.time.LocalDateTime cityDate; + private boolean canAccess = false; + private byte canBind = 0; + private int accessType = 0; + + /** + * This is the general purpose constructor + */ + public GuildTreeStatusMsg() { + super(Protocol.GUILDTREESTATUS); + + this.targetType = 0; + this.targetID = 0; + this.OwnerName = ""; + this.CityName = ""; + this.GuildName = ""; + this.cityGuildTag = null; + this.cityNationTag = null; + } + + public GuildTreeStatusMsg(Building treeOfLife, PlayerCharacter sourcePlayer) { + super(Protocol.GUILDTREESTATUS); + this.treeOfLife = treeOfLife; + this.player = sourcePlayer; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public GuildTreeStatusMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.GUILDTREESTATUS, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + + targetType = reader.getInt(); + targetID = reader.getInt(); + + for (int i = 0; i < 3; i++) { + reader.monitorInt(0, "GuildTreeStatusMSG"); + } + } + + public void configure() { + + this.targetType = treeOfLife.getObjectType().ordinal(); + this.targetID = treeOfLife.getObjectUUID(); + this.OwnerName = treeOfLife.getOwner() != null ? treeOfLife.getOwnerName() : "Abandoned"; + this.CityName = treeOfLife.getCityName(); + this.GuildName = treeOfLife.getGuildName(); + + this.cityGuildTag = treeOfLife.getGuild().getGuildTag(); + this.cityNationTag = this.treeOfLife.getGuild().getNation().getGuildTag(); + + canAccess = this.canModify(); + canBind = 0; + + if (player.getGuild() != null && this.treeOfLife.getGuild() != null && !this.treeOfLife.getGuild().isErrant() + && player.getGuild().getNation() == this.treeOfLife.getGuild().getNation()) + canBind = 1; + + + if (this.treeOfLife.getGuild() != null && this.treeOfLife.getGuild().getOwnedCity() == null) + accessType = 9; + + //accessType not 9 not null city + if (accessType != 9) + if (this.treeOfLife.getGuild().getOwnedCity().isForceRename() && canAccess ) + accessType = 10; + else accessType = 8; + + cityZone = this.treeOfLife.getParentZone(); + city = null; + + if (cityZone != null) + if (cityZone.isPlayerCity()) + city = City.GetCityFromCache(cityZone.getPlayerCityUUID()); + else + if (this.treeOfLife != null && this.treeOfLife.getGuild() != null) + city = this.treeOfLife.getGuild().getOwnedCity(); + + + if (city == null) + CityName = "None"; + else + CityName = city.getCityName(); + + if (city == null) + cityDate = LocalDateTime.now(); + else + cityDate = city.established; + + } + + private boolean canModify() { + + return this.player.getGuild() == this.treeOfLife.getGuild() && GuildStatusController.isInnerCouncil(player.getGuildStatus()); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + writer.putInt(targetType); + writer.putInt(targetID); + + if (canAccess) + writer.putInt(accessType); + + else + writer.putInt(9); + + GuildTag._serializeForDisplay(cityGuildTag,writer); + GuildTag._serializeForDisplay(cityNationTag,writer); + + writer.putString(CityName); + writer.putString(GuildName); + writer.putString(OwnerName); + + writer.putLocalDateTime(cityDate); + writer.putInt(0); + writer.putInt(0); + if (city == null) + writer.putInt(0); + else + writer.putInt(city.isOpen() ? 1 : 0); //check mark for open city + writer.putInt(0); + writer.putInt(0); + writer.put(canBind); + writer.put((byte) 0); + + if (canAccess) + writer.put((byte) 1); + else + writer.put((byte) 0); + + writer.put((byte) 0); + + writer.putInt(1); + writer.putString(city != null ? city.getDescription() : "None"); + writer.putInt(0); + } + + public int getTargetID() { + return targetID; + } + +} \ No newline at end of file diff --git a/src/engine/net/client/msg/HirelingServiceMsg.java b/src/engine/net/client/msg/HirelingServiceMsg.java new file mode 100644 index 00000000..f60636ef --- /dev/null +++ b/src/engine/net/client/msg/HirelingServiceMsg.java @@ -0,0 +1,122 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.Enum.GameObjectType; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class HirelingServiceMsg extends ClientNetMsg { + + + public int npcID; + public int buildingID; + public int messageType; + public int repairCost; + + public static final int SETREPAIRCOST = 2; + + /** + * This is the general purpose constructor. + * + * @param channel + */ + public HirelingServiceMsg(int channel) { + super(Protocol.HIRELINGSERVICE); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public HirelingServiceMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.HIRELINGSERVICE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.messageType); + switch (this.messageType){ + case SETREPAIRCOST: + this.writeSetRepairCost(writer); + break; + } + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.messageType = reader.getInt(); + + switch (this.messageType){ + case SETREPAIRCOST: + this.readSetRepairCost(reader); + break; + + } + } + + private void readSetRepairCost(ByteBufferReader reader){ + reader.getInt(); //building type; + this.buildingID = reader.getInt(); + reader.getInt(); + this.npcID = reader.getInt(); + reader.getInt(); + reader.getInt(); //3 + this.repairCost = reader.getInt(); + reader.getInt(); + reader.getInt(); + + } + + private void writeSetRepairCost(ByteBufferWriter writer){ + writer.putInt(GameObjectType.Building.ordinal()); + writer.putInt(this.buildingID); + writer.putInt(GameObjectType.NPC.ordinal()); + writer.putInt(this.npcID); + writer.putInt(0); + writer.putInt(3); + writer.putInt(this.repairCost); + writer.putInt(0); + writer.putInt(0); + } + + private void writeTest(ByteBufferWriter writer){ + writer.putInt(GameObjectType.Building.ordinal()); + writer.putInt(this.buildingID); + writer.putInt(GameObjectType.NPC.ordinal()); + writer.putInt(this.npcID); + writer.putInt(3); + writer.putInt(1); + writer.putInt(1); + writer.putInt(GameObjectType.Building.ordinal()); + writer.putInt(this.buildingID); + writer.putInt(GameObjectType.NPC.ordinal()); + writer.putInt(this.npcID); + writer.putInt(0); + writer.putInt(0); + writer.putInt(3); + writer.putInt(1); + + writer.putInt(0); + + } +} diff --git a/src/engine/net/client/msg/HotzoneChangeMsg.java b/src/engine/net/client/msg/HotzoneChangeMsg.java new file mode 100644 index 00000000..c39f218d --- /dev/null +++ b/src/engine/net/client/msg/HotzoneChangeMsg.java @@ -0,0 +1,80 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.math.FastMath; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class HotzoneChangeMsg extends ClientNetMsg { + + private int zoneType; + private int zoneID; + + /** + * This is the general purpose constructor. + */ + public HotzoneChangeMsg(int zoneType, int zoneID) { + super(Protocol.ARCHOTZONECHANGE); + this.zoneType = zoneType; + this.zoneID = zoneID; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public HotzoneChangeMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.ARCHOTZONECHANGE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.zoneType); + writer.putInt(this.zoneID); + writer.putInt(FastMath.secondsUntilNextHour()); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.zoneType = reader.getInt(); + this.zoneID = reader.getInt(); + reader.getInt(); + } + + public int getZoneType() { + return this.zoneType; + } + + public int getZoneID() { + return this.zoneID; + } + + public void setZoneType(int value) { + this.zoneType = value; + } + + public void setZoneID(int value) { + this.zoneID = value; + } +} diff --git a/src/engine/net/client/msg/IgnoreListMsg.java b/src/engine/net/client/msg/IgnoreListMsg.java new file mode 100644 index 00000000..c7f75a03 --- /dev/null +++ b/src/engine/net/client/msg/IgnoreListMsg.java @@ -0,0 +1,110 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +import java.util.ArrayList; + +public class IgnoreListMsg extends ClientNetMsg { + + private final ArrayList names = new ArrayList<>(); + private int playerType; + private int playerID; + private String playerName; + + /** + * This is the general purpose constructor. + */ + public IgnoreListMsg() { + super(Protocol.ARCIGNORELISTUPDATE); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public IgnoreListMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ARCIGNORELISTUPDATE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.names.size()); + for (String name : this.names) { + writer.putString(name); + } + writer.putInt(this.playerType); + writer.putInt(this.playerID); + writer.putString(this.playerName); + writer.put((byte) 0); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + int size = reader.getInt(); + for (int i = 0; i < size; i++) { + this.names.add(reader.getString()); + } + this.playerType = reader.getInt(); + this.playerID = reader.getInt(); + this.playerName = reader.getString(); + reader.get(); + } + + public ArrayList getNames() { + return this.names; + } + + public void addName(String value) { + this.names.add(value); + } + + public void removeName(String value) { + this.names.remove(value); + } + + public int getPlayerType() { + return this.playerType; + } + + public void setPlayerType(int value) { + this.playerType = value; + } + + public int getPlayerID() { + return this.playerID; + } + + public void setPlayerID(int value) { + this.playerID = value; + } + + public String getName() { + return this.playerName; + } + + public void setName(String value) { + this.playerName = value; + } + +} diff --git a/src/engine/net/client/msg/IgnoreMsg.java b/src/engine/net/client/msg/IgnoreMsg.java new file mode 100644 index 00000000..c154df9c --- /dev/null +++ b/src/engine/net/client/msg/IgnoreMsg.java @@ -0,0 +1,181 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.gameManager.SessionManager; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.ClientConnection; +import engine.net.client.Protocol; +import engine.objects.Account; +import engine.objects.PlayerCharacter; + + +public class IgnoreMsg extends ClientNetMsg { + + private int unknown1; + private int unknown2; + private String nameToIgnore; + + /** + * This is the general purpose constructor. + */ + public IgnoreMsg() { + super(Protocol.IGNORE); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public IgnoreMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.IGNORE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(unknown1); + writer.putInt(unknown2); + writer.putUnicodeString(nameToIgnore); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + unknown1 = reader.getInt(); + unknown2 = reader.getInt(); + nameToIgnore = reader.getUnicodeString(); + } + + public void handleRequest(ClientConnection origin) { + + PlayerCharacter pcSource = SessionManager.getPlayerCharacter(origin); + + if (nameToIgnore.isEmpty()) { // list ignored players + String[] ignoredPlayers = pcSource.getIgnoredPlayerNames(); + String crlf = "\r\n"; + String out = "Ignored players (" + ignoredPlayers.length + "):"; + for (String name : ignoredPlayers) { + out += crlf + name; + } + ChatManager.chatSystemInfo(pcSource, out); + return; + } + + //FIX THIS, USE OUR CACHE! + PlayerCharacter pcToIgnore = PlayerCharacter.getByFirstName(nameToIgnore); + + if (pcSource == null) { + return; + } + if (pcToIgnore == null || pcToIgnore.getAccount() == null) { + ChatManager.chatSystemError(pcSource, "Character name " + nameToIgnore + " does not exist and cannot be ignored."); + return; + } + if (pcToIgnore.getObjectUUID() == pcSource.getObjectUUID()) { + ChatManager.chatSystemError(pcSource, "Try as you might, you are unable to ignore yourself!"); + return; + } + String fn = pcToIgnore.getFirstName(); + + Account ac = pcSource.getAccount(); + + if (ac == null) + return; + + if (pcSource.isIgnoringPlayer(pcToIgnore)) { + + if (ac != null) { + if (!DbManager.PlayerCharacterQueries.SET_IGNORE_LIST(ac.getObjectUUID(), pcToIgnore.getObjectUUID(), false, pcToIgnore.getFirstName())) { + ChatManager.chatSystemError(pcSource, "Unable to update database ignore list."); + } + } else { + ChatManager.chatSystemError(pcSource, "Unable to update database ignore list."); + } + + pcSource.removeIgnoredPlayer(pcToIgnore.getAccount()); + ChatManager.chatSystemInfo(pcSource, "Character " + fn + " is no longer ignored."); + } else { + if (!PlayerCharacter.isIgnorable()) { + ChatManager.chatSystemError(pcSource, "This character cannot be ignored."); + return; + } + if (PlayerCharacter.isIgnoreListFull()) { + ChatManager.chatSystemError(pcSource, "Your ignore list is already full."); + return; + } + + if (ac != null) { + if (!DbManager.PlayerCharacterQueries.SET_IGNORE_LIST(ac.getObjectUUID(), pcToIgnore.getObjectUUID(), true, pcToIgnore.getFirstName())) { + ChatManager.chatSystemError(pcSource, "Unable to update database ignore list. This ignore will not persist past server down."); + } + } else { + ChatManager.chatSystemError(pcSource, "Unable to update database ignore list."); + } + + pcSource.addIgnoredPlayer(pcToIgnore.getAccount(), pcToIgnore.getFirstName()); + ChatManager.chatSystemInfo(pcSource, "Character " + fn + " is now being ignored."); + } + } + + /** + * @return the unknown1 + */ + public int getUnknown1() { + return unknown1; + } + + /** + * @param unknown1 the unknown1 to set + */ + public void setUnknown1(int unknown1) { + this.unknown1 = unknown1; + } + + /** + * @return the unknown2 + */ + public int getUnknown2() { + return unknown2; + } + + /** + * @param unknown2 the unknown2 to set + */ + public void setUnknown2(int unknown2) { + this.unknown2 = unknown2; + } + + /** + * @return the nameToIgnore + */ + public String getNameToIgnore() { + return nameToIgnore; + } + + /** + * @param nameToIgnore the nameToIgnore to set + */ + public void setNameToIgnore(String nameToIgnore) { + this.nameToIgnore = nameToIgnore; + } + +} diff --git a/src/engine/net/client/msg/InvalidTradeRequestMsg.java b/src/engine/net/client/msg/InvalidTradeRequestMsg.java new file mode 100644 index 00000000..ade68c9a --- /dev/null +++ b/src/engine/net/client/msg/InvalidTradeRequestMsg.java @@ -0,0 +1,98 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractGameObject; + +/** + * Invalid trade request. + * Attempt to trade with player who is already trading. + */ + +public class InvalidTradeRequestMsg extends ClientNetMsg { + + private int unknown01; + private int busyType; + private int busyID; + private int requesterType; + private int requesterID; + + /** + * This is the general purpose constructor + */ + public InvalidTradeRequestMsg(int unknown01, AbstractGameObject busy, AbstractGameObject requester) { + super(Protocol.ARCREQUESTTRADEBUSY); + this.busyType = busy.getObjectType().ordinal(); + this.busyID = busy.getObjectUUID(); + this.requesterType = requester.getObjectType().ordinal(); + this.requesterID = requester.getObjectUUID(); + this.unknown01 = unknown01; + + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public InvalidTradeRequestMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.ARCREQUESTTRADEBUSY, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + unknown01 = reader.getInt(); + busyType = reader.getInt(); + busyID = reader.getInt(); + requesterType = reader.getInt(); + requesterID = reader.getInt(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + writer.putInt(unknown01); + writer.putInt(busyType); + writer.putInt(busyID); + writer.putInt(requesterType); + writer.putInt(requesterID); + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + public int getRequesterID() { + return requesterID; + } + +} diff --git a/src/engine/net/client/msg/ItemEffectMsg.java b/src/engine/net/client/msg/ItemEffectMsg.java new file mode 100644 index 00000000..c5348447 --- /dev/null +++ b/src/engine/net/client/msg/ItemEffectMsg.java @@ -0,0 +1,263 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractWorldObject; + +public class ItemEffectMsg extends ClientNetMsg { + + protected int numTrains; + protected int effectID; + protected int sourceType; + protected int sourceID; + protected int targetType; + protected int targetID; + + protected int unknown02; + protected int unknown03; + protected int duration; + protected int unknown05; + protected byte unknown06; + protected int ItemType; + protected int powerTypeID; + protected String powerUsedName; + protected int itemID; + /** + * This is the general purpose constructor. + */ + public ItemEffectMsg() { + super(Protocol.POWERACTION); + this.numTrains = 0; + this.effectID = 0; + this.sourceType = 0; + this.sourceID = 0; + this.targetType = 0; + this.targetID = 0; + + this.unknown02 = 0; + this.unknown03 = 0; + this.duration = 0; + this.unknown05 = 0; + this.unknown06 = (byte) 0; + + this.ItemType = 0; + this.itemID = 0; + this.powerUsedName = ""; + } + + /** + * This is the general purpose constructor. + */ + public ItemEffectMsg(AbstractWorldObject source, AbstractWorldObject target, int numTrains, int effectID, int duration, + int powerUsedID, String powerUsedName) { + super(Protocol.POWERACTION); + this.numTrains = numTrains; + this.effectID = effectID; + + if (source != null) { + this.sourceType = source.getObjectType().ordinal(); + this.sourceID = source.getObjectUUID(); + } else { + this.sourceType = 0; + this.sourceID = 0; + } + + if (target != null) { + this.targetType = target.getObjectType().ordinal(); + this.targetID = target.getObjectUUID(); + } else { + this.targetType = 0; + this.targetID = 0; + } + this.unknown02 = 0; //0 = apply, 2= remove + this.unknown03 = 0; + this.duration = duration; + this.unknown05 = 0; //1 = remove item effect + this.unknown06 = (byte) 0; //1 = remove item effect + this.ItemType = powerUsedID; + this.itemID = 0; + this.powerUsedName = powerUsedName; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ItemEffectMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.POWERACTION, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.numTrains); + writer.putInt(this.effectID); + + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.putInt(this.targetType); + writer.putInt(this.targetID); + + writer.putInt(this.unknown02); + writer.putInt(this.unknown03); + writer.putInt(this.duration); + writer.putInt(this.unknown05); + writer.put(this.unknown06); + + writer.putInt(this.ItemType); + writer.putInt(this.itemID); + + writer.putString(this.powerUsedName); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.numTrains = reader.getInt(); + this.effectID = reader.getInt(); + + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + + this.unknown02 = reader.getInt(); + this.unknown03 = reader.getInt(); + this.duration = reader.getInt(); + this.unknown05 = reader.getInt(); + this.unknown06 = reader.get(); + + this.ItemType = reader.getInt(); + this.itemID = reader.getInt(); + + this.powerUsedName = reader.getString(); + } + + public int getNumTrains() { + return this.numTrains; + } + + public int getEffectID() { + return this.effectID; + } + + public int getSourceType() { + return this.sourceType; + } + + public int getSourceID() { + return this.sourceID; + } + + public int getTargetType() { + return this.targetType; + } + + public int getTargetID() { + return this.targetID; + } + + public int getUnknown02() { + return this.unknown02; + } + + public int getUnknown03() { + return this.unknown03; + } + + public int getDuration() { + return this.duration; + } + + public int getUnknown05() { + return this.unknown05; + } + + public byte getUnknown06() { + return this.unknown06; + } + + public int getItemType() { + return this.ItemType; + } + public int getItemID() { + return this.itemID; + } + + public String getPowerUsedName() { + return this.powerUsedName; + } + + public void setNumTrains(int value) { + this.numTrains = value; + } + + public void setEffectID(int value) { + this.effectID = value; + } + + public void setSourceType(int value) { + this.sourceType = value; + } + + public void setSourceID(int value) { + this.sourceID = value; + } + + public void setTargetType(int value) { + this.targetType = value; + } + + public void setTargetID(int value) { + this.targetID = value; + } + + public void setUnknown02(int value) { + this.unknown02 = value; + } + + public void setUnknown03(int value) { + this.unknown03 = value; + } + + public void setDuration(int value) { + this.duration = value; + } + + public void setUnknown05(int value) { + this.unknown05 = value; + } + + public void setUnknown06(byte value) { + this.unknown06 = value; + } + + public void setItemType(int value) { + this.ItemType = value; + } + public void setItemID(int value){ + this.itemID = value; + } + + public void setPowerUsedName(String value) { + this.powerUsedName = value; + } +} diff --git a/src/engine/net/client/msg/ItemHealthUpdateMsg.java b/src/engine/net/client/msg/ItemHealthUpdateMsg.java new file mode 100644 index 00000000..fc7a5443 --- /dev/null +++ b/src/engine/net/client/msg/ItemHealthUpdateMsg.java @@ -0,0 +1,76 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class ItemHealthUpdateMsg extends ClientNetMsg { + + private int slot; + private float newDurability; + + /** + * This is the general purpose constructor. + */ + public ItemHealthUpdateMsg(int slot, float newDurability) { + super(Protocol.ITEMHEALTHUPDATE); + this.slot = slot; + this.newDurability = newDurability; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ItemHealthUpdateMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ITEMHEALTHUPDATE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.slot); + writer.putFloat(this.newDurability); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.slot = reader.getInt(); + this.newDurability = reader.getFloat(); + } + + public int getSlot() { + return this.slot; + } + + public void setSlot(int value) { + this.slot = value; + } + + public float getNewDurability() { + return this.newDurability; + } + + public void setNewDurability(float value) { + this.newDurability = value; + } +} diff --git a/src/engine/net/client/msg/ItemProductionMsg.java b/src/engine/net/client/msg/ItemProductionMsg.java new file mode 100644 index 00000000..96d6570d --- /dev/null +++ b/src/engine/net/client/msg/ItemProductionMsg.java @@ -0,0 +1,567 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.gameManager.BuildingManager; +import engine.gameManager.PowersManager; +import engine.net.AbstractConnection; +import engine.net.AbstractNetMsg; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Building; +import engine.objects.Item; +import engine.objects.MobLoot; +import engine.objects.NPC; +import engine.powers.EffectsBase; + +import java.util.ArrayList; +import java.util.HashMap; + +public class ItemProductionMsg extends ClientNetMsg { + + private static final int ACTION_PRODUCE = 1; + private static final int ACTION_JUNK = 2; + private static final int ACTION_RECYCLE = 3; + private static final int ACTION_COMPLETE = 4; + private static final int ACTION_DEPOSIT = 6; + private static final int ACTION_SETPRICE = 5; + private static final int ACTION_TAKE = 7; + private static final int ACTION_CONFIRM_SETPRICE = 9; + private static final int ACTION_CONFIRM_DEPOSIT = 10; + private static final int ACTION_CONFIRM_TAKE = 11; // Unsure. Sent by client + private static final int ACTION_CONFIRM_PRODUCE = 8; + + private ArrayList ItemList; + private int size; + private int buildingUUID; + private int unknown01; + private int itemUUID; + private int itemType; + public void setItemUUID(int itemUUID) { + this.itemUUID = itemUUID; + } + + private int totalProduction; + private int unknown03; + private int pToken; + private int sToken; + private String name; + private int actionType; + private int npcUUID; + private boolean add; + private int itemPrice; + private boolean isMultiple; + + private HashMap itemIDtoTypeMap;; + + /** + * This is the general purpose constructor. + */ + + public ItemProductionMsg() { + super(Protocol.ITEMPRODUCTION); + this.actionType = 0; + this.size = 0; + this.buildingUUID = 0; + this.unknown01 = 0; + this.itemUUID = 0; + this.totalProduction = 0; + this.unknown03 = 0; + this.pToken = 0; + this.sToken = 0; + this.name = ""; + this.itemPrice = 0; + this.itemType = 0; + + } + + public ItemProductionMsg(Building building, NPC vendor, Item item, int actionType, boolean add) { + super(Protocol.ITEMPRODUCTION); + this.actionType = actionType; + this.size = 0; + this.buildingUUID = building.getObjectUUID(); + this.npcUUID = vendor.getObjectUUID(); + this.itemType = item.getObjectType().ordinal(); + this.itemUUID = item.getObjectUUID(); + this.unknown01 = 0; + this.totalProduction = 0; + this.unknown03 = 0; + this.pToken = 0; + this.sToken = 0; + this.name = ""; + this.add = add; + this.itemPrice = item.getValue(); + + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ItemProductionMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ITEMPRODUCTION, origin, reader); + + } + + /** + * @see AbstractNetMsg#getPowerOfTwoBufferSize() + */ + @Override + protected int getPowerOfTwoBufferSize() { + //Larger size for historically larger opcodes + return (16); // 2^16 == 64k + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + Building building = BuildingManager.getBuildingFromCache(this.buildingUUID); + if (building == null) + return; + // Common Header + + writer.putInt(this.actionType); + writer.putInt(GameObjectType.Building.ordinal()); + writer.putInt(this.buildingUUID); + writer.putInt(GameObjectType.NPC.ordinal()); + writer.putInt(this.npcUUID); + + switch (this.actionType) { + + case ACTION_CONFIRM_DEPOSIT: + writer.putInt(0); // Not item ordinal? + writer.putInt(0); // Not item uuid? + writer.putInt(1); + writer.putInt(0); + + if (!add) { + writer.put((byte) 1); + Item item = Item.getFromCache(this.itemUUID); + if (item != null) + Item.serializeForClientMsgWithoutSlot(item,writer); + writer.putInt(building.getStrongboxValue()); + writer.putInt(0); + writer.putInt(0); + writer.put((byte) 0); + break; + } + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.put((byte) 1); + Item item; + if (this.itemType == GameObjectType.Item.ordinal()) + item = Item.getFromCache(this.itemUUID); + else + item = MobLoot.getFromCache(this.itemUUID); + if (item != null) + Item.serializeForClientMsgWithoutSlot(item,writer); + writer.putInt(building.getStrongboxValue()); + writer.putInt(0); + writer.putInt(0); + writer.put((byte) 0); + break; + case ACTION_CONFIRM_TAKE: + writer.putInt(this.itemType); + writer.putInt(this.itemUUID); + writer.putInt(1); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.put((byte) 0); + break; + case ACTION_SETPRICE: + writer.putInt(this.itemType); + writer.putInt(this.itemUUID); + writer.putInt(1); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(this.itemPrice); // new price + writer.putInt(0); + writer.putInt(0); + writer.put((byte)0); + break; + case ACTION_CONFIRM_SETPRICE: + writer.putInt(this.itemType); + writer.putInt(this.itemUUID); + writer.putInt(1); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.put((byte)1); + writer.putInt(building.getStrongboxValue()); // new price + writer.putInt(0); + writer.putInt(0); + //writer.put((byte) 0); + break; + case ACTION_DEPOSIT: + writer.putInt(this.itemType); + writer.putInt(this.itemUUID); + writer.putInt(1); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.put((byte) 0); + break; + case ACTION_TAKE: + case ACTION_RECYCLE: + + writer.putInt(0); + writer.putInt(0); + writer.putInt(1); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.put((byte) 1); + writer.putInt(building.getStrongboxValue()); + + if (this.itemIDtoTypeMap != null){ + writer.putInt(this.itemIDtoTypeMap.size()); + + for (int itemID : this.itemIDtoTypeMap.keySet()) { + writer.putInt(this.itemIDtoTypeMap.get(itemID)); + writer.putInt(itemID); + } + + }else + writer.putInt(0); + + writer.putInt(0); + + break; + case ACTION_CONFIRM_PRODUCE: + writer.putInt(0); + writer.putInt(0); + writer.putInt(1); + MobLoot toRoll = MobLoot.getFromCache(this.itemUUID); + writer.putInt(-1497023830); + if (toRoll != null && toRoll.getPrefix() != null && !toRoll.getPrefix().isEmpty()){ + EffectsBase eb = PowersManager.getEffectByIDString(toRoll.getPrefix()); + if (eb == null) + this.pToken = 0; + else + this.pToken = eb.getToken(); + } + + if (toRoll != null && toRoll.getSuffix() != null && !toRoll.getSuffix().isEmpty()){ + EffectsBase eb = PowersManager.getEffectByIDString(toRoll.getSuffix()); + if (eb == null) + this.sToken = 0; + else + this.sToken = eb.getToken(); + } + if (toRoll.isRandom() == false || (toRoll != null && toRoll.isComplete())){ + writer.putInt(this.pToken); + writer.putInt(this.sToken); + }else{ + writer.putInt(0); + writer.putInt(0); + } + + writer.putString(toRoll.getCustomName());; + writer.putInt(GameObjectType.MobLoot.ordinal()); + writer.putInt(this.itemUUID); + writer.putInt(0); //items left to produce? + if (toRoll != null){ + writer.putInt(toRoll.getItemBaseID()); + writer.putInt(toRoll.getValue()); + } + else{ + writer.putInt(0); + writer.putInt(0); + } + + NPC vendor = NPC.getFromCache(this.npcUUID); + if (vendor != null){ + if (toRoll.isComplete()){ + writer.putInt(0); + writer.putInt(0); + }else{ + long timeLeft = toRoll.getDateToUpgrade() - System.currentTimeMillis(); + + timeLeft /=1000; + writer.putInt((int)timeLeft); + writer.putInt(vendor.getRollingTimeInSeconds(toRoll.getItemBaseID())); + } + + }else{ + writer.putInt(0); + writer.putInt(0); + } + if (toRoll.isComplete()) + writer.putInt(0); + else + writer.putInt(1); + writer.put((byte)0); + + if (toRoll != null && toRoll.isComplete()) + writer.put((byte)1); + else + writer.put((byte)0); + writer.put((byte)0); + writer.put((byte)1); + writer.putInt(vendor.getBuilding().getStrongboxValue()); + + writer.putInt(0); + writer.putInt(0); + //writer.putInt(0); //error popup + + break; + case ACTION_COMPLETE: + writer.putInt(this.itemType); + writer.putInt(this.itemUUID); + writer.putInt(this.totalProduction); + writer.putInt(this.unknown03); + writer.putInt(this.pToken); + writer.putInt(this.sToken); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.put((byte)0); + break; + case ACTION_JUNK: + writer.putInt(this.itemType); + writer.putInt(this.itemUUID); + writer.putInt(this.totalProduction); + writer.putInt(this.unknown03); + writer.putInt(this.pToken); + writer.putInt(this.sToken); + writer.putString(this.name); + writer.putInt(0); + writer.putInt(0); + writer.putShort((short)0); + writer.put((byte)0); + break; + default: + writer.putInt(0); + writer.putInt(1); + writer.putInt(0); + writer.putInt(0); + break; + } + + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + + // Common header + + this.actionType = reader.getInt(); + reader.getInt(); // Building type padding + this.buildingUUID = reader.getInt(); + reader.getInt(); // NPC type padding + this.npcUUID = reader.getInt(); + + switch (this.actionType) { + case ACTION_SETPRICE: + this.itemType = reader.getInt(); + this.itemUUID = reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + this.itemPrice = reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.get(); + break; + case ACTION_RECYCLE: + case ACTION_TAKE: + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.get(); + this.size = reader.getInt(); + HashMap tempIDs = new HashMap<>(); + for (int i = 0; i < this.size; i++) { + int type = reader.getInt(); // Item type padding + this.itemUUID = reader.getInt(); + tempIDs.put(this.itemUUID, type); + } + reader.getInt(); + this.itemIDtoTypeMap = tempIDs; + break; + case ACTION_DEPOSIT: + this.itemType = reader.getInt(); + this.itemUUID = reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.get(); + break; + case ACTION_JUNK: + this.itemType = reader.getInt(); + this.itemUUID = reader.getInt(); + this.totalProduction =reader.getInt(); + this.unknown03 = reader.getInt(); + this.pToken = reader.getInt(); + this.sToken = reader.getInt(); + this.name = reader.getString(); + reader.getInt(); + reader.getInt(); + reader.get(); + break; + default: + this.itemType = reader.getInt(); + this.itemUUID = reader.getInt(); + this.totalProduction =reader.getInt(); + this.unknown03 = reader.getInt(); + this.pToken = reader.getInt(); + this.sToken = reader.getInt(); + this.name = reader.getString(); + this.isMultiple = reader.getInt() != 0 ? true:false; + reader.getInt(); + + if (this.actionType == ACTION_COMPLETE || this.actionType == ACTION_JUNK) + reader.get(); + else + reader.getShort(); + break; + + } + } + + public int getItemType() { + return itemType; + } + + // TODO fix ArrayList Accessability. + public ArrayList getItemList() { + return this.ItemList; + } + + public void addItem(Long item) { + this.ItemList.add(item); + } + + public int getUnknown01() { + return unknown01; + } + + public int getTotalProduction() { + return totalProduction; + } + + public int getUnknown03() { + return unknown03; + } + + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + public void setTotalProduction(int unknown02) { + this.totalProduction = unknown02; + } + + public void setUnknown03(int unknown03) { + this.unknown03 = unknown03; + } + + public void setItemList(ArrayList value) { + this.ItemList = value; + } + + public int getItemUUID() { + return itemUUID; + } + + public void setpToken(int pToken) { + this.pToken = pToken; + } + + public int getpToken() { + return pToken; + } + + public void setsToken(int sToken) { + this.sToken = sToken; + } + + public int getsToken() { + return sToken; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public int getSize() { + return size; + } + + public void setSize(int size) { + this.size = size; + } + + public int getActionType() { + return actionType; + } + + public final void setActionType(int actionType) { + this.actionType = actionType; + } + + public int getNpcUUID() { + return npcUUID; + } + + public HashMap getItemIDtoTypeMap() { + return itemIDtoTypeMap; + } + + /** + * @return the itemPrice + */ + public int getItemPrice() { + return itemPrice; + } + + public boolean isMultiple() { + return isMultiple; + } + + public void setMultiple(boolean isMultiple) { + this.isMultiple = isMultiple; + } +} diff --git a/src/engine/net/client/msg/KeepAliveServerClientMsg.java b/src/engine/net/client/msg/KeepAliveServerClientMsg.java new file mode 100644 index 00000000..17c35351 --- /dev/null +++ b/src/engine/net/client/msg/KeepAliveServerClientMsg.java @@ -0,0 +1,86 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class KeepAliveServerClientMsg extends ClientNetMsg { + + private int firstData; // First 4 bytes + private int secondData; // Second 4 bytes + + private double timeLoggedIn; + private byte endData; // Last byte + + /** + * This is the general purpose constructor. + */ + public KeepAliveServerClientMsg(int firstData, int secondData, byte endData) { + super(Protocol.KEEPALIVESERVERCLIENT); + this.firstData = firstData; + this.secondData = secondData; + this.endData = endData; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public KeepAliveServerClientMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.KEEPALIVESERVERCLIENT, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putDouble(this.timeLoggedIn); + writer.put((byte)0); + //writer.putDouble(1000); + + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.timeLoggedIn = reader.getDouble(); + this.endData = reader.get(); + } + + public int getFirstData() { + return firstData; + } + + public int getSecondData() { + return secondData; + } + + public byte getEndData() { + return endData; + } + + public double getTimeLoggedIn() { + return timeLoggedIn; + } + + public void setTimeLoggedIn(double timeLoggedIn) { + this.timeLoggedIn = timeLoggedIn; + } +} diff --git a/src/engine/net/client/msg/LeaderboardMessage.java b/src/engine/net/client/msg/LeaderboardMessage.java new file mode 100644 index 00000000..7dc992a0 --- /dev/null +++ b/src/engine/net/client/msg/LeaderboardMessage.java @@ -0,0 +1,103 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.ShrineType; +import engine.exception.SerializationException; +import engine.gameManager.BuildingManager; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Building; +import engine.objects.Guild; +import engine.objects.GuildTag; +import engine.objects.Shrine; + +public class LeaderboardMessage extends ClientNetMsg { + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public LeaderboardMessage(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.LEADERBOARD, origin, reader); + } + + public LeaderboardMessage() { + super(Protocol.LEADERBOARD); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + + writer.putInt(ShrineType.values().length);//?? + + for (ShrineType shrineType : ShrineType.values()) { + writer.putInt(shrineType.ordinal()); + writer.putInt(shrineType.getShrinesCopy().size()); + int i = 0; + for (Shrine shrine : shrineType.getShrinesCopy()) { + i++; + writer.putInt(shrine.getFavors()); + Building shrineBuilding = BuildingManager.getBuilding(shrine.getBuildingID()); + if (shrineBuilding != null) { + Guild shrineGuild = shrineBuilding.getGuild(); + if (shrineGuild != null) { + writer.putInt(shrineGuild.getObjectType().ordinal()); + writer.putInt(shrineGuild.getObjectUUID()); + + GuildTag._serializeForDisplay(shrineGuild.getGuildTag(),writer); + writer.putString(shrineGuild.getName()); + } else { + writer.putLong(0); + writer.putInt(16); + writer.putInt(16); + writer.putInt(16); + writer.putInt(0); + writer.putInt(0); + writer.putString(""); + } + }else{ + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + + } + } + writer.putString(shrineType.name()); + } + + } + @Override + protected int getPowerOfTwoBufferSize() { + return (14); // 2^14 == 16384 + } + +} diff --git a/src/engine/net/client/msg/LeaveWorldMsg.java b/src/engine/net/client/msg/LeaveWorldMsg.java new file mode 100644 index 00000000..62ab8bbc --- /dev/null +++ b/src/engine/net/client/msg/LeaveWorldMsg.java @@ -0,0 +1,52 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class LeaveWorldMsg extends ClientNetMsg { + + /** + * This is the general purpose constructor. + */ + public LeaveWorldMsg() { + super(Protocol.LEAVEREQUEST); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public LeaveWorldMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.LEAVEREQUEST, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + } + +} diff --git a/src/engine/net/client/msg/LoadCharacterMsg.java b/src/engine/net/client/msg/LoadCharacterMsg.java new file mode 100644 index 00000000..0471da38 --- /dev/null +++ b/src/engine/net/client/msg/LoadCharacterMsg.java @@ -0,0 +1,169 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.AbstractNetMsg; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.*; + +public class LoadCharacterMsg extends ClientNetMsg { + + private AbstractCharacter absChar; + private Corpse corpse; + private boolean hideNonAscii; + + /** + * This is the general purpose constructor. + */ + public LoadCharacterMsg(AbstractCharacter ch, boolean laln) { + super(Protocol.LOADCHARACTER); + this.absChar = ch; + this.corpse = null; + this.hideNonAscii = laln; + } + + /** + * This is the general purpose constructor. + */ + public LoadCharacterMsg(Corpse corpse, boolean laln) { + super(Protocol.LOADCHARACTER); + this.corpse = corpse; + this.absChar = null; + this.hideNonAscii = laln; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public LoadCharacterMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.LOADCHARACTER, origin, reader); + } + + /** + * @see AbstractNetMsg#getPowerOfTwoBufferSize() + */ + @Override + protected int getPowerOfTwoBufferSize() { + //Larger size for historically larger opcodes + return (17); // 2^17 == 131,072 + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + + if (absChar != null && absChar.getObjectType() == GameObjectType.NPC) { + NPC npc = (NPC)absChar; + + + + if (npc.getBuilding() != null){ + writer.putInt(npc.getBuildingLevel()); + writer.putInt(npc.getBuildingFloor()); + }else{ + writer.putInt(-1); + writer.putInt(-1); + } + + + } else if (absChar != null) { + + if (absChar.getObjectType().equals(GameObjectType.PlayerCharacter)){ + Regions region = absChar.getRegion(); + + if (region == null){ + writer.putInt(-1); + writer.putInt(-1); + }else{ + Building regionBuilding = Regions.GetBuildingForRegion(region); + if (regionBuilding == null){ + writer.putInt(-1); + writer.putInt(-1); + }else{ + writer.putInt(region.getLevel()); + writer.putInt(region.getRoom()); + } + } + //TODO below is Mob Region Serialization, not implemented. default to -1, which is ground. + }else{ + writer.putInt(-1); + writer.putInt(-1); + } + + + + + } else if (corpse != null){ + writer.putInt(-1); + writer.putInt(-1); + } + if (corpse != null) { + writer.putInt(Float.floatToIntBits(corpse.getLoc().getX())); + writer.putInt(Float.floatToIntBits(corpse.getLoc().getY())); + writer.putInt(Float.floatToIntBits(corpse.getLoc().getZ())); + writer.put((byte) 0); + } else if (absChar != null) { + + writer.putFloat(absChar.getLoc().getX()); + writer.putFloat(absChar.getLoc().getY()); + writer.putFloat(absChar.getLoc().getZ()); + + if (absChar.isMoving()) { + writer.put((byte) 1); + writer.putFloat(absChar.getEndLoc().x); + writer.putFloat(absChar.getEndLoc().y); + writer.putFloat(absChar.getEndLoc().z); + } else + writer.put((byte) 0); + } else { + writer.put((byte) 0); + } + + if (corpse != null) + Corpse._serializeForClientMsg(corpse, writer, this.hideNonAscii); + else if (absChar != null) + AbstractCharacter.serializeForClientMsgOtherPlayer(this.absChar,writer, this.hideNonAscii); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + int unknown1 = reader.getInt(); + int unknown2 = reader.getInt(); + int unknown3 = reader.getInt(); + int unknown4 = reader.getInt(); + int unknown5 = reader.getInt(); + // TODO finish deserialization impl + } + + public AbstractCharacter getChar() { + return this.absChar; + } + + public void setChar(AbstractCharacter value) { + this.absChar = value; + } + + public void setCorpse(Corpse value) { + this.corpse = value; + } +} diff --git a/src/engine/net/client/msg/LoadStructureMsg.java b/src/engine/net/client/msg/LoadStructureMsg.java new file mode 100644 index 00000000..6584ef83 --- /dev/null +++ b/src/engine/net/client/msg/LoadStructureMsg.java @@ -0,0 +1,104 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.AbstractNetMsg; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Building; + +import java.util.ArrayList; + + +public class LoadStructureMsg extends ClientNetMsg { + + private ArrayList structureList; + + /** + * This is the general purpose constructor. + */ + public LoadStructureMsg() { + this(new ArrayList<>()); + } + + /** + * This is the general purpose constructor. + * + * @param structures + */ + public LoadStructureMsg(ArrayList structures) { + super(Protocol.LOADSTRUCTURE); + this.structureList = structures; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public LoadStructureMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.LOADSTRUCTURE, origin, reader); + } + + /** + * @see AbstractNetMsg#getPowerOfTwoBufferSize() + */ + @Override + protected int getPowerOfTwoBufferSize() { + //Larger size for historically larger opcodes + return (18); // 2^16 == 64k + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + for (int i = 0; i < 4; i++) + writer.putInt(0); + + writer.putInt(this.structureList.size()); + + for (Building building:this.structureList){ + Building._serializeForClientMsg(building, writer); + } + writer.putInt(0); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + int size = reader.getInt(); + // TODO finish Deserialize impl + } + + // TODO fix ArrayList Accessability. + public ArrayList getStructureList() { + return this.structureList; + } + + public void addObject(Building obj) { + this.structureList.add(obj); + } + + public int size() { + return this.structureList.size(); + } +} diff --git a/src/engine/net/client/msg/LockUnlockDoorMsg.java b/src/engine/net/client/msg/LockUnlockDoorMsg.java new file mode 100644 index 00000000..b99f5740 --- /dev/null +++ b/src/engine/net/client/msg/LockUnlockDoorMsg.java @@ -0,0 +1,113 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class LockUnlockDoorMsg extends ClientNetMsg { + + private int doorID; + private long targetID; + private int unk1; + private int unk2; + + /** + * This is the general purpose constructor. + */ + public LockUnlockDoorMsg() { + super(Protocol.LOCKUNLOCKDOOR); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public LockUnlockDoorMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.LOCKUNLOCKDOOR, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(0); + writer.putInt(doorID); + writer.putLong(targetID); + writer.putInt(unk1); + writer.putInt(unk2); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); + this.doorID = reader.getInt(); + this.targetID = reader.getLong(); + this.unk1 = reader.getInt(); + this.unk2 = reader.getInt(); + } + + /** + * @return the unknown1 + */ + public int getDoorID() { + return doorID; + } + + /** + * @param unknown1 + * the unknown1 to set + */ + public void setDoorID(int doorID) { + this.doorID = doorID; + } + + /** + * @return the targetID + */ + public long getTargetID() { + return targetID; + } + + /** + * @param targetID + * the targetID to set + */ + public void setTargetID(long targetID) { + this.targetID = targetID; + } + + public int getUnk1() { + return this.unk1; + } + + public void setUnk1(int value) { + this.unk1 = value; + } + + public int getUnk2() { + return this.unk2; + } + + public void setUnk2(int value) { + this.unk2 = value; + } + +} diff --git a/src/engine/net/client/msg/LoginToGameServerMsg.java b/src/engine/net/client/msg/LoginToGameServerMsg.java new file mode 100644 index 00000000..a25f8fc3 --- /dev/null +++ b/src/engine/net/client/msg/LoginToGameServerMsg.java @@ -0,0 +1,106 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class LoginToGameServerMsg extends ClientNetMsg { + + private String secKey; + private int Unknown01; + private int Unknown02; + + /** + * This is the general purpose constructor. + */ + public LoginToGameServerMsg() { + super(Protocol.LOGINTOGAMESERVER); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public LoginToGameServerMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.LOGINTOGAMESERVER, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putHexString(this.secKey); + writer.putInt(this.Unknown01); + writer.putInt(this.Unknown02); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.secKey = reader.getHexString(); + + this.Unknown01 = reader.monitorInt(1065353216, "ValidateGameServerMsg 01"); + this.Unknown02 = reader.monitorInt(1065353216, "ValidateGameServerMsg 02"); + } + + /** + * @return the secKey + */ + public String getSecKey() { + return secKey; + } + + /** + * @param secKey + * the secKey to set + */ + public void setSecKey(String secKey) { + this.secKey = secKey; + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return Unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + Unknown01 = unknown01; + } + + /** + * @return the unknown02 + */ + public int getUnknown02() { + return Unknown02; + } + + /** + * @param unknown02 + * the unknown02 to set + */ + public void setUnknown02(int unknown02) { + Unknown02 = unknown02; + } +} diff --git a/src/engine/net/client/msg/LootMsg.java b/src/engine/net/client/msg/LootMsg.java new file mode 100644 index 00000000..ec81e181 --- /dev/null +++ b/src/engine/net/client/msg/LootMsg.java @@ -0,0 +1,267 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Item; +import engine.objects.MobEquipment; + +public class LootMsg extends ClientNetMsg { + + private Item item; + private int sourceType1; + private int sourceID1; + private int targetType; + private int targetID; + private int sourceType2; + private int sourceID2; + + private int unknown01; + private int unknown02; + private int unknown03; + private int unknown04; + private int unknown05; + private byte unknown06 = (byte) 0; + private int unknown07; + private int unknown08; + + private MobEquipment mobEquipment = null; + + /** + * This is the general purpose constructor. + */ + public LootMsg(int sourceType, int sourceID, int targetType, int targetID, Item item) { + super(Protocol.MOVEOBJECTTOCONTAINER); + this.sourceType1 = sourceType; + this.sourceID1 = sourceID; + this.targetType = targetType; + this.targetID = targetID; + this.sourceType2 = sourceType; + this.sourceID2 = sourceID; + this.item = item; + this.unknown01 = 0; + this.unknown02 = 0; + this.unknown03 = 0; + this.unknown04 = 0; + this.unknown05 = 0; + this.unknown07 = 0; + this.unknown08 = 0; + } + + //for MobEquipment + + public LootMsg(int sourceType, int sourceID, int targetType, int targetID, MobEquipment mobEquipment) { + super(Protocol.MOVEOBJECTTOCONTAINER); + this.sourceType1 = sourceType; + this.sourceID1 = sourceID; + this.targetType = targetType; + this.targetID = targetID; + this.sourceType2 = sourceType; + this.sourceID2 = sourceID; + this.item = null; + this.mobEquipment = mobEquipment; + + this.unknown01 = 0; + this.unknown02 = 0; + this.unknown03 = 0; + this.unknown04 = 0; + this.unknown05 = 0; + this.unknown07 = 0; + this.unknown08 = 0; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public LootMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.MOVEOBJECTTOCONTAINER, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + if (this.item != null) + Item.serializeForClientMsgWithoutSlot(this.item,writer); + else if (this.mobEquipment != null) + try { + MobEquipment._serializeForClientMsg(this.mobEquipment,writer, false); + } catch (SerializationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + writer.putInt(this.sourceType1); + writer.putInt(this.sourceID1); + writer.putInt(this.targetType); + writer.putInt(this.targetID); + writer.putInt(this.sourceType2); + writer.putInt(this.sourceID2); + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + writer.putInt(this.unknown03); + writer.putInt(this.unknown04); + writer.putInt(this.unknown05); + writer.put(this.unknown06); + writer.putInt(this.unknown07); + writer.putInt(this.unknown08); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.item = Item.deserializeFromClientMsg(reader, false); + this.sourceType1 = reader.getInt(); + this.sourceID1 = reader.getInt(); + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + this.sourceType2 = reader.getInt(); + this.sourceID2 = reader.getInt(); + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + this.unknown03 = reader.getInt(); + this.unknown04 = reader.getInt(); + this.unknown05 = reader.getInt(); + this.unknown06 = reader.get(); + this.unknown07 = reader.getInt(); + this.unknown08 = reader.getInt(); + } + + public int getSourceType1() { + return this.sourceType1; + } + + public int getSourceID1() { + return this.sourceID1; + } + + public int getTargetType() { + return this.targetType; + } + + public int getTargetID() { + return this.targetID; + } + + public int getSourceType2() { + return this.sourceType2; + } + + public int getSourceID2() { + return this.sourceID2; + } + + public int getUnknown01() { + return this.unknown01; + } + + public int getUnknown02() { + return this.unknown02; + } + + public int getUnknown03() { + return this.unknown03; + } + + public int getUnknown04() { + return this.unknown04; + } + + public int getUnknown05() { + return this.unknown05; + } + + public byte getUnknown06() { + return this.unknown06; + } + + public int getUnknown07() { + return this.unknown07; + } + + public int getUnknown08() { + return this.unknown08; + } + + public Item getItem() { + return this.item; + } + + public void setUnknown01(int value) { + this.unknown01 = value; + } + + public void setUnknown02(int value) { + this.unknown02 = value; + } + + public void setUnknown03(int value) { + this.unknown03 = value; + } + + public void setUnknown04(int value) { + this.unknown04 = value; + } + + public void setUnknown05(int value) { + this.unknown05 = value; + } + + public void setUnknown06(byte value) { + this.unknown06 = value; + } + + public void setUnknown07(int value) { + this.unknown07 = value; + } + + public void setUnknown08(int value) { + this.unknown08 = value; + } + + public void setSourceType1(int value) { + this.sourceType1 = value; + } + + public void setSourceID1(int value) { + this.sourceID1 = value; + } + + public void setTargetType(int value) { + this.targetType = value; + } + + public void setTargetID(int value) { + this.targetID = value; + } + + public void setSourceType2(int value) { + this.sourceType2 = value; + } + + public void setSourceID2(int value) { + this.sourceID2 = value; + } + + public void setItem(Item value) { + this.item = value; + } +} diff --git a/src/engine/net/client/msg/LootWindowRequestMsg.java b/src/engine/net/client/msg/LootWindowRequestMsg.java new file mode 100644 index 00000000..5f92be1e --- /dev/null +++ b/src/engine/net/client/msg/LootWindowRequestMsg.java @@ -0,0 +1,112 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class LootWindowRequestMsg extends ClientNetMsg { + + private int sourceType; + private int sourceID; + private int targetType; + private int targetID; + + /** + * This is the general purpose constructor. + */ + public LootWindowRequestMsg(int sourceType, int sourceID, int targetType, int targetID) { + super(Protocol.REQUESTCONTENTS); + this.sourceType = sourceType; + this.sourceID = sourceID; + this.targetType = targetType; + this.targetID = targetID; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public LootWindowRequestMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.REQUESTCONTENTS, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.putInt(this.targetType); + writer.putInt(this.targetID); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + } + + /** + * @return the sourceType + */ + public int getSourceType() { + return sourceType; + } + + /** + * @return the sourceID + */ + public int getSourceID() { + return sourceID; + } + + /** + * @return the targetType + */ + public int getTargetType() { + return targetType; + } + + /** + * @return the targetID + */ + public int getTargetID() { + return targetID; + } + + public void setSourceType(int value) { + this.sourceType = value; + } + + public void setSourceID(int value) { + this.sourceID = value; + } + + public void setTargetType(int value) { + this.targetType = value; + } + + public void setTargetID(int value) { + this.targetID = value; + } + +} diff --git a/src/engine/net/client/msg/LootWindowResponseMsg.java b/src/engine/net/client/msg/LootWindowResponseMsg.java new file mode 100644 index 00000000..408ae77c --- /dev/null +++ b/src/engine/net/client/msg/LootWindowResponseMsg.java @@ -0,0 +1,105 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Item; + +import java.util.ArrayList; + + +public class LootWindowResponseMsg extends ClientNetMsg { + + private int targetType; + private int targetID; + private ArrayList inventory; + private int unknown01 = 45; + + /** + * This is the general purpose constructor. + */ + public LootWindowResponseMsg(int targetType, int targetID, ArrayList inventory) { + super(Protocol.WEIGHTINVENTORY); + this.targetType = targetType; + this.targetID = targetID; + this.inventory = inventory; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public LootWindowResponseMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.WEIGHTINVENTORY, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.put((byte) 1); + Item.putList(writer, this.inventory, false, this.targetID); + writer.putInt(this.unknown01); + writer.putInt(this.targetType); + writer.putInt(this.targetID); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + } + + public int getUnknown01() { + return this.unknown01; + } + + public int getTargetType() { + return targetType; + } + + public int getTargetID() { + return targetID; + } + + public ArrayList getInventory() { + return this.inventory; + } + + public void setUnknown01(int value) { + this.unknown01 = value; + } + + public void setTargetType(int value) { + this.targetType = value; + } + + public void setTargetID(int value) { + this.targetID = value; + } + + public void setInventory(ArrayList value) { + this.inventory = value; + } + + @Override + protected int getPowerOfTwoBufferSize() { + // Larger size for historically larger opcodes + return 17; // 2^15 == 32,768 + } +} diff --git a/src/engine/net/client/msg/ManageCityAssetsMsg.java b/src/engine/net/client/msg/ManageCityAssetsMsg.java new file mode 100644 index 00000000..85ce6dae --- /dev/null +++ b/src/engine/net/client/msg/ManageCityAssetsMsg.java @@ -0,0 +1,866 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.*; +import engine.gameManager.BuildingManager; +import engine.gameManager.ZoneManager; +import engine.math.Vector3fImmutable; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.*; +import org.joda.time.DateTime; +import org.joda.time.Period; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Open manage city asset window + */ +public class ManageCityAssetsMsg extends ClientNetMsg { + + //messageType + //C->S 2: S->C: 0, 3, 4, 6 + //C->S 15: S->C: 15 + //C->S 14: S->C: 14 + //C->S ?: S->C: 10, 11, 16 + + //C->S 2 = manage this asset + // 20 = manage entire city + + //S->C, 0 = error message + // 3 = manage asset + // 4 = no access / building info + + public int actionType; + private int targetID; + private int targetType; + private int targetType1; + private int targetType2; + private int targetType3; + private int targetID1; + private int targetID2; + private int targetID3; + public String assetName; + private String AssetName1; + public String CityName; + private int rank; + private int symbol; + public int upgradeCost; + private int unknown04; + private int unknown05; + private int unknown06; + private int unknown07; + private int unknown14; + private int unknown15; + private int unknown16; + private int unknown17; + private int unknown54; + private int preName01; + + private byte UnkByte03; + private byte UnkByte04; + private int strongbox; + + private int baneHour; + private PlayerCharacter assetManager; + private Building asset; + public byte labelProtected; + public byte labelSiege; + public byte labelCeaseFire; + public byte buttonTransfer; + public byte buttonDestroy; + public byte buttonAbandon; + public byte buttonUpgrade; + + /** + * This is the general purpose constructor + */ + public ManageCityAssetsMsg() { + super(Protocol.MANAGECITYASSETS); + this.actionType = 0; + this.targetType = 0; + this.targetID = 0; + this.preName01 = 0; + this.assetName = ""; + this.CityName = ""; + this.rank = 0; + this.symbol = 0; + this.unknown04 = 0; + this.unknown06 = 0; + this.unknown07 = 0; + this.unknown14 = 0; + this.unknown15 = 0; + this.unknown16 = 0; + this.unknown17 = 0; + + this.strongbox = 0; + + this.targetType1 = 0; + this.targetType2 = 0; + this.targetType3 = 0; + + this.targetID1 = 0; + this.targetID2 = 0; + this.targetID3 = 0; + this.UnkByte03 = 0; + this.UnkByte04 = 0; + this.AssetName1 = ""; + this.unknown54 = 0; + this.strongbox = 0; + this.upgradeCost = 0; + + this.labelProtected = 0; + this.labelSiege = 0; + this.labelCeaseFire = 0; + this.buttonTransfer = 0; + this.buttonDestroy = 0; + this.buttonAbandon = 0; + this.buttonUpgrade = 0; + + } + + public ManageCityAssetsMsg(PlayerCharacter pc, Building asset) { + super(Protocol.MANAGECITYASSETS); + this.assetManager = pc; + this.asset = asset; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public ManageCityAssetsMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.MANAGECITYASSETS, origin, reader); + } + + public int getTargetID() { + return targetID; + } + + protected int getPowerOfTwoBufferSize() { + return (20); // 2^10 == 1024 + } + public void setTargetID(int targetID) { + this.targetID = targetID; + } + + public int getTargetType() { + return targetType; + } + + public void setTargetType(int targetType) { + this.targetType = targetType; + } + + public void setTargetType3(int targetType3) { + this.targetType3 = targetType3; + } + + public void setTargetID3(int targetID3) { + this.targetID3 = targetID3; + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + actionType = reader.getInt(); + targetType = reader.getInt(); + targetID = reader.getInt(); + if (this.actionType == 20) { + reader.getInt(); + this.baneHour = reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + + } else if (this.actionType == 5) { //rename building. + reader.getInt(); + assetName = reader.getString(); + for (int i = 0; i < 5; i++) + reader.getInt(); + } else if (this.actionType == 2) { + reader.getInt(); + this.strongbox = reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + + + }else{ + for (int i = 0; i < 6; i++) + reader.getInt(); + } + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.actionType); + + if (this.actionType == 2) { + writer.putInt(asset.getObjectType().ordinal()); + writer.putInt(asset.getObjectUUID()); + writer.putInt(0); + writer.putInt(asset.reserve); + writer.putInt(0); + return; + } + + if (this.actionType == 13) { + writer.putInt(asset.getObjectType().ordinal()); + writer.putInt(asset.getObjectUUID()); + writer.putInt(0); + writer.putInt(0); + writer.putInt(asset.getHirelings().size()); + for (AbstractCharacter hireling : asset.getHirelings().keySet()){ + if (!hireling.getObjectType().equals(GameObjectType.NPC)) + writer.putString(hireling.getName()); + else{ + NPC npc = (NPC)hireling; + if (!npc.getNameOverride().isEmpty()){ + writer.putString(npc.getNameOverride()); + }else + + if (npc.getContract() != null) { + if (npc.getContract().isTrainer()) { + writer.putString(npc.getName() + ", " + npc.getContract().getName()); + } else { + writer.putString(npc.getName() + " " + npc.getContract().getName()); + } + } else { + writer.putString(npc.getName()); + } + } + } + + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + } + + //Bane window + + if (this.actionType == 11) { + writer.putInt(asset.getObjectType().ordinal()); + writer.putInt(asset.getObjectUUID()); + for (int a = 0;a<5;a++) + writer.putInt(0); + + writer.putInt(asset.getHirelings().size()); + + for (AbstractCharacter npcHire : asset.getHirelings().keySet()) { + writer.putInt(npcHire.getObjectType().ordinal()); + writer.putInt(npcHire.getObjectUUID()); + if (npcHire.getObjectType() == GameObjectType.NPC) + writer.putString(((NPC)npcHire).getContract().getName()); + else + writer.putString("Guard Captain"); + writer.putString(npcHire.getName()); + writer.putInt(1); + writer.putInt(Blueprint.getNpcMaintCost(npcHire.getRank())); + if (npcHire.getObjectType() == GameObjectType.NPC) + writer.putInt(((NPC)npcHire).getContract().getIconID()); // Was 60 + else if (npcHire.getObjectType() == GameObjectType.Mob){ + writer.putInt(((Mob)npcHire).getContract().getIconID()); // Was 60 + } + else + writer.putInt(5); + writer.put((byte) 0); + writer.put((byte) 0); + writer.put((byte) 1); + writer.put((byte) 0); + writer.put((byte) 0); + } + return; + } + + if (this.actionType == 15) { + writer.putInt(1); + writer.putInt(1); + City city = null; + Zone playerZone = ZoneManager.findSmallestZone(assetManager.getLoc()); + Set buildings = ZoneManager.findSmallestZone(assetManager.getLoc()).zoneBuildingSet; + + Building tol = null; + if (playerZone.getPlayerCityUUID() != 0) + city = City.GetCityFromCache(playerZone.getPlayerCityUUID()); + + if (city != null) + tol = city.getTOL(); + + + + writer.putInt(0); // 1 + String = custom message, cant control assets. + writer.putInt(0); + writer.putInt(0); //array + + writer.putInt(buildings.size()); + + int i = 0; + for (Building building: buildings){ + + i++; + writer.putString(building.getName()); //ARRAY + writer.putInt(building.getObjectType().ordinal()); //? + writer.putInt(building.getObjectUUID()); //? + + writer.putInt(4); + writer.putInt(4); + + writer.put((byte)0); + writer.put((byte)0); + writer.put((byte)1); + writer.put((byte)1); + + //max distance to bypass clientside check. + float maxDistance = 2000; + + + writer.putFloat(maxDistance); + + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + + if (building.getPatrolPoints() != null){ + writer.putInt(building.getPatrolPoints().size()); + for (Vector3fImmutable patrolPoint: building.getPatrolPoints()){ + writer.putVector3f(patrolPoint); + } + }else{ + writer.putInt(0); + } + writer.putInt(0); //Sentry Point + + if (building.getBlueprint() != null && building.getBlueprint().getBuildingGroup() == BuildingGroup.BARRACK){ + writer.putInt(1); //Tab left Random Town? //Opens up 16 Bytes + writer.putInt(4); + writer.putInt(0); + writer.putInt(0); + writer.putInt(4); + }else + writer.putInt(0); + writer.putInt(0); //array with size 32 bytes. // Adds information of building + } + writer.putInt(0); //ARRAY + writer.putInt(0); + } + + if (this.actionType == 18) { + Zone zone = asset.getParentZone(); + + if (zone == null) + return; + + City banedCity = City.getCity(zone.getPlayerCityUUID()); + + if (banedCity == null) + return; + + Bane bane = banedCity.getBane(); + + if (bane == null) + return; + + Guild attackerGuild = bane.getOwner().getGuild(); + + if (attackerGuild == null) + return; + + writer.putInt(asset.getObjectType().ordinal()); + writer.putInt(asset.getObjectUUID()); + + writer.putInt(0); + writer.putString(attackerGuild.getName()); + writer.putString(Guild.GetGL(attackerGuild).getName()); + writer.putInt(bane.getSiegePhase().ordinal()); //1 challenge //2 standoff //3 war + writer.put((byte) 0); + + if (!bane.isAccepted() && this.assetManager.getGuild() == banedCity.getGuild() && GuildStatusController.isInnerCouncil(this.assetManager.getGuildStatus())) + writer.put((byte) 1); //canSetTime + else + writer.put((byte) 0); + + DateTime placedOn = bane.getLiveDate(); + + if (placedOn == null) + placedOn = new DateTime(DateTime.now()); + + //set Calander to date of bane live. + DateTime now = DateTime.now(); + DateTime defaultTime = new DateTime(bane.getPlacementDate()); + DateTime playerEnterWorldTime = new DateTime(this.assetManager.getTimeStamp("EnterWorld")); + Period period = new Period(playerEnterWorldTime.getMillis(), now.getMillis()); + int hoursLoggedIn = period.getHours(); + hoursLoggedIn = hoursLoggedIn < 0 ? 0 : hoursLoggedIn; + + defaultTime = defaultTime.plusDays(2); + defaultTime = defaultTime.hourOfDay().setCopy(22); + defaultTime = defaultTime.minuteOfHour().setCopy(0); + defaultTime = defaultTime.secondOfMinute().setCopy(0); + + long curTime = now.getMillis(); + long timeLeft = 0; + + if (bane.getLiveDate() != null) + timeLeft = bane.getLiveDate().getMillis() - curTime; + else + timeLeft = defaultTime.getMillis() - curTime + 1000; + + //DO not touch these. They are static formula's until i get the correct converter for SB Time. + + writer.put((byte) placedOn.dayOfMonth().get()); + writer.put((byte) placedOn.monthOfYear().get()); + writer.putInt(placedOn.year().get() - 1900); + writer.put((byte) 0); + writer.put((byte) 0); + writer.put((byte) 0); + + if (timeLeft < 0) + writer.putInt(0); + else + writer.putInt((int) timeLeft / 1000); // Time remaing until bane/Seconds + + if (attackerGuild.getGuildState() == GuildState.Sworn) + writer.putInt(4); //3 capture/errant,4 capture/sworn, 5 destroy/soveirgn. + else + writer.putInt(5); + + writer.put((byte) (16 - hoursLoggedIn)); // hour start + writer.put((byte) (24 - hoursLoggedIn)); // hour end + writer.put((byte) 2); + writer.putString(banedCity.getCityName()); + writer.putString(Guild.GetGL(bane.getOwner().getGuild()) != null ? Guild.GetGL(bane.getOwner().getGuild()).getName() : "No Guild Leader"); + GuildTag._serializeForDisplay(attackerGuild.getGuildTag(),writer); + GuildTag._serializeForDisplay(attackerGuild.getNation().getGuildTag(),writer); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + } + + if (this.actionType == 3) { + + writer.putInt(targetType); + writer.putInt(targetID); + + Guild nation = null; + Building building = BuildingManager.getBuildingFromCache(targetID); + Guild guild = building.getGuild(); + Zone zone = ZoneManager.findSmallestZone(building.getLoc()); + + writer.putInt(0);//unknown Might be to allow manager to open or not! + writer.putString(building.getName()); + + AbstractCharacter buildingOwner = building.getOwner(); + + if (buildingOwner == null) + writer.putString("Morloch"); + else + writer.putString(buildingOwner.getName()); + + if (zone == null) + writer.putString("Forlord"); + else + writer.putString(zone.getName()); + + writer.putString(building.getGuild().getName()); + + writer.putInt(building.getRank()); + + // Maintenance costs include resource if + // this structure is an R8 tree + + if (building.getRank() == 8) + writer.putInt(5); // Resources included + else + writer.putInt(1); // Gold only + + writer.putInt(2308551); //Gold + if (building.getBlueprint() == null) + writer.putInt(0); + else + writer.putInt(building.getBlueprint().getMaintCost(building.getRank())); // maint cost + + if (building.getRank() == 8) { + writer.putInt(74856115); // Stone + writer.putInt(1500); // maint cost + writer.putInt(-1603256692); // Lumber + writer.putInt(1500); // maint cost + writer.putInt(-1596311545); // Galvor + writer.putInt(5); // maint cost + writer.putInt(1532478436); // Wormwood + writer.putInt(5); // maint cost + } + + LocalDateTime maintDate = building.maintDateTime; + + if (maintDate == null) + maintDate = LocalDateTime.now(); + writer.putLocalDateTime(LocalDateTime.now()); // current time + + // utc offset? + writer.putInt((int)java.time.Duration.between(LocalDateTime.now(), maintDate).getSeconds()); // Seconds to maint date + + writer.putInt(building.getStrongboxValue()); + writer.putInt(building.reserve);//reserve Sets the buildings reserve display + writer.putInt(0);//prosperity under maintenance (wtf is prosperity?) + writer.putInt(10); + writer.putFloat((float) .1); + + if (this.buttonUpgrade == 1) { + if (building.getBlueprint() == null) + this.upgradeCost = Integer.MAX_VALUE; + else + if (building.getRank() == building.getBlueprint().getMaxRank()) + this.upgradeCost = Integer.MAX_VALUE; + else + this.upgradeCost = building.getBlueprint().getRankCost(Math.min(building.getRank() + 1, 7)); + + writer.putInt(this.upgradeCost); + } + else + writer.putInt(0); + + LocalDateTime uc = LocalDateTime.now(); + + if (building.getDateToUpgrade() != null) + uc = building.getDateToUpgrade(); + + long timeLeft = uc.atZone(ZoneId.systemDefault()) + .toInstant().toEpochMilli() - System.currentTimeMillis(); + long hour = timeLeft / 3600000; + long noHour = timeLeft - (hour * 3600000); + long minute = noHour / 60000; + long noMinute = noHour - (minute * 60000); + long second = noMinute / 1000; + + writer.put((byte) 0);//Has to do with repair time. A 1 here puts 23.9 hours in repair time A 2 here is 1.9 days + writer.put((byte) 0);//unknown + writer.putInt(0); //unknown + + if (LocalDateTime.now().isAfter(uc)) { + writer.put((byte) 0); + writer.put((byte) 0); + writer.put((byte) 0); + } + else { + writer.put((byte) (hour)); + writer.put((byte) minute); + writer.put((byte) second); + } + + if (timeLeft < 0) + writer.putInt(0); + else + writer.putInt((int) timeLeft); + + writer.putInt((int) building.getCurrentHitpoints()); + writer.putInt((int) building.getMaxHitPoints()); + writer.putInt(BuildingManager.GetRepairCost(building));//sets the repair cost. + writer.putInt(0);//unknown + + if (building.getBlueprint() == null) + writer.putInt(0); + else + writer.putInt(building.getBlueprint().getSlotsForRank(building.getRank())); + writer.put((byte) 1);//Has to do with removing update timer and putting in cost for upgrade + + writer.put(labelProtected); // 1 sets protection to invulnerable. + writer.put(labelSiege);// 1 sets the protection under siege + writer.put(labelCeaseFire); //0 with 1 set above sets to under siege // 1 with 1 set above sets protection status to under siege(cease fire) + + writer.put(buttonTransfer);// 1 enables the transfer asset button + writer.put(buttonDestroy);// 1 enables the destroy asset button + writer.put(buttonAbandon);// 1 here enables the abandon asset button + writer.put(buttonUpgrade); //disable upgrade building + + if (building.getBlueprint() == null) + writer.putInt(0); + else + writer.putInt(building.getBlueprint().getIcon()); //Symbol + + if (guild == null) { + for (int i = 0; i < 3; i++) + writer.putInt(16); + for (int i = 0; i < 2; i++) + writer.putInt(0); + } + else { + GuildTag._serializeForDisplay(guild.getGuildTag(),writer); + nation = guild.getNation(); + } + + if (nation == null) { + for (int i = 0; i < 3; i++) + writer.putInt(16); + for (int i = 0; i < 2; i++) + writer.putInt(0); + } + else { + GuildTag._serializeForDisplay(nation.getGuildTag(),writer); + } + writer.putInt(0); + writer.putInt(0); + writer.putInt(0);//1 makes it so manage window does not open. + + if (!building.assetIsProtected() && !building.getProtectionState().equals(ProtectionState.PENDING)){ + writer.putInt(0); + } + else{ + writer.putInt(1); //kos on/off? + writer.putInt(3); // was 3 + if (zone.getPlayerCityUUID() != 0 && asset.assetIsProtected()){ + writer.putInt(GameObjectType.Building.ordinal()); + writer.putInt(City.getCity(zone.getPlayerCityUUID()).getTOL().getObjectUUID()); + } + else{ + writer.putInt(0); + writer.putInt(0); + } + + writer.putInt(0); + writer.putInt(0); + + writer.putInt(targetType3); + writer.putInt(targetID3); + + + if (building.getProtectionState() == ProtectionState.PENDING) + writer.put((byte)1); //Accept or decline. + else + writer.put((byte)0); + + if (building.taxType == TaxType.NONE) + writer.put((byte)0); //? ?? + else if(building.taxDateTime != null) + writer.put((byte)1); + else + writer.put((byte)0); + + writer.putString(""); //tree of life protection tax + writer.putInt(0); //?? + writer.putInt(0); //?? + if (building.taxType == TaxType.NONE){ + writer.putInt(0); + writer.putInt(0); + }else if (building.taxType == TaxType.WEEKLY){ + writer.putInt(building.taxAmount); + writer.putInt(0); + }else{ + writer.putInt(0); + writer.putInt(building.taxAmount); + } + + + writer.put(building.enforceKOS ? (byte)1:0); //enforceKOS + writer.put((byte) 0); //? + writer.putInt(1); + } + + + + ConcurrentHashMap npcList = building.getHirelings(); + writer.putInt(npcList.size()); + if (npcList.size() > 0) { + for (AbstractCharacter npcHire : npcList.keySet()) { + writer.putInt(npcHire.getObjectType().ordinal()); + if (npcHire.getObjectType() == GameObjectType.Mob) + writer.putInt(((Mob)npcHire).getDBID()); + else + writer.putInt(npcHire.getObjectUUID()); + if (npcHire.getObjectType() == GameObjectType.NPC) + writer.putString(((NPC)npcHire).getContract().getName()); + else + writer.putString("Guard Captain"); + writer.putString(npcHire.getName()); + writer.putInt(npcHire.getRank()); + writer.putInt(Blueprint.getNpcMaintCost(npcHire.getRank())); + if (npcHire.getObjectType() == GameObjectType.NPC) + writer.putInt(((NPC)npcHire).getContract().getIconID()); // Was 60 + else if (npcHire.getObjectType() == GameObjectType.Mob) + writer.putInt(((Mob)npcHire).getContract().getIconID()); // Was 60 + + int contractID = 0; + + + if (npcHire.getObjectType() == GameObjectType.Mob) + contractID = ((Mob)npcHire).getContract().getContractID(); + else if (npcHire.getObjectType() == GameObjectType.NPC) + contractID = ((NPC)npcHire).getContract().getContractID(); + + if (contractID ==830){ + writer.putInt(24580); + } + else if (building.getBlueprint() != null && (building.getBlueprint().getBuildingGroup() == BuildingGroup.FORGE ||building.getBlueprint().getBuildingGroup() == BuildingGroup.MAGICSHOP||building.getBlueprint().getBuildingGroup() == BuildingGroup.TAILOR)){ + + writer.put((byte)0); + writer.put((byte)4); + writer.put((byte)128); + writer.put((byte)0); + + }else{ + writer.put((byte)0); + if (building.getBlueprint() != null && building.getBlueprint().getBuildingGroup() == BuildingGroup.BARRACK) + writer.put((byte)1); + else + writer.put((byte)0); + writer.put((byte)0); + writer.put((byte)0); + } + + + if (!npcHire.isAlive()){ + writer.put((byte) 1); // 1 SHOWs respawning + writer.putInt(10); // Seconds in respawn. + writer.putInt(20); + } + else + writer.put((byte)0); + + } + } + } + if (this.actionType == 4) { + writer.putInt(targetType); + writer.putInt(targetID); + Building building = BuildingManager.getBuildingFromCache(targetID); + + writer.putInt(preName01); + writer.putString(building.getName()); //assetName + writer.putString(building.getOwnerName()); //ownerName + writer.putString(building.getGuild().getName());//guild name + writer.putString(building.getCityName()); //City Name + writer.putInt(building.getRank()); + if (building.getBlueprint() == null) + writer.putInt(0); + else + writer.putInt(building.getBlueprint().getIcon()); + + //tags + GuildTag._serializeForDisplay(building.getGuild().getGuildTag(), writer); + GuildTag._serializeForDisplay(building.getGuild().getNation().getGuildTag(), writer); + + writer.putInt(unknown14); + writer.putInt(unknown15); + writer.putInt(unknown16); + writer.putInt(unknown17); + writer.putInt(0); // previously uninitialized unknown18 + } + } + + public int getRank() { + return rank; + } + + public void setRank(int rank) { + this.rank = rank; + } + + public int getSymbol() { + return symbol; + } + + public void setSymbol(int symbol) { + this.symbol = symbol; + } + + public int getUnknown04() { + return unknown04; + } + + public void setUnknown04(int unknown04) { + this.unknown04 = unknown04; + } + + public int getUnknown05() { + return unknown05; + } + + public void setUnknown05(int unknown05) { + this.unknown05 = unknown05; + } + + public int getUnknown06() { + return unknown06; + } + + public void setUnknown06(int unknown06) { + this.unknown06 = unknown06; + } + + public void setUnknown07(int unknown07) { + this.unknown07 = unknown07; + } + + public String getAssetName() { + return assetName; + } + + public void setAssetName(String AssetName) { + this.assetName = AssetName; + } + + public void setAssetName1(String AssetName1) { + this.AssetName1 = AssetName1; + } + + public String getCityName() { + return CityName; + } + + public void setUnknown54(int unknown54) { + this.unknown54 = unknown54; + } + + public int getStrongbox() { + return strongbox; + } + + public void setStrongbox(int strongbox) { + this.strongbox = strongbox; + } + + public int getBaneHour() { + return baneHour; + } + + public Building getAsset() { + return asset; + } + + public void setAsset(Building asset) { + this.asset = asset; + } + +} + +//Debug Info +//Run: Failed to make object TEMPLATE:135700 INSTANCE:1717987027141... (t=50.46) (r=7/4/2011 11:56:39) +//C:\ArcanePrime\Main_Branch\Shadowbane\Source\ArcObjectLoader.cpp(466):ERROR: ArcObjectLoader::Run: Failed to make object TEMPLATE:108760 INSTANCE:1717987027161... (t=50.46) (r=7/4/2011 11:56:39) +//C:\ArcanePrime\Main_Branch\Shadowbane\Source\ArcObjectLoader.cpp(466):ERROR: ArcObjectLoader::Run: Failed to make object TEMPLATE:108760 INSTANCE:1717987027177... (t=50.67) (r=7/4/2011 11:56:39) +//C:\ArcanePrime\Main_Branch\Shadowbane\Source\ArcObjectLoader.cpp(466):ERROR: ArcObjectLoader::Run: Failed to make object TEMPLATE:60040 INSTANCE:1717987027344... (t=50.87) (r=7/4/2011 11:56:39) +//C:\ArcanePrime\Main_Branch\Shadowbane\Source\ArcObjectLoader.cpp(466):ERROR: ArcObjectLoader::Run: Failed to make object TEMPLATE:3 INSTANCE:1717987027164... (t=50.88) (r=7/4/2011 11:56:39) + diff --git a/src/engine/net/client/msg/ManageNPCMsg.java b/src/engine/net/client/msg/ManageNPCMsg.java new file mode 100644 index 00000000..07b995ac --- /dev/null +++ b/src/engine/net/client/msg/ManageNPCMsg.java @@ -0,0 +1,1462 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.Enum.MinionType; +import engine.Enum.ProtectionState; +import engine.gameManager.PowersManager; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.*; +import engine.powers.EffectsBase; +import org.joda.time.DateTime; +import org.joda.time.Period; +import org.joda.time.Seconds; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Order NPC + */ +public class ManageNPCMsg extends ClientNetMsg { + + private int targetType; + private int targetID; + private int unknown03; + private int unknown04; + private int unknown05; + private int unknown06; + + private int unknown07; + private int unknown08; + private int unknown09; + private int unknown10; + private int unknown11; + private int buyNormal; + private int buyGuild; + private int buyNation; + private int sellNormal; + private int sellGuild; + private int sellNation; + + private String CityName; + private String OwnerName; + private String GuildName; + private int unknown12; + + private int unknown13; + private int unknown14; + private int unknown15; + private int unknown16; + private int unknown17; + private int unknown18; + + private int messageType; + + private int unknown19; //Arraylist motto length? + private String motto; //motto Length 60 max? + + private int unknown01; + + private int buildingID; + private int unknown20; + private int unknown21; + private int unknown22; + private int unknown23; + private int unknown24; + private int unknown25; + private int unknown26; + private int unknown28; + private int unknown30; + private int unknown31; + private int unknown32; + private int unknown33; + private int unknown34; + private int unknown35; + private int unknown36; + private int unknown37; + private int unknown38; + private int unknown39; + private int unknown40; + private int unknown41; + private int unknown42; + private int unknown43; + private int unknown44; + private int unknown45; + private int unknown46; + private int unknown47; + private int unknown48; + private int unknown49; + private int unknown50; + private int unknown51; + private int unknown52; + private int unknown53; + private int unknown54; + private int unknown55; + private int unknown56; + private int unknown57; + private int unknown58; + private int unknown59; + private int unknown60; + private int unknown61; + private int unknown62; + private int unknown63; + private int unknown64; + private int unknown65; + private int unknown66; + private int unknown67; + private int unknown68; + private int unknown69; + private int unknown70; + private int unknown71; + private int unknown72; + private int unknown73; + private int unknown74; + private int unknown75; + private int unknown76; + private int unknown77; + private int unknown78; + private int unknown79; + private int unknown80; + private int unknown81; + private int unknown82; + private int unknown83; + + /** + * This is the general purpose constructor + */ + public ManageNPCMsg(AbstractCharacter ac) { + super(Protocol.MANAGENPC); + this.targetType = ac.getObjectType().ordinal(); + this.targetID = ac.getObjectUUID(); + this.buyGuild = 26; //TODO pull all these from the NPC object + this.buyNation = 26; + this.buyNormal = 26; + this.sellGuild = 100; + this.sellNation = 100; + this.sellNormal = 100; + this.messageType = 1; //This seems to be the "update Hireling window" value flag + + //Unknown defaults... + this.unknown20 = 0; + this.unknown21 = 0; + this.unknown22 = 0; + this.unknown23 = 0; + this.unknown24 = 0; + this.unknown25 = 0; + this.unknown26 = 0; + this.unknown28 = 0; + this.unknown30 = 0; + this.unknown31 = 0; + this.unknown32 = 0; + this.unknown33 = 0; + this.unknown34 = 0; + this.unknown35 = 0; + this.unknown36 = 0; + this.unknown37 = 1;//1 + this.unknown38 = 0;//0 + this.unknown39 = 0;//0 + this.unknown40 = 1;//1 + this.unknown41 = 0;//0 + this.unknown42 = 1;//1 [Toggles tree icon in protection slots] + this.unknown43 = 0;//0 + this.unknown44 = 1;//1 + this.unknown45 = 0;//0 + this.unknown46 = 0;//0 + this.unknown47 = 0;//0 + this.unknown48 = 0;//0 + this.unknown49 = 0;//0 + this.unknown50 = 0;//0 + this.unknown51 = 0;//0 + this.unknown52 = 0;//0 + this.unknown53 = 0;//0 + this.unknown54 = 1;//1 + this.unknown55 = 0;//0 + this.unknown56 = 3;//3 + this.unknown57 = 3;//3 + this.unknown58 = 0;//0 + this.unknown59 = 5;//5 + this.unknown60 = 0;//0 + this.unknown61 = 0;//0 + this.unknown62 = 0;//0 + this.unknown63 = 64;//64 + this.unknown64 = 0;//0 + this.unknown65 = 0;//0 + this.unknown66 = 0;//0 + this.unknown67 = 0;//0 + this.unknown68 = 1;//1 + this.unknown69 = 1;//1 + this.unknown70 = 0;//0 + this.unknown71 = 1;//1 + this.unknown72 = 0; + this.unknown73 = 0; + this.unknown74 = 5; + this.unknown75 = 1; + this.unknown76 = 2; + this.unknown77 = 15; + this.unknown78 = 3; + this.unknown79 = 18; + this.unknown80 = 0; + this.unknown81 = 0; + this.unknown82 = 0; + this.unknown83 = 0; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ManageNPCMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.MANAGENPC, origin, reader); + } + + @Override + protected int getPowerOfTwoBufferSize() { + return (19); // 2^10 == 1024 + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + //TODO do we need to do anything here? Does the client ever send this message to the server? + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + Period upgradePeriod; + int upgradePeriodInSeconds; + + try{ + + + writer.putInt(messageType); //1 + if (messageType == 5) { + writer.putInt(unknown20);//0 + writer.putInt(targetType); + writer.putInt(targetID); + + writer.putInt(GameObjectType.Building.ordinal()); + writer.putInt(buildingID); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + + } else if (messageType == 1) { + NPC npc = null; + Mob mobA = null; + + if (this.targetType == GameObjectType.NPC.ordinal()){ + + npc = NPC.getFromCache(this.targetID); + + if (npc == null) { + Logger.error("Missing NPC of ID " + this.targetID); + return; + } + + Contract contract = null; + contract = npc.getContract(); + + if (contract == null) { + Logger.error("Missing contract for NPC " + this.targetID); + return; + } + + writer.putInt(0); //anything other than 0 seems to mess up the client + writer.putInt(targetType); + writer.putInt(targetID); + writer.putInt(0); //static.... + writer.putInt(0);//static.... + writer.putInt(Blueprint.getNpcMaintCost(npc.getRank())); // salary + + writer.putInt(npc.getUpgradeCost()); + + if (npc.isRanking() && npc.getUpgradeDateTime().isAfter(DateTime.now())) + upgradePeriod = new Period(DateTime.now(), npc.getUpgradeDateTime()); + else + upgradePeriod = new Period(0); + + writer.put((byte) upgradePeriod.getDays()); //for timer + writer.put((byte) unknown26);//unknown + writer.putInt(100); //unknown + + writer.put((byte) upgradePeriod.getHours()); //for timer + writer.put((byte) upgradePeriod.getMinutes()); //for timer + writer.put((byte) upgradePeriod.getSeconds()); //for timer + + if (npc.isRanking() && npc.getUpgradeDateTime().isAfter(DateTime.now())) + upgradePeriodInSeconds = Seconds.secondsBetween(DateTime.now(), npc.getUpgradeDateTime()).getSeconds(); + else + upgradePeriodInSeconds = 0; + + writer.putInt(upgradePeriodInSeconds); + + writer.put((byte) 0); + writer.put((byte) (npc.getRank() == 7 ? 0 : 1)); //0 will make the upgrade field show "N/A" + writer.put((byte) 0); + writer.put((byte) 0); + writer.putInt(0); + writer.putInt(10000); //no idea... + writer.put((byte) 0); + writer.put((byte) 0); + writer.put((byte) 0); + writer.putInt(0); + + NPCProfits profit = NPC.GetNPCProfits(npc); + + if (profit == null) + profit = NPCProfits.defaultProfits; + //adding .000000001 to match client. + int buyNormal = (int) ((profit.buyNormal + .000001f) * 100); + int buyGuild = (int) ((profit.buyGuild + .000001f) *100); + int buyNation = (int) ((profit.buyNation + .000001f) * 100); + + int sellNormal = (int) ((profit.sellNormal + .000001f) * 100); + int sellGuild = (int) ((profit.sellGuild + .000001f) * 100); + int sellNation = (int) ((profit.sellNation + .000001f) * 100); + + writer.putInt(buyNormal); + writer.putInt(buyGuild); + writer.putInt(buyNation); + writer.putInt(sellNormal); + writer.putInt(sellGuild); + writer.putInt(sellNation); + + if (contract.isRuneMaster()) { + writer.putInt(0); //vendor slots + writer.putInt(0); //artillery slots + + //figure out number of protection slots based on building rank + int runemasterSlots = (2 * npc.getRank()) + 6; + + writer.putInt( runemasterSlots); + + for (int i = 0; i < 13; i++) { + writer.putInt(0); //statics + } + //some unknown list + writer.putInt(4); //list count + writer.putInt(17); + writer.putInt(2); + writer.putInt(12); + writer.putInt(23); + + writer.putInt(0); //static + writer.putInt(0); //static + + //TODO add runemaster list here + + ArrayList buildingList = npc.getProtectedBuildings(); + + writer.putInt(buildingList.size()); + + for (Building b : buildingList) { + writer.putInt(3); + writer.putInt(b.getObjectType().ordinal()); + writer.putInt(b.getObjectUUID()); + + writer.putInt(npc.getParentZone().getObjectType().ordinal()); + writer.putInt(npc.getParentZone().getObjectUUID()); + + writer.putLong(0); //TODO Identify what Comp this is suppose to be. + if (b.getProtectionState() == ProtectionState.PENDING) + writer.put((byte)1); + else + writer.put((byte)0); + writer.put((byte)0); + writer.putString(b.getName()); + writer.putInt(1);//what? + writer.putInt(1);//what? + //taxType = b.getTaxType() + switch(b.taxType){ + case NONE: + writer.putInt(0); + writer.putInt(0); + break; + case WEEKLY: + writer.putInt(b.taxAmount); + writer.putInt(0); + break; + case PROFIT: + writer.putInt(0); + writer.putInt(b.taxAmount); + break; + + } + writer.put(b.enforceKOS ? (byte)1:0); //ENFORCE KOS + writer.put((byte)0); //?? + writer.putInt(1); + } + + writer.putInt(0); //artillery captain list + + } else if (contract.isArtilleryCaptain()) { + int slots = 1; + if (contract.getContractID() == 839) + slots = 3; + + + writer.putInt(0); //vendor slots + writer.putInt(slots); //artillery slots + writer.putInt(0); //runemaster slots + + for (int i = 0; i < 13; i++) { + writer.putInt(0); //statics + } + //some unknown list + writer.putInt(1); //list count + writer.putInt(16); + + writer.putInt(0); //static + writer.putInt(0); //static + writer.putInt(0); //runemaster list + + //artillery captain list + ConcurrentHashMap siegeMinions = npc.getSiegeMinionMap(); + writer.putInt(1 + siegeMinions.size()); + serializeBulwarkList(writer, 1); //Trebuchet + //serializeBulwarkList(writer, 2); //Ballista + + if (siegeMinions != null && siegeMinions.size() > 0) + + for (Mob mob : siegeMinions.keySet()) { + this.unknown83 = mob.getObjectUUID(); + writer.putInt(2); + writer.putInt(mob.getObjectType().ordinal()); + writer.putInt(this.unknown83); + writer.putInt(0); + writer.putInt(10); + writer.putInt(0); + writer.putInt(1); + writer.putInt(1); + writer.put((byte) 0); + long curTime = System.currentTimeMillis() / 1000; + long upgradeTime = mob.getTimeToSpawnSiege() / 1000; + long timeLife = upgradeTime - curTime; + + writer.putInt(900); + writer.putInt(900); + writer.putInt((int) timeLife); //time remaining? + writer.putInt(0); + writer.put((byte)0); + writer.putString(mob.getName()); + writer.put((byte) 0); + } + return; + + }else{ + + if (Contract.NoSlots(npc.getContract())) + writer.putInt(0); + else + writer.putInt(npc.getRank()); //vendor slots + writer.putInt(0); //artilerist slots + writer.putInt(0); //runemaster slots + + writer.putInt(1); //is this static? + for (int i = 0; i < 4; i++) { + writer.putInt(0); //statics + } + //Begin Item list for creation. + writer.putInt(npc.getCanRoll().size()); + + for (Integer ib : npc.getCanRoll()) { + ItemBase item = ItemBase.getItemBase(ib); + writer.put((byte) 1); + writer.putInt(0); + writer.putInt(ib); //itemID + writer.putInt(item.getBaseValue()); + writer.putInt(600); + writer.put((byte) 1); + writer.put((byte) item.getModTable()); + writer.put((byte) item.getModTable()); + writer.put((byte) item.getModTable()); + writer.put((byte) item.getModTable());//EffectItemType + } + ArrayList itemList = npc.getRolling(); + + if (itemList.isEmpty()) + writer.putInt(0); + else { + if (itemList.size() < npc.getRank()) + writer.putInt(itemList.size()); + else + writer.putInt(npc.getRank()); + for (Item i : itemList) { + if (itemList.indexOf(i) >= npc.getRank()) + break; + ItemBase ib = i.getItemBase(); + writer.put((byte) 0); // ? Unknown45 + writer.putInt(i.getObjectType().ordinal()); + writer.putInt(i.getObjectUUID()); + + writer.putInt(0); + writer.putInt(i.getItemBaseID()); + writer.putInt(ib.getBaseValue()); + long curTime = System.currentTimeMillis() / 1000; + long upgradeTime = i.getDateToUpgrade() / 1000; + long timeLife = i.getDateToUpgrade() - System.currentTimeMillis(); + + timeLife /= 1000; + writer.putInt((int) timeLife); + writer.putInt(npc.getRollingTimeInSeconds(i.getItemBaseID())); + writer.putInt(1); + if (i.isComplete()) + writer.put((byte) 1); + else + writer.put((byte) 0); + + ArrayList effectsList = i.getEffectNames(); + EffectsBase prefix = null; + EffectsBase suffix = null; + + for (String effectName: effectsList){ + if (effectName.contains("PRE")) + prefix = PowersManager.getEffectByIDString(effectName); + if (effectName.contains("SUF")) + suffix = PowersManager.getEffectByIDString(effectName); + + } + + if ((prefix == null && suffix == null)) + writer.putInt(0); + else + writer.putInt(-1497023830); + if ((prefix != null && !i.isRandom()) || (prefix != null && i.isComplete())) + writer.putInt(prefix.getToken()); + else + writer.putInt(0); + if ((suffix != null && !i.isRandom()) || (suffix != null && i.isComplete())) + writer.putInt(suffix.getToken()); + else + writer.putInt(0); + writer.putString(i.getCustomName()); + } + } + + writer.putInt(0); + writer.putInt(0); + writer.putInt(1); + writer.putInt(0); + writer.putInt(3); + writer.putInt(3); + writer.putInt(0); + writer.putString("Repair items"); + writer.putString("percent"); + writer.putInt(npc.getRepairCost()); //cost for repair + writer.putInt(0); + //ArrayList modSuffixList = + ArrayList modPrefixList = npc.getModTypeTable(); + Integer mod = modPrefixList.get(0); + + if (mod != 0) { + writer.putInt(npc.getModTypeTable().size()); //Effects size + for (Integer mtp : npc.getModTypeTable()) { + + Integer imt = modPrefixList.indexOf(mtp); + writer.putInt(npc.getItemModTable().get(imt)); //? + writer.putInt(0); + writer.putInt(0); + writer.putFloat(2); + writer.putInt(0); + writer.putInt(1); + writer.putInt(2); + writer.putInt(0); + writer.putInt(1); + writer.put(npc.getItemModTable().get(imt)); + writer.put(npc.getItemModTable().get(imt)); + writer.put(npc.getItemModTable().get(imt)); + writer.put(npc.getItemModTable().get(imt));//writer.putInt(-916801465); effectItemType + writer.putInt(mtp); //prefix + Integer mts = modPrefixList.indexOf(mtp); + writer.putInt(npc.getModSuffixTable().get(mts)); //suffix + } + } else + writer.putInt(0); + ArrayList inventory = npc.getInventory(); + + + writer.putInt(inventory.size()); //placeholder for item cnt + + + + for (Item i : inventory) { + + Item.serializeForClientMsgWithoutSlot(i,writer); + } + + + writer.putInt(0); + writer.putInt(5); + writer.putInt(1); + writer.putInt(2); + writer.putInt(15); + writer.putInt(3); + writer.putInt(18); + + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + } + + }else if (this.targetType == GameObjectType.Mob.ordinal()){ + + mobA = Mob.getFromCacheDBID(this.targetID); + if (mobA == null) { + Logger.error("Missing Mob of ID " + this.targetID); + return; + } + + if (mobA != null){ + Contract con = mobA.getContract(); + if (con == null) { + Logger.error("Missing contract for NPC " + this.targetID); + return; + } + + int maxSlots = 1; + + switch (mobA.getRank()){ + case 1: + case 2: + maxSlots = 1; + break; + case 3: + maxSlots = 2; + break; + case 4: + case 5: + maxSlots = 3; + break; + case 6: + maxSlots = 4; + break; + case 7: + maxSlots = 5; + break; + default: + maxSlots = 1; + + } + writer.putInt(0); //anything other than 0 seems to mess up the client + writer.putInt(targetType); + writer.putInt(targetID); + writer.putInt(0); //static.... + writer.putInt(0);//static.... + writer.putInt(Blueprint.getNpcMaintCost(mobA.getRank())); // salary + + writer.putInt(Mob.getUpgradeCost(mobA)); + + if (mobA.isRanking() && mobA.getUpgradeDateTime().isAfter(DateTime.now())) + upgradePeriod = new Period(DateTime.now(), mobA.getUpgradeDateTime()); + else + upgradePeriod = new Period(0); + + writer.put((byte) upgradePeriod.getDays()); //for timer + writer.put((byte) unknown26);//unknown + writer.putInt(100); //unknown + + writer.put((byte) upgradePeriod.getHours()); //for timer + writer.put((byte) upgradePeriod.getMinutes()); //for timer + writer.put((byte) upgradePeriod.getSeconds()); //for timer + + if (mobA.isRanking() && mobA.getUpgradeDateTime().isAfter(DateTime.now())) + upgradePeriodInSeconds = Seconds.secondsBetween(DateTime.now(), mobA.getUpgradeDateTime()).getSeconds(); + else + upgradePeriodInSeconds = 0; + + writer.putInt(upgradePeriodInSeconds); + + + writer.put((byte) 0); + writer.put((byte) (mobA.getRank() == 7 ? 0 : 1)); //0 will make the upgrade field show "N/A" + writer.put((byte) 0); + writer.put((byte) 0); + writer.putInt(0); + writer.putInt(10000); //no idea... + writer.put((byte) 0); + writer.put((byte) 0); + writer.put((byte) 0); + writer.putInt(0); + + + NPCProfits profit = NPCProfits.defaultProfits; + + writer.putInt((int) (profit.buyNormal * 100)); + writer.putInt((int) (profit.buyGuild * 100)); + writer.putInt((int) (profit.buyNation * 100)); + writer.putInt((int) (profit.sellNormal * 100)); + writer.putInt((int) (profit.sellGuild * 100)); + writer.putInt((int) (profit.sellNation * 100)); + + writer.putInt(0); //vendor slots + writer.putInt(maxSlots); //artillery slots + writer.putInt(0); //runemaster slots + + for (int i = 0; i < 13; i++) { + writer.putInt(0); //statics + } + //some unknown list + writer.putInt(1); //list count + writer.putInt(16); + + writer.putInt(0); //static + writer.putInt(0); //static + writer.putInt(0); //runemaster list + + //artillery captain list + ConcurrentHashMap siegeMinions = mobA.getSiegeMinionMap(); + + + writer.putInt(siegeMinions.size() + 1); + serializeGuardList(writer, mobA.getContract().getContractID()); //Guard + + if (siegeMinions != null && siegeMinions.size() > 0) + + for (Mob mob : siegeMinions.keySet()) { + this.unknown83 = mob.getObjectUUID(); + writer.putInt(2); + writer.putInt(mob.getObjectType().ordinal()); + writer.putInt(this.unknown83); + writer.putInt(0); + writer.putInt(10); + writer.putInt(0); + writer.putInt(1); + writer.putInt(1); + writer.put((byte) 0); + long curTime = System.currentTimeMillis() / 1000; + long upgradeTime = mob.getTimeToSpawnSiege() / 1000; + long timeLife = upgradeTime - curTime; + + writer.putInt(900); + writer.putInt(900); + writer.putInt((int) timeLife); //time remaining? + writer.putInt(0); + writer.put((byte)0); + writer.putString(mob.getNameOverride().isEmpty() ? mob.getName() : mob.getNameOverride()); + writer.put((byte) 0); + } + + } + + + } + + } + + }catch(Exception e){ + e.printStackTrace(); + } + + + } + + + + //Serializes lists for Bulwarks + private static void serializeBulwarkList(ByteBufferWriter writer, int minion) { + int minionIndex; + + if (minion < 1 || minion > 3) + minionIndex = 1; + else + minionIndex = minion; + + writer.putInt(0); + for (int i = 0; i < 3; i++) { + writer.putInt(0); //static + } + writer.putInt(9); + writer.putInt(5); + writer.putInt(9); + writer.putInt(5); + writer.put((byte) 0); + + writer.putInt((minion == 1) ? 900 : 600); //roll time + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); //Array + writer.put((byte) 0); + + if (minion == 1) + writer.putString("Trebuchet"); + else if (minion == 2) + writer.putString("Ballista"); + else + writer.putString("Mangonel"); + writer.put((byte) 1); + writer.putString("A weapon suited to laying siege"); + } + + private static void serializeGuardList(ByteBufferWriter writer, int minion) { + + writer.putInt(1); + + for (int i = 0; i < 3; i++) + writer.putInt(0); //static + + writer.putInt(minion); + writer.putInt(1); + writer.putInt(minion); + writer.putInt(1); + writer.put((byte) 0); + + writer.putInt(600); //roll time + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); //Array + writer.put((byte) 0); + + MinionType minionType = MinionType.ContractToMinionMap.get(minion); + writer.putString(minionType != null ? minionType.getRace() + " " + minionType.getName() : "Minion Guard"); + writer.put((byte) 1); + writer.putString("A Guard To Protect Your City."); + } + + + + + + public String getCityName() { + return CityName; + } + + + public void setUnknown07(int unknown07) { + this.unknown07 = unknown07; + } + + + + public void setMotto(String motto) { + this.motto = motto; + } + + public String getMotto() { + return motto; + } + + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + public int getUnknown01() { + return unknown01; + } + + public void setUnknown03(int unknown03) { + this.unknown03 = unknown03; + } + + public int getUnknown03() { + return unknown03; + } + + public void setUnknown04(int unknown04) { + this.unknown04 = unknown04; + } + + public int getUnknown04() { + return unknown04; + } + + public void setUnknown05(int unknown05) { + this.unknown05 = unknown05; + } + + public int getUnknown05() { + return unknown05; + } + + public void setUnknown06(int unknown06) { + this.unknown06 = unknown06; + } + + public int getUnknown06() { + return unknown06; + } + + public int getBuyNormal() { + return buyNormal; + } + + public void setBuyNormal(int buyNormal) { + this.buyNormal = buyNormal; + } + + public int getBuyGuild() { + return buyGuild; + } + + public void setBuyGuild(int buyGuild) { + this.buyGuild = buyGuild; + } + + public int getBuyNation() { + return buyNation; + } + + public void setBuyNation(int buyNation) { + this.buyNation = buyNation; + } + + public int getSellNormal() { + return sellNormal; + } + + public void setSellNormal(int sellNormal) { + this.sellNormal = sellNormal; + } + + public int getSellGuild() { + return sellGuild; + } + + public void setSellGuild(int sellGuild) { + this.sellGuild = sellGuild; + } + + public int getSellNation() { + return sellNation; + } + + public void setSellNation(int sellNation) { + this.sellNation = sellNation; + } + + public int getMessageType() { + return messageType; + } + + public void setMessageType(int messageType) { + this.messageType = messageType; + } + + public int getBuildingID() { + return buildingID; + } + + public void setBuildingID(int buildingID) { + this.buildingID = buildingID; + } + + public int getUnknown20() { + return unknown20; + } + + public void setUnknown20(int unknown20) { + this.unknown20 = unknown20; + } + + public int getUnknown21() { + return unknown21; + } + + public void setUnknown21(int unknown21) { + this.unknown21 = unknown21; + } + + public int getUnknown22() { + return unknown22; + } + + public void setUnknown22(int unknown22) { + this.unknown22 = unknown22; + } + + public int getUnknown23() { + return unknown23; + } + + public void setUnknown23(int unknown23) { + this.unknown23 = unknown23; + } + + public int getUnknown24() { + return unknown24; + } + + public void setUnknown24(int unknown24) { + this.unknown24 = unknown24; + } + + public int getUnknown25() { + return unknown25; + } + + public void setUnknown25(int unknown25) { + this.unknown25 = unknown25; + } + + public int getUnknown26() { + return unknown26; + } + + public void setUnknown26(int unknown26) { + this.unknown26 = unknown26; + } + + public int getUnknown28() { + return unknown28; + } + + public void setUnknown28(int unknown28) { + this.unknown28 = unknown28; + } + + public int getUnknown30() { + return unknown30; + } + + public void setUnknown30(int unknown30) { + this.unknown30 = unknown30; + } + + public int getUnknown31() { + return unknown31; + } + + public void setUnknown31(int unknown31) { + this.unknown31 = unknown31; + } + + public int getUnknown32() { + return unknown32; + } + + public void setUnknown32(int unknown32) { + this.unknown32 = unknown32; + } + + public int getUnknown33() { + return unknown33; + } + + public void setUnknown33(int unknown33) { + this.unknown33 = unknown33; + } + + public int getUnknown34() { + return unknown34; + } + + public void setUnknown34(int unknown34) { + this.unknown34 = unknown34; + } + + public int getUnknown35() { + return unknown35; + } + + public void setUnknown35(int unknown35) { + this.unknown35 = unknown35; + } + + public int getUnknown36() { + return unknown36; + } + + public void setUnknown36(int unknown36) { + this.unknown36 = unknown36; + } + + public int getUnknown37() { + return unknown37; + } + + public void setUnknown37(int unknown37) { + this.unknown37 = unknown37; + } + + public int getUnknown38() { + return unknown38; + } + + public void setUnknown38(int unknown38) { + this.unknown38 = unknown38; + } + + public int getUnknown39() { + return unknown39; + } + + public void setUnknown39(int unknown39) { + this.unknown39 = unknown39; + } + + public int getUnknown40() { + return unknown40; + } + + public void setUnknown40(int unknown40) { + this.unknown40 = unknown40; + } + + public int getUnknown41() { + return unknown41; + } + + public void setUnknown41(int unknown41) { + this.unknown41 = unknown41; + } + + public int getUnknown42() { + return unknown42; + } + + public void setUnknown42(int unknown42) { + this.unknown42 = unknown42; + } + + public int getUnknown44() { + return unknown44; + } + + public void setUnknown44(int unknown44) { + this.unknown44 = unknown44; + } + + public int getUnknown43() { + return unknown43; + } + + public void setUnknown43(int unknown43) { + this.unknown43 = unknown43; + } + + public int getUnknown45() { + return unknown45; + } + + public void setUnknown45(int unknown45) { + this.unknown45 = unknown45; + } + + public int getUnknown46() { + return unknown46; + } + + public void setUnknown46(int unknown46) { + this.unknown46 = unknown46; + } + + public int getUnknown47() { + return unknown47; + } + + public void setUnknown47(int unknown47) { + this.unknown47 = unknown47; + } + + public int getUnknown48() { + return unknown48; + } + + public void setUnknown48(int unknown48) { + this.unknown48 = unknown48; + } + + public int getUnknown49() { + return unknown49; + } + + public void setUnknown49(int unknown49) { + this.unknown49 = unknown49; + } + + public int getUnknown50() { + return unknown50; + } + + public void setUnknown50(int unknown50) { + this.unknown50 = unknown50; + } + + public int getUnknown51() { + return unknown51; + } + + public void setUnknown51(int unknown51) { + this.unknown51 = unknown51; + } + + public int getUnknown52() { + return unknown52; + } + + public void setUnknown52(int unknown52) { + this.unknown52 = unknown52; + } + + public int getUnknown53() { + return unknown53; + } + + public void setUnknown53(int unknown53) { + this.unknown53 = unknown53; + } + + public int getUnknown54() { + return unknown54; + } + + public void setUnknown54(int unknown54) { + this.unknown54 = unknown54; + } + + public int getUnknown55() { + return unknown55; + } + + public void setUnknown55(int unknown55) { + this.unknown55 = unknown55; + } + + public int getUnknown56() { + return unknown56; + } + + public void setUnknown56(int unknown56) { + this.unknown56 = unknown56; + } + + public int getUnknown57() { + return unknown57; + } + + public void setUnknown57(int unknown57) { + this.unknown57 = unknown57; + } + + public int getUnknown58() { + return unknown58; + } + + public void setUnknown58(int unknown58) { + this.unknown58 = unknown58; + } + + public int getUnknown59() { + return unknown59; + } + + public void setUnknown59(int unknown59) { + this.unknown59 = unknown59; + } + + public int getUnknown60() { + return unknown60; + } + + public void setUnknown60(int unknown60) { + this.unknown60 = unknown60; + } + + public int getUnknown61() { + return unknown61; + } + + public void setUnknown61(int unknown61) { + this.unknown61 = unknown61; + } + + public int getUnknown62() { + return unknown62; + } + + public void setUnknown62(int unknown62) { + this.unknown62 = unknown62; + } + + public int getUnknown63() { + return unknown63; + } + + public void setUnknown63(int unknown63) { + this.unknown63 = unknown63; + } + + public int getUnknown64() { + return unknown64; + } + + public void setUnknown64(int unknown64) { + this.unknown64 = unknown64; + } + + public int getUnknown65() { + return unknown65; + } + + public void setUnknown65(int unknown65) { + this.unknown65 = unknown65; + } + + public int getUnknown66() { + return unknown66; + } + + public void setUnknown66(int unknown66) { + this.unknown66 = unknown66; + } + + public int getUnknown67() { + return unknown67; + } + + public void setUnknown67(int unknown67) { + this.unknown67 = unknown67; + } + + public int getUnknown68() { + return unknown68; + } + + public void setUnknown68(int unknown68) { + this.unknown68 = unknown68; + } + + public int getUnknown69() { + return unknown69; + } + + public void setUnknown69(int unknown69) { + this.unknown69 = unknown69; + } + + public int getUnknown70() { + return unknown70; + } + + public void setUnknown70(int unknown70) { + this.unknown70 = unknown70; + } + + public int getUnknown71() { + return unknown71; + } + + public void setUnknown71(int unknown71) { + this.unknown71 = unknown71; + } + + public int getUnknown72() { + return unknown72; + } + + public void setUnknown72(int unknown72) { + this.unknown72 = unknown72; + } + + public int getUnknown73() { + return unknown73; + } + + public void setUnknown73(int unknown73) { + this.unknown73 = unknown73; + } + + public int getUnknown74() { + return unknown74; + } + + public void setUnknown74(int unknown74) { + this.unknown74 = unknown74; + } + + public int getUnknown75() { + return unknown75; + } + + public void setUnknown75(int unknown75) { + this.unknown75 = unknown75; + } + + public int getUnknown76() { + return unknown76; + } + + public void setUnknown76(int unknown76) { + this.unknown76 = unknown76; + } + + public int getUnknown77() { + return unknown77; + } + + public void setUnknown77(int unknown77) { + this.unknown77 = unknown77; + } + + public int getUnknown78() { + return unknown78; + } + + public void setUnknown78(int unknown78) { + this.unknown78 = unknown78; + } + + public int getUnknown79() { + return unknown79; + } + + public void setUnknown79(int unknown79) { + this.unknown79 = unknown79; + } + + public int getTargetType() { + return targetType; + } + + public void setTargetType(int targetType) { + this.targetType = targetType; + } + + public int getTargetID() { + return targetID; + } + + public void setTargetID(int targetID) { + this.targetID = targetID; + } + + + +} diff --git a/src/engine/net/client/msg/MerchantMsg.java b/src/engine/net/client/msg/MerchantMsg.java new file mode 100644 index 00000000..1510c493 --- /dev/null +++ b/src/engine/net/client/msg/MerchantMsg.java @@ -0,0 +1,228 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class MerchantMsg extends ClientNetMsg { + + private int type; + private int unknown01; + private int unknown02; + private int unknown03; + private int npcType; + private int npcID; + private int cityType; + private int cityID; + private int teleportTime; + private int unknown04; + private int itemType; + private int itemID; + private int amount; + private int hashID; + + /** + * This is the general purpose constructor. + */ + public MerchantMsg() { + super(Protocol.MERCHANT); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public MerchantMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.MERCHANT, origin, reader); + } + + /** + * Copy constructor + */ + public MerchantMsg(MerchantMsg msg) { + super(Protocol.MERCHANT); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.type = reader.getInt(); + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + this.unknown03 = reader.getInt(); + this.npcType = reader.getInt(); + this.npcID = reader.getInt(); + if (this.type == 11 || type == 13) { + this.cityType = reader.getInt(); + this.cityID = reader.getInt(); + this.teleportTime = reader.getInt(); + }else if(this.type == 18){ + this.itemType = reader.getInt(); + this.itemID = reader.getInt(); + this.amount = reader.getInt(); + }else if (this.type == 17){ + this.hashID = reader.getInt(); + this.amount = reader.getInt(); + }else if (this.type == 19){ + this.hashID = reader.getInt(); + }else { + + this.cityType = 0; + this.cityID = 0; + this.teleportTime = 0; + } + this.unknown04 = reader.getInt(); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.type); + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + writer.putInt(this.unknown03); + writer.putInt(this.npcType); + writer.putInt(this.npcID); + if (this.type == 11 || type == 13) { + writer.putInt(this.cityType); + writer.putInt(this.cityID); + writer.putInt(this.teleportTime); + } + writer.putInt(this.unknown04); + if (this.type == 5){ + writer.putInt(2097253); + writer.putInt(0); + } + + } + + public int getType() { + return this.type; + } + + public int getUnknown01() { + return this.unknown01; + } + + public int getUnknown02() { + return this.unknown02; + } + + public int getUnknown03() { + return this.unknown03; + } + + public int getNPCType() { + return this.npcType; + } + + public int getNPCID() { + return this.npcID; + } + + public int getCityType() { + return this.cityType; + } + + public int getCityID() { + return this.cityID; + } + + public int getTeleportTime() { + return this.teleportTime; + } + + public int getUnknown04() { + return this.unknown04; + } + + public void setType(int value) { + this.type = value; + } + + public void setUnknown01(int value) { + this.unknown01 = value; + } + + public void setUnknown02(int value) { + this.unknown02 = value; + } + + public void setUnknown03(int value) { + this.unknown03 = value; + } + + public void setNPCType(int value) { + this.npcType = value; + } + + public void setNPCID(int value) { + this.npcID = value; + } + + public void setCityType(int value) { + this.cityType = value; + } + + public void setCityID(int value) { + this.cityID = value; + } + + public void setTeleportTime(int value) { + this.teleportTime = value; + } + + public void setUnknown04(int value) { + this.unknown04 = value; + } + + public int getItemType() { + return itemType; + } + + public void setItemType(int itemType) { + this.itemType = itemType; + } + + public int getItemID() { + return itemID; + } + + public void setItemID(int itemID) { + this.itemID = itemID; + } + + public int getAmount() { + return amount; + } + + public void setAmount(int amount) { + this.amount = amount; + } + + public int getHashID() { + return hashID; + } + + public void setHashID(int hashID) { + this.hashID = hashID; + } +} diff --git a/src/engine/net/client/msg/MinionTrainingMessage.java b/src/engine/net/client/msg/MinionTrainingMessage.java new file mode 100644 index 00000000..7de521e5 --- /dev/null +++ b/src/engine/net/client/msg/MinionTrainingMessage.java @@ -0,0 +1,195 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class MinionTrainingMessage extends ClientNetMsg { + private int npcID; + private int npcType; + private int buildingID; + private int buildingType; + private int type; + private int pad = 0; + private int objectType; + private int objectUUID; + private boolean isTreb = false; + private boolean isMangonal = false; + private boolean isBallista = false; + private int minion; + private int mobType; + private int mobID; + + + + + + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public MinionTrainingMessage(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.MINIONTRAINING, origin, reader); + } + + public MinionTrainingMessage() { + super(Protocol.MINIONTRAINING); + } + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.type = reader.getInt(); + if (this.type == 2){ + this.buildingType = reader.getInt(); + this.buildingID = reader.getInt(); + this.npcType = reader.getInt(); + this.npcID = reader.getInt(); + this.objectType = reader.getInt(); + this.objectUUID = reader.getInt(); + reader.getInt(); + reader.getInt(); + + }else{ + this.buildingType = reader.getInt(); + this.buildingID = reader.getInt(); + this.npcType = reader.getInt(); + this.npcID = reader.getInt(); + reader.getInt(); + this.minion = reader.getInt(); + if (this.minion == 1) + this.isTreb = true; + else if(this.minion == 2) + this.isBallista = true; + else if (this.minion == 3) + this.isMangonal = true; + reader.getInt(); + reader.getInt(); + } + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + + } + + public int getObjectType() { + return objectType; + } + + public void setObjectType(int value) { + this.objectType = value; + } + + public void setPad(int value) { + this.pad = value; + } + + public int getUUID() { + return objectUUID; + + } + + public int getPad() { + return pad; + } + + public int getType() { + return type; + } + public void setType(int type) { + this.type = type; + } + public boolean isTreb() { + return isTreb; + } + public void setTreb(boolean isTreb) { + this.isTreb = isTreb; + } + public boolean isMangonal() { + return isMangonal; + } + public void setMangonal(boolean isMangonal) { + this.isMangonal = isMangonal; + } + public boolean isBallista() { + return isBallista; + } + public void setBallista(boolean isBallista) { + this.isBallista = isBallista; + } + public int getMinion() { + return minion; + } + public void setMinion(int minion) { + this.minion = minion; + } + + public int getNpcID() { + return npcID; + } + + public void setNpcID(int npcID) { + this.npcID = npcID; + } + + public int getNpcType() { + return npcType; + } + + public void setNpcType(int npcType) { + this.npcType = npcType; + } + + public int getBuildingID() { + return buildingID; + } + + public void setBuildingID(int buildingID) { + this.buildingID = buildingID; + } + + public int getBuildingType() { + return buildingType; + } + + public void setBuildingType(int buildingType) { + this.buildingType = buildingType; + } + + public int getMobType() { + return mobType; + } + + public void setMobType(int mobType) { + this.mobType = mobType; + } + + public int getMobID() { + return mobID; + } + + public void setMobID(int mobID) { + this.mobID = mobID; + } + +} diff --git a/src/engine/net/client/msg/ModifyCommitToTradeMsg.java b/src/engine/net/client/msg/ModifyCommitToTradeMsg.java new file mode 100644 index 00000000..9e2c1b06 --- /dev/null +++ b/src/engine/net/client/msg/ModifyCommitToTradeMsg.java @@ -0,0 +1,145 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractGameObject; + +/** + * Commit to trade + * + * @author Eighty + */ +public class ModifyCommitToTradeMsg extends ClientNetMsg { + + private int playerType; + private int playerID; + private int targetType; + private int targetID; + private byte commit1; + private byte commit2; + + /** + * This is the general purpose constructor + */ + public ModifyCommitToTradeMsg(AbstractGameObject player, AbstractGameObject target, byte commit1, byte commit2) { + super(Protocol.TRADECONFIRMSTATUS); + this.playerType = player.getObjectType().ordinal(); + this.playerID = player.getObjectUUID(); + this.targetType = target.getObjectType().ordinal(); + this.targetID = target.getObjectUUID(); + this.commit1 = commit1; + this.commit2 = commit2; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public ModifyCommitToTradeMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.TRADECONFIRMSTATUS, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + this.playerType = reader.getInt(); + this.playerID = reader.getInt(); + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + commit1 = reader.get(); + commit2 = reader.get(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + writer.putInt(playerType); + writer.putInt(playerID); + writer.putInt(targetType); + writer.putInt(targetID); + writer.put(commit1); + writer.put(commit2); + } + + + + /** + * @return the commit1 + */ + public byte getCommit1() { + return commit1; + } + + /** + * @param commit1 the commit1 to set + */ + public void setCommit1(byte commit1) { + this.commit1 = commit1; + } + + /** + * @return the commit2 + */ + public byte getCommit2() { + return commit2; + } + + /** + * @param commit2 the commit2 to set + */ + public void setCommit2(byte commit2) { + this.commit2 = commit2; + } + + public int getPlayerType() { + return playerType; + } + + public void setPlayerType(int playerType) { + this.playerType = playerType; + } + + public int getPlayerID() { + return playerID; + } + + public void setPlayerID(int playerID) { + this.playerID = playerID; + } + + public int getTargetType() { + return targetType; + } + + public void setTargetType(int targetType) { + this.targetType = targetType; + } + + public int getTargetID() { + return targetID; + } + + public void setTargetID(int targetID) { + this.targetID = targetID; + } + +} diff --git a/src/engine/net/client/msg/ModifyHealthKillMsg.java b/src/engine/net/client/msg/ModifyHealthKillMsg.java new file mode 100644 index 00000000..4e0e74dd --- /dev/null +++ b/src/engine/net/client/msg/ModifyHealthKillMsg.java @@ -0,0 +1,236 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractCharacter; + +public class ModifyHealthKillMsg extends ClientNetMsg { + + private int trains; + private int unknownID; //effectID + private int sourceType; + private int sourceID; + private int targetType; + private int targetID; + private int unknown02 = 0; //1 heal, 0 hurt? + private int unknown03 = 0; //0=normalCast, 1to4=powerFailed, 5=targetIsImmune, 6=targetResisted + private int unknown04 = -1; + private int unknown05 = 0; + private byte unknownByte = (byte) 0; //0 + private int powerID; + private String powerName; + private float health; + private float healthMod; + private float mana; + private float manaMod; + private float stamina; + private float staminaMod; + + /** + * This is the general purpose constructor. + */ + + public ModifyHealthKillMsg(AbstractCharacter source, AbstractCharacter target, float healthMod, float manaMod, float staminaMod, int powerID, String powerName, int trains, int effectID) { + super(Protocol.POWERACTIONDDDIE); + if (source != null) { + this.sourceType = source.getObjectType().ordinal(); + this.sourceID = source.getObjectUUID(); + } else { + this.sourceType = 0; + this.sourceID = 0; + } + if (target != null) { + this.targetType = target.getObjectType().ordinal(); + this.targetID = target.getObjectUUID(); + this.health = target.getCurrentHitpoints(); + this.healthMod = healthMod; + this.mana = target.getMana(); + this.manaMod = manaMod; + this.stamina = target.getStamina(); + this.staminaMod = staminaMod; + } else { + this.targetType = 0; + this.targetID = 0; + this.health = 0; + this.healthMod = 0; + this.mana = 0; + this.manaMod = 0; + this.stamina = 0; + this.staminaMod = 0; + } + this.unknownID = effectID; + this.trains = trains; + this.powerID = powerID; + this.powerName = powerName; + + this.unknown02 = 0; + } + + //called for kills + public ModifyHealthKillMsg(AbstractCharacter source, AbstractCharacter target, int powerID, String powerName, int trains, int effectID) { + super(Protocol.POWERACTIONDDDIE); + if (source != null) { + this.sourceType = source.getObjectType().ordinal(); + this.sourceID = source.getObjectUUID(); + } else { + this.sourceType = 0; + this.sourceID = 0; + } + if (target != null) { + this.targetType = target.getObjectType().ordinal(); + this.targetID = target.getObjectUUID(); + this.mana = target.getMana(); + this.stamina = target.getStamina(); + } else { + this.targetType = 0; + this.targetID = 0; + this.mana = 0f; + this.stamina = 0f; + } + this.health = -50f; + this.healthMod = 0f; + this.manaMod = 0f; + this.staminaMod = 0f; + this.unknown02 = 0; + this.unknownID = effectID; + this.trains = trains; + this.powerID = powerID; + this.powerName = powerName; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public ModifyHealthKillMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.POWERACTIONDDDIE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.trains); + writer.putInt(this.unknownID); + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.putInt(this.targetType); + writer.putInt(this.targetID); + writer.putInt(this.unknown02); + writer.putInt(this.unknown03); + writer.putInt(this.unknown04); + writer.putInt(this.unknown05); + writer.put(this.unknownByte); + writer.putInt(this.powerID); + writer.putString(this.powerName); + writer.putFloat(this.health); + writer.putFloat(this.healthMod); + writer.putFloat(this.mana); + writer.putFloat(this.manaMod); + writer.putFloat(this.stamina); + writer.putFloat(this.staminaMod); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.trains = reader.getInt(); + this.unknownID = reader.getInt(); + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + this.unknown02 = reader.getInt(); + this.unknown03 = reader.getInt(); + this.unknown04 = reader.getInt(); + this.unknown05 = reader.getInt(); + this.unknownByte = reader.get(); + this.powerID = reader.getInt(); + this.powerName = reader.getString(); + this.health = reader.getFloat(); + this.healthMod = reader.getFloat(); + this.mana = reader.getFloat(); + this.manaMod = reader.getFloat(); + this.stamina = reader.getFloat(); + this.staminaMod = reader.getFloat(); + } + + /** + * @return the sourceType + */ + public int getSourceType() { + return sourceType; + } + + /** + * @return the sourceID + */ + public int getSourceID() { + return sourceID; + } + + /** + * @return the targetType + */ + public int getTargetType() { + return targetType; + } + + public float getHealthMod() { + return healthMod; + } + + public float getManaMod() { + return manaMod; + } + + public float getStaminaMod() { + return manaMod; + } + + /** + * @return the targetID + */ + public int getTargetID() { + return targetID; + } + + public void setSourceType(int value) { + this.sourceType = value; + } + + public void setSourceID(int value) { + this.sourceID = value; + } + + public void setTargetType(int value) { + this.targetType = value; + } + + public void setTargetID(int value) { + this.targetID = value; + } + + public void setUnknown02(int value) { + this.unknown02 = value; + } + + public void setUnknown03(int value) { + this.unknown03 = value; + } +} diff --git a/src/engine/net/client/msg/ModifyHealthMsg.java b/src/engine/net/client/msg/ModifyHealthMsg.java new file mode 100644 index 00000000..77d8d176 --- /dev/null +++ b/src/engine/net/client/msg/ModifyHealthMsg.java @@ -0,0 +1,272 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractCharacter; +import engine.objects.Building; + +public class ModifyHealthMsg extends ClientNetMsg { + + private int trains; + private int unknownID; //effectID + private int sourceType; + private int sourceID; + private int targetType; + private int targetID; + private int omitFromChat = 0; //1 heal, 0 hurt? + private int unknown03 = 0; //0=normalCast, 1to4=powerFailed, 5=targetIsImmune, 6=targetResisted + private int unknown04 = -1; + private int unknown05 = 0; + private byte unknownByte = (byte) 0; //0 + private int powerID; + private String powerName; + private float health; + private float healthMod; + private float mana; + private float manaMod; + private float stamina; + private float staminaMod; + + /** + * This is the general purpose constructor. + */ + public ModifyHealthMsg(AbstractCharacter source, Building target, float healthMod, float manaMod, float staminaMod, int powerID, String powerName, int trains, int effectID) { + super(Protocol.POWERACTIONDD); + if (source != null) { + this.sourceType = source.getObjectType().ordinal(); + this.sourceID = source.getObjectUUID(); + } else { + this.sourceType = 0; + this.sourceID = 0; + } + if (target != null) { + this.targetType = target.getObjectType().ordinal(); + this.targetID = target.getObjectUUID(); + this.health = target.getCurrentHitpoints(); + this.healthMod = healthMod; + this.mana = 0; + this.manaMod = 0; + this.stamina = 0; + this.staminaMod = 0; + } else { + this.targetType = 0; + this.targetID = 0; + this.health = 0; + this.healthMod = 0; + this.mana = 0; + this.manaMod = 0; + this.stamina = 0; + this.staminaMod = 0; + } + this.unknownID = effectID; + this.trains = trains; + this.powerID = powerID; + this.powerName = powerName; + + this.omitFromChat = 0; + } + + public ModifyHealthMsg(AbstractCharacter source, AbstractCharacter target, float healthMod, float manaMod, float staminaMod, int powerID, String powerName, int trains, int effectID) { + super(Protocol.POWERACTIONDD); + if (source != null) { + this.sourceType = source.getObjectType().ordinal(); + this.sourceID = source.getObjectUUID(); + } else { + this.sourceType = 0; + this.sourceID = 0; + } + if (target != null) { + this.targetType = target.getObjectType().ordinal(); + this.targetID = target.getObjectUUID(); + this.health = target.getCurrentHitpoints(); + this.healthMod = healthMod; + this.mana = target.getMana(); + this.manaMod = manaMod; + this.stamina = target.getStamina(); + this.staminaMod = staminaMod; + } else { + this.targetType = 0; + this.targetID = 0; + this.health = 0; + this.healthMod = 0; + this.mana = 0; + this.manaMod = 0; + this.stamina = 0; + this.staminaMod = 0; + } + this.unknownID = effectID; + this.trains = trains; + this.powerID = powerID; + this.powerName = powerName; + + this.omitFromChat = 0; + } + + //called for kills + public ModifyHealthMsg(AbstractCharacter source, AbstractCharacter target, int powerID, String powerName, int trains, int effectID) { + super(Protocol.POWERACTIONDD); + if (source != null) { + this.sourceType = source.getObjectType().ordinal(); + this.sourceID = source.getObjectUUID(); + } else { + this.sourceType = 0; + this.sourceID = 0; + } + if (target != null) { + this.targetType = target.getObjectType().ordinal(); + this.targetID = target.getObjectUUID(); + this.mana = target.getMana(); + this.stamina = target.getStamina(); + } else { + this.targetType = 0; + this.targetID = 0; + this.mana = 0f; + this.stamina = 0f; + } + this.health = -50f; + this.healthMod = 0f; + this.manaMod = 0f; + this.staminaMod = 0f; + this.omitFromChat = 0; + this.unknownID = effectID; + this.trains = trains; + this.powerID = powerID; + this.powerName = powerName; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public ModifyHealthMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.POWERACTIONDD, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.trains); + writer.putInt(this.unknownID); + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.putInt(this.targetType); + writer.putInt(this.targetID); + writer.putInt(this.omitFromChat); + writer.putInt(this.unknown03); + writer.putInt(this.unknown04); + writer.putInt(this.unknown05); + writer.put(this.unknownByte); + writer.putInt(this.powerID); + writer.putString(this.powerName); + writer.putFloat(this.health); + writer.putFloat(this.healthMod); + writer.putFloat(this.mana); + writer.putFloat(this.manaMod); + writer.putFloat(this.stamina); + writer.putFloat(this.staminaMod); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.trains = reader.getInt(); + this.unknownID = reader.getInt(); + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + this.omitFromChat = reader.getInt(); + this.unknown03 = reader.getInt(); + this.unknown04 = reader.getInt(); + this.unknown05 = reader.getInt(); + this.unknownByte = reader.get(); + this.powerID = reader.getInt(); + this.powerName = reader.getString(); + this.health = reader.getFloat(); + this.healthMod = reader.getFloat(); + this.mana = reader.getFloat(); + this.manaMod = reader.getFloat(); + this.stamina = reader.getFloat(); + this.staminaMod = reader.getFloat(); + } + + /** + * @return the sourceType + */ + public int getSourceType() { + return sourceType; + } + + /** + * @return the sourceID + */ + public int getSourceID() { + return sourceID; + } + + /** + * @return the targetType + */ + public int getTargetType() { + return targetType; + } + + public float getHealthMod() { + return healthMod; + } + + public float getManaMod() { + return manaMod; + } + + public float getStaminaMod() { + return manaMod; + } + + /** + * @return the targetID + */ + public int getTargetID() { + return targetID; + } + + public void setSourceType(int value) { + this.sourceType = value; + } + + public void setSourceID(int value) { + this.sourceID = value; + } + + public void setTargetType(int value) { + this.targetType = value; + } + + public void setTargetID(int value) { + this.targetID = value; + } + + public void setOmitFromChat(int value) { + this.omitFromChat = value; + } + + public void setUnknown03(int value) { + this.unknown03 = value; + } +} diff --git a/src/engine/net/client/msg/ModifyStatMsg.java b/src/engine/net/client/msg/ModifyStatMsg.java new file mode 100644 index 00000000..7b5909ca --- /dev/null +++ b/src/engine/net/client/msg/ModifyStatMsg.java @@ -0,0 +1,91 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class ModifyStatMsg extends ClientNetMsg { + + private int amount; + private int type; + private int unknown01; + + /** + * This is the general purpose constructor. + */ + public ModifyStatMsg() { + super(Protocol.RAISEATTR); + } + + public ModifyStatMsg(int amount, int type, int unknown01) { + super(Protocol.RAISEATTR); + this.amount = amount; + this.type = type; + this.unknown01 = unknown01; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ModifyStatMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.RAISEATTR, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.amount); + writer.putInt(this.type); + writer.putInt(this.unknown01); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.amount = reader.getInt(); + this.type = reader.getInt(); + this.unknown01 = reader.getInt(); + } + + public int getAmount() { + return this.amount; + } + + public int getType() { + return this.type; + } + + public int getUnknown01() { + return this.unknown01; + } + + public void setAmount(int value) { + this.amount = value; + } + + public void setType(int value) { + this.type = value; + } + + public void setUnknown01(int value) { + this.unknown01 = value; + } +} diff --git a/src/engine/net/client/msg/MoveCorrectionMsg.java b/src/engine/net/client/msg/MoveCorrectionMsg.java new file mode 100644 index 00000000..e527df67 --- /dev/null +++ b/src/engine/net/client/msg/MoveCorrectionMsg.java @@ -0,0 +1,226 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.math.Vector3fImmutable; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractCharacter; + +public class MoveCorrectionMsg extends ClientNetMsg { + + private int sourceType; + private int sourceID; + private float startLat; + private float startAlt; + private float startLon; + private float endLat; + private float endAlt; + private float endLon; + private int unknown01 = 2; + private int unknown02 = 0; + private int unknown03 = 0; + + /** + * This is the general purpose constructor. + */ + public MoveCorrectionMsg(AbstractCharacter ac, boolean teleport) { + super(Protocol.MOVECORRECTION); + this.sourceType = ac.getObjectType().ordinal(); + this.sourceID = ac.getObjectUUID(); + this.startLat = ac.getLoc().x; + this.startAlt = ac.getLoc().y; + this.startLon = ac.getLoc().z; + if (teleport){ + this.endLat = ac.getLoc().x; + this.endAlt = ac.getLoc().y; + this.endLon = ac.getLoc().z; + }else{ + if (ac.isMoving()){ + this.endLat = ac.getEndLoc().x; + this.endAlt = ac.getEndLoc().y; + this.endLon = ac.getEndLoc().z; + }else{ + this.endLat = ac.getLoc().x; + this.endAlt = ac.getLoc().y; + this.endLon = ac.getLoc().z; + } + } + + this.unknown01 = Float.floatToIntBits(ac.getAltitude()); + this.unknown02 =Float.floatToIntBits(ac.getAltitude()); + this.unknown03 = Float.floatToIntBits(ac.getAltitude()); + + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public MoveCorrectionMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.MOVECORRECTION, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + + writer.putFloat(this.startLat); + writer.putFloat(this.startAlt); + writer.putFloat(this.startLon); + + writer.putFloat(this.endLat); + writer.putFloat(this.endAlt); + writer.putFloat(this.endLon); + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + writer.putInt(this.unknown03); + + + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + + this.startLat = reader.getFloat(); + this.startAlt = reader.getFloat(); + this.startLon = reader.getFloat(); + + this.endLat = reader.getFloat(); + this.endAlt = reader.getFloat(); + this.endLon = reader.getFloat(); + + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + this.unknown03 = reader.getInt(); + + } + + public int getSourceType() { + return this.sourceType; + } + + public int getSourceID() { + return this.sourceID; + } + + public float getStartLat() { + return this.startLat; + } + + public float getStartLon() { + return this.startLon; + } + + public float getStartAlt() { + return this.startAlt; + } + + public float getEndLat() { + return this.endLat; + } + + public float getEndLon() { + return this.endLon; + } + + public float getEndAlt() { + return this.endAlt; + } + + public int getUnknown01() { + return this.unknown01; + } + + public int getUnknown02() { + return this.unknown01; + } + + public int getUnknown03() { + return this.unknown01; + } + + public void setSourceType(int value) { + this.sourceType = value; + } + + public void setSourceID(int value) { + this.sourceID = value; + } + + public void setStartLat(float value) { + this.startLat = value; + } + + public void setStartLon(float value) { + this.startLon = value; + } + + public void setStartAlt(float value) { + this.startAlt = value; + } + + public void setStartCoord(Vector3fImmutable value) { + this.startLat = value.x; + this.startAlt = value.y; + this.startLon = value.z; + } + + public void setEndLat(float value) { + this.endLat = value; + } + + public void setEndLon(float value) { + this.endLon = value; + } + + public void setEndAlt(float value) { + this.endAlt = value; + } + + public void setEndCoord(Vector3fImmutable value) { + this.endLat = value.x; + this.endAlt = value.y; + this.endLon = value.z; + } + + public void setUnknown01(int value) { + this.unknown01 = value; + } + + public void setUnknown02(int value) { + this.unknown02 = value; + } + + public void setUnknown03(int value) { + this.unknown03 = value; + } + + public void setPlayer(AbstractCharacter ac) { + this.sourceType = 85; + this.sourceID = ac.getObjectUUID(); + this.setStartCoord(ac.getLoc()); + this.setEndCoord(ac.getEndLoc()); + } +} diff --git a/src/engine/net/client/msg/MoveToPointMsg.java b/src/engine/net/client/msg/MoveToPointMsg.java new file mode 100644 index 00000000..4a22fa01 --- /dev/null +++ b/src/engine/net/client/msg/MoveToPointMsg.java @@ -0,0 +1,305 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.gameManager.ZoneManager; +import engine.math.Vector3fImmutable; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractCharacter; +import engine.objects.Building; + +public class MoveToPointMsg extends ClientNetMsg { + + private int sourceType; + private int sourceID; + private float startLat; + private float startLon; + private float startAlt; + private float endLat; + private float endLon; + private float endAlt; + private int targetType; + private int targetID; + private int inBuilding; // 0=true, -1=false 0/1/2 = floor you are on + private int unknown01; + private byte unknown02; + private byte unknown03; + + /** + * This is the general purpose constructor. + */ + public MoveToPointMsg() { + super(Protocol.MOVETOPOINT); + } + + + + public MoveToPointMsg(MoveToPointMsg msg) { + super(Protocol.MOVETOPOINT); + this.sourceType = msg.sourceType; + this.sourceID = msg.sourceID; + this.startLat = msg.startLat; + this.startLon = msg.startLon; + this.startAlt = msg.startAlt; + this.endLat = msg.endLat; + this.endLon = msg.endLon; + this.endAlt = msg.endAlt; + this.targetType = msg.targetType; + this.targetID = msg.targetID; + this.inBuilding = msg.inBuilding; + this.unknown01 = msg.unknown01; + this.unknown02 = msg.unknown02; + this.unknown03 = msg.unknown03; + } + //Moving Furniture out of building to unload properly. //for outside regions only + public MoveToPointMsg(Building building) { + super(Protocol.MOVETOPOINT); + this.sourceType = building.getObjectType().ordinal(); + this.sourceID = building.getObjectUUID(); + this.startLat = building.getLoc().x; + this.startLon = building.getLoc().z; + this.startAlt = building.getLoc().y; + this.endLat = building.getLoc().x; + this.endLon = building.getLoc().z; + this.endAlt = building.getLoc().y; + this.targetType = 0; + this.targetID = 0; + this.inBuilding = -1; + this.unknown01 = -1; + this.unknown02 = 0; + this.unknown03 = 0; + } + + + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public MoveToPointMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.MOVETOPOINT, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + + writer.putFloat(this.startLat); + writer.putFloat(this.startAlt); + writer.putFloat(this.startLon); + + writer.putFloat(this.endLat); + writer.putFloat(this.endAlt); + writer.putFloat(this.endLon); + + writer.putInt(this.targetType); + writer.putInt(this.targetID); + + writer.putInt(this.inBuilding); + writer.putInt(this.unknown01); + + writer.put((byte)0); + writer.put((byte)0); + + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + + this.startLat = reader.getFloat(); + this.startAlt = reader.getFloat(); + this.startLon = reader.getFloat(); + + this.endLat = reader.getFloat(); + this.endAlt = reader.getFloat(); + this.endLon = reader.getFloat(); + + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + + this.inBuilding = reader.getInt(); + this.unknown01 = reader.getInt(); + + this.unknown02 = reader.get(); + this.unknown03 = reader.get(); + } + + public int getSourceType() { + return this.sourceType; + } + + public int getSourceID() { + return this.sourceID; + } + + public float getStartLat() { + return this.startLat; + } + + public float getStartLon() { + return this.startLon; + } + + public float getStartAlt() { + return this.startAlt; + } + + public float getEndLat() { + return this.endLat; + } + + public float getEndLon() { + return this.endLon; + } + + public float getEndAlt() { + return this.endAlt; + } + + public int getTargetType() { + return this.targetType; + } + + public int getTargetID() { + return this.targetID; + } + + public int getInBuilding() { + return this.inBuilding; + } + + public int getUnknown01() { + return this.unknown01; + } + + public void setSourceType(int value) { + this.sourceType = value; + } + + public void setSourceID(int value) { + this.sourceID = value; + } + + public void setStartLat(float value) { + this.startLat = value; + } + + public void setStartLon(float value) { + this.startLon = value; + } + + public void setStartAlt(float value) { + this.startAlt = value; + } + + public void setStartCoord(Vector3fImmutable value) { + this.startLat = value.x; + this.startAlt = value.y; + this.startLon = value.z; + } + + public void setEndLat(float value) { + this.endLat = value; + } + + public void setEndLon(float value) { + this.endLon = value; + } + + public void setEndAlt(float value) { + this.endAlt = value; + } + + public void setEndCoord(Vector3fImmutable value) { + this.endLat = value.x; + this.endAlt = value.y; + this.endLon = value.z; + } + + public void setTargetType(int value) { + this.targetType = value; + } + + public void setTargetID(int value) { + this.targetID = value; + } + + public void clearTarget() { + this.targetType = 0; + this.targetID = 0; + } + + public void setInBuilding(int value) { + this.inBuilding = value; + } + + public void setUnknown01(int value) { + this.unknown01 = value; + } + + public void setPlayer(AbstractCharacter ac) { + this.sourceType = ac.getObjectType().ordinal(); + this.sourceID = ac.getObjectUUID(); + this.setStartCoord(ac.getLoc()); + this.setEndCoord(ac.getEndLoc()); + this.targetType = 0; + this.targetID = 0; + this.inBuilding = ac.getInBuilding(); + this.unknown01 = ac.getInFloorID(); + + } + + public void setTarget(AbstractCharacter ac, Building target){ + if (target == null){ + this.setStartCoord(ac.getLoc()); + this.setEndCoord(ac.getEndLoc()); + this.targetType = 0; + this.targetID = 0; + this.inBuilding = -1; + this.unknown01 = -1; + }else{ + Vector3fImmutable convertLocStart = ZoneManager.convertWorldToLocal(target, ac.getLoc()); + Vector3fImmutable convertLocEnd = convertLocStart; + if (ac.isMoving()) + convertLocEnd = ZoneManager.convertWorldToLocal(target, ac.getEndLoc()); + + this.setStartCoord(convertLocStart); + this.setEndCoord(convertLocEnd); + this.targetType = GameObjectType.Building.ordinal(); + this.targetID = target.getObjectUUID(); + this.inBuilding = ac.getInBuilding(); + this.unknown01 = ac.getInFloorID(); + } + + } + + public int getUnknown03() { + return unknown03; + } + + public int getUnknown02() { + return unknown02; + } +} diff --git a/src/engine/net/client/msg/ObjectActionMsg.java b/src/engine/net/client/msg/ObjectActionMsg.java new file mode 100644 index 00000000..c1a66dcd --- /dev/null +++ b/src/engine/net/client/msg/ObjectActionMsg.java @@ -0,0 +1,131 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +import java.util.ArrayList; + + +/** + * Use item + * + * @author Eighty + */ +public class ObjectActionMsg extends ClientNetMsg { + + private int unknown01; + private int unknown02; + private ArrayList targetCompID; + + /** + * This is the general purpose constructor + */ + public ObjectActionMsg(int unknown01, int unknown02, ArrayList targetCompID) { + super(Protocol.OBJECTACTION); + this.unknown01 = unknown01; + this.unknown02 = unknown02; + this.targetCompID = targetCompID; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public ObjectActionMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.OBJECTACTION, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + if (targetCompID == null) + targetCompID = new ArrayList<>(); + unknown01 = reader.getInt(); + unknown02 = reader.getInt(); + for (int i=0; i getTargetCompID() { + return targetCompID; + } + + /** + * @param targetCompID the targetCompID to set + */ + public void setTargetCompID(ArrayList targetCompID) { + this.targetCompID = targetCompID; + } + +} diff --git a/src/engine/net/client/msg/OpenFriendsCondemnListMsg.java b/src/engine/net/client/msg/OpenFriendsCondemnListMsg.java new file mode 100644 index 00000000..b28fb317 --- /dev/null +++ b/src/engine/net/client/msg/OpenFriendsCondemnListMsg.java @@ -0,0 +1,931 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.gameManager.DbManager; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.ClientConnection; +import engine.net.client.Protocol; +import engine.objects.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; + +public class OpenFriendsCondemnListMsg extends ClientNetMsg { + + private int messageType; + private ArrayList characterList; + private int buildingType; + private int buildingID; + + private int playerType; + private int playerID; + private int guildID; + private int inviteType; + private ConcurrentHashMapfriends; + private int removeFriendType; + private int removeFriendID; + private boolean reverseKOS; //TODO Rename this for to fit ReverseKOS/Activate/deactive Condemned. + private ConcurrentHashMap guildCondemned; + private int nationID; + + public OpenFriendsCondemnListMsg(int messageType,ConcurrentHashMapfriends) { + super(Protocol.OPENFRIENDSCONDEMNLIST); + this.messageType = messageType; + this.friends = friends; + + } + + public OpenFriendsCondemnListMsg(int messageType,ConcurrentHashMap guildCondemned ,boolean reverse) { + super(Protocol.OPENFRIENDSCONDEMNLIST); + this.messageType = messageType; + this.guildCondemned = guildCondemned; + this.reverseKOS = reverse; + } + + // clone + + public OpenFriendsCondemnListMsg(OpenFriendsCondemnListMsg openFriendsCondemnListMsg) { + super(Protocol.OPENFRIENDSCONDEMNLIST); + this.messageType = openFriendsCondemnListMsg.messageType; + this.guildCondemned = openFriendsCondemnListMsg.guildCondemned; + this.reverseKOS = openFriendsCondemnListMsg.reverseKOS; + this.playerType = openFriendsCondemnListMsg.playerType; + this.playerID = openFriendsCondemnListMsg.playerID; + this.inviteType = openFriendsCondemnListMsg.inviteType; + this.removeFriendID = openFriendsCondemnListMsg.removeFriendID; + this.removeFriendType = openFriendsCondemnListMsg.removeFriendType; + this.reverseKOS = openFriendsCondemnListMsg.reverseKOS; + this.nationID = openFriendsCondemnListMsg.nationID; + this.buildingType = openFriendsCondemnListMsg.buildingType; + this.buildingID = openFriendsCondemnListMsg.buildingID; + this.friends = openFriendsCondemnListMsg.friends; + this.characterList = openFriendsCondemnListMsg.characterList; + } + + public void configure() { + + // Pre-Cache all players and guild targets + + if (characterList == null) + return; + + for (Integer uuid : characterList) { + + PlayerCharacter player = (PlayerCharacter) DbManager.getObject(GameObjectType.PlayerCharacter, uuid); + + if (player == null) + continue; + + Guild guild = player.getGuild(); + + if (guild == null) + continue; + } + + } + + public void configureHeraldry(PlayerCharacter player){ + + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public OpenFriendsCondemnListMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.OPENFRIENDSCONDEMNLIST, origin, reader); // openFriendsCondemnList =1239809615 + characterList = new ArrayList<>(); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.messageType); + + if (this.messageType == 2){ + this.showHeraldy(writer); + return; + } + + if (this.messageType == 4){ + this.writeAddHealrdy(writer); + return; + } + if (this.messageType == 26){ + showBuildingFriends(writer); + return; + } + + if (this.messageType == 12){ + this.showCondemnList(writer); + return; + } + + if (this.messageType == 15){ + this.removeCondemned(writer); + return; + } + if (this.messageType == 17){ + this.handleActivateCondemned(writer); + return; + } + + + + writer.putInt(0); + writer.putInt(this.characterList.size()); + writer.putInt(this.characterList.size()); + + for (Integer uuid : characterList) { + + PlayerCharacter player = (PlayerCharacter) DbManager.getObject(GameObjectType.PlayerCharacter, uuid); + + if (player == null) + continue; + + writer.put((byte) 1); + writer.putInt(0); + writer.putInt(0); + writer.putInt(1); + writer.putInt(GameObjectType.PlayerCharacter.ordinal()); + writer.putInt(player.getObjectUUID()); + Guild guild = player.getGuild(); + Guild nation = null; + + if (guild != null) { + writer.putInt(guild.getObjectType().ordinal()); + writer.putInt(guild.getObjectUUID()); + nation = guild.getNation(); + if (nation != null) { + writer.putInt(nation.getObjectType().ordinal()); + writer.putInt(nation.getObjectUUID()); + } else { + writer.putInt(0); + writer.putInt(0); + } + } else { + for (int i=0;i<4;i++) + writer.putInt(0); + } + writer.putShort((short)0); + writer.put((byte)0); + writer.putString(player.getFirstName()); + + if (guild != null) { + GuildTag._serializeForDisplay(guild.getGuildTag(),writer); + } else { + writer.putInt(16); + writer.putInt(16); + writer.putInt(16); + writer.putInt(0); + writer.putInt(0); + } + if (nation != null) { + GuildTag._serializeForDisplay(nation.getGuildTag(),writer); + } else { + writer.putInt(16); + writer.putInt(16); + writer.putInt(16); + writer.putInt(0); + writer.putInt(0); + } + if (guild != null) + writer.putString(guild.getName()); + else + writer.putString("[No Guild]"); + if (nation != null) + writer.putString(nation.getName()); + else + writer.putString("[No Nation]"); + writer.putInt(0); + } + } + + private void readHandleToItem(ByteBufferReader reader){ + reader.getInt(); + reader.getInt(); + reader.getInt(); + + reader.getInt(); //object Type; + reader.getInt(); //objectID; + + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + } + + private void writeHandleToItem(ByteBufferWriter writer){ + } + + private void removeCondemned(ByteBufferWriter writer){ + writer.putInt(0); + writer.putInt(playerType); + writer.putInt(playerID); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(removeFriendType); + writer.putInt(removeFriendID); + writer.putInt(buildingType); + writer.putInt(buildingID); + writer.putInt(0); + writer.putInt(0); + writer.putShort((short) 0); + + } + + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.messageType = reader.getInt(); //25 on open of friends list // 28 friends list // 11 open condemned list // 14 Open condemned selection of (indivual, guild, nation) + + if (this.messageType == 1){ + this.viewHealrdy(reader); + return; + } + + if (this.messageType == 4){ + this.readAddHealrdy(reader); + return; + } + + if (this.messageType == 6){ + this.readRemoveHeraldry(reader); + return; + } + if (this.messageType == 11){ + this.ackBuildingFriends(reader); + reader.get(); + return; + } + + if (this.messageType == 14){ + this.addGuildCondemn(reader); + return; + } + if (this.messageType == 15){ + this.removeFriendList(reader); + reader.get(); + return; + } + + if (this.messageType == 17){ + this.handleActivateCondemned(reader); + return; + } + + if (this.messageType == 18){ + this.handleCondemnErrant(reader); + return; + } + + + if (this.messageType == 19){ + this.handleKOS(reader); + return; + } + + if (this.messageType == 23){ + this.readHandleToItem(reader); + return; + } + if (this.messageType == 28){ + addFriendsList(reader); + return; + } + if (this.messageType == 30){ + removeFriendList(reader); + return; + } + if (this.messageType == 25){ + ackBuildingFriends(reader); + return; + } + + reader.getInt(); + int size = reader.getInt(); + reader.getInt(); //size again + for (int i=0;i heraldryMap = Heraldry.HeraldyMap.get(player.getObjectUUID()); + + //send empty list if no heraldry + if (heraldryMap == null || heraldryMap.isEmpty()){ + writer.putInt(0); + return; + } + + + writer.putInt(heraldryMap.size()); + + for (int characterID : heraldryMap.keySet()){ + AbstractCharacter heraldryCharacter = null; + int characterType = heraldryMap.get(characterID); + if (characterType == GameObjectType.PlayerCharacter.ordinal()) + heraldryCharacter = PlayerCharacter.getFromCache(characterID); + else if (characterType == GameObjectType.NPC.ordinal()) + heraldryCharacter = NPC.getFromCache(characterID); + else if (characterType == GameObjectType.Mob.ordinal()) + heraldryCharacter = Mob.getFromCache(characterID); + + if (heraldryCharacter == null) + this.showNullHeraldryCharacter(writer); + else{ + writer.put((byte)1); + writer.putInt(heraldryCharacter.getObjectType().ordinal()); + writer.putInt(heraldryCharacter.getObjectUUID()); + + writer.putInt(9); + writer.putInt(heraldryCharacter.getObjectType().ordinal()); + writer.putInt(heraldryCharacter.getObjectUUID()); + + if (heraldryCharacter.getGuild() != null) { + writer.putInt(heraldryCharacter.getGuild().getObjectType().ordinal()); + writer.putInt(heraldryCharacter.getGuild().getObjectUUID()); + + if (!heraldryCharacter.getGuild().getNation().isErrant()) { + writer.putInt(heraldryCharacter.getGuild().getNation().getObjectType().ordinal()); + writer.putInt(heraldryCharacter.getGuild().getNation().getObjectUUID()); + }else{ + writer.putInt(0); + writer.putInt(0); + } + }else{ + writer.putLong(0); + writer.putLong(0); + } + writer.putShort((short)0); + writer.put((byte)0); + writer.putString(heraldryCharacter.getName()); + if (heraldryCharacter.getGuild() != null) + GuildTag._serializeForDisplay(heraldryCharacter.getGuild().getGuildTag(),writer); + else{ + writer.putInt(16); + writer.putInt(16); + writer.putInt(16); + writer.putInt(0); + writer.putInt(0); + } + writer.putInt(16); + writer.putInt(16); + writer.putInt(16); + writer.putInt(0); + writer.putInt(0); + if (heraldryCharacter.getGuild() == null){ + writer.putString("Errant"); + writer.putString("Errant"); + } + else{ + writer.putString(heraldryCharacter.getGuild().getName()); + if (heraldryCharacter.getGuild().getNation() == null) + writer.putString("Errant"); + else + writer.putString(heraldryCharacter.getGuild().getNation().getName()); + } + writer.putInt(0); + } + + } + + } + + private void readAddHealrdy(ByteBufferReader reader){ + reader.getInt(); + reader.getInt(); + this.playerType = reader.getInt(); //player object type; + this.playerID = reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + + reader.getInt(); + reader.getInt(); + reader.getInt(); + } + + private void writeAddHealrdy(ByteBufferWriter writer){ + writer.putInt(0); + writer.putInt(0); + writer.putInt(this.playerType); //player object type; + writer.putInt(this.playerID); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + } + + public int getMessageType() { + return this.messageType; + } + + public ArrayList getList() { + return this.characterList; + } + + public void setMessageType(int value) { + this.messageType = value; + } + + public void setList(ArrayList value) { + this.characterList = value; + } + + public void updateMsg(int messageType, ArrayList list) { + this.messageType = messageType; + this.characterList = list; + this.configure(); + } + + public int getInviteType() { + return inviteType; + } + + public int getPlayerType() { + return playerType; + } + + public void setPlayerType(int playerType) { + this.playerType = playerType; + } + + public int getPlayerID() { + return playerID; + } + + public void setPlayerID(int playerID) { + this.playerID = playerID; + } + + public int getGuildID() { + return guildID; + } + + public int getNationID() { + return nationID; + } + + public void setGuildID(int guildID) { + this.guildID = guildID; + } + + public int getBuildingID() { + return buildingID; + } + + public void setBuildingID(int buildingID) { + this.buildingID = buildingID; + } + + public boolean isReverseKOS() { + return reverseKOS; + } + + public void setReverseKOS(boolean reverseKOS) { + this.reverseKOS = reverseKOS; + } + + + private void showNullHeraldryCharacter(ByteBufferWriter writer){ + writer.put((byte)1); + writer.putInt(0); + writer.putInt(0); + + writer.putInt(6); + writer.putInt(0); + writer.putInt(0); + + + writer.putLong(0); + writer.putLong(0); + writer.putShort((short)0); + writer.put((byte)0); + writer.putInt(0); + + writer.putInt(16); + writer.putInt(16); + writer.putInt(16); + writer.putInt(0); + writer.putInt(0); + writer.putInt(16); + writer.putInt(16); + writer.putInt(16); + writer.putInt(0); + writer.putInt(0); + + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + + } +} diff --git a/src/engine/net/client/msg/OpenTradeWindowMsg.java b/src/engine/net/client/msg/OpenTradeWindowMsg.java new file mode 100644 index 00000000..6bc21d35 --- /dev/null +++ b/src/engine/net/client/msg/OpenTradeWindowMsg.java @@ -0,0 +1,94 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractGameObject; + +/** + * Open trade window + * + * @author Eighty + */ +public class OpenTradeWindowMsg extends ClientNetMsg { + + private int unknown01; + private int playerType; + private int playerID; + private int targetType; + private int targetID; + /** + * This is the general purpose constructor + */ + public OpenTradeWindowMsg(int unknown01, AbstractGameObject player, AbstractGameObject target) { + super(Protocol.INITIATETRADEHUDS); + this.unknown01 = unknown01; + this.playerType = player.getObjectType().ordinal(); + this.playerID = player.getObjectUUID(); + this.targetType = target.getObjectType().ordinal(); + this.targetID = target.getObjectUUID(); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public OpenTradeWindowMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.INITIATETRADEHUDS, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + unknown01 = reader.getInt(); + playerType = reader.getInt(); + playerID = reader.getInt(); + targetType = reader.getInt(); + targetID = reader.getInt(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + writer.putInt(unknown01); + writer.putInt(playerType); + writer.putInt(playerID); + writer.putInt(targetType); + writer.putInt(targetID); + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + + +} diff --git a/src/engine/net/client/msg/OpenVaultMsg.java b/src/engine/net/client/msg/OpenVaultMsg.java new file mode 100644 index 00000000..2ae03be3 --- /dev/null +++ b/src/engine/net/client/msg/OpenVaultMsg.java @@ -0,0 +1,76 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractGameObject; + +/** + * Vault window opened + * @author Eighty + */ +public class OpenVaultMsg extends ClientNetMsg { + + private int playerType; + private int playerID; + private int npcType; + private int npcID; + + + /** + * This is the general purpose constructor. + */ + public OpenVaultMsg(AbstractGameObject ago, AbstractGameObject target) { + super(Protocol.OPENVAULT); + this.playerType = ago.getObjectType().ordinal(); + this.playerID = ago.getObjectUUID(); + this.npcType = target.getObjectType().ordinal(); + this.npcID = target.getObjectUUID(); + + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public OpenVaultMsg(AbstractConnection origin, + ByteBufferReader reader) { + super(Protocol.OPENVAULT, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + playerType = reader.getInt(); + playerID = reader.getInt(); + npcType = reader.getInt(); + npcID = reader.getInt(); + + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + writer.putInt(playerType); + writer.putInt(playerID); + writer.putInt(npcType); + writer.putInt(npcID); + } + +} diff --git a/src/engine/net/client/msg/OrderNPCMsg.java b/src/engine/net/client/msg/OrderNPCMsg.java new file mode 100644 index 00000000..fb4ab60f --- /dev/null +++ b/src/engine/net/client/msg/OrderNPCMsg.java @@ -0,0 +1,209 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.math.Vector3fImmutable; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +import java.util.ArrayList; + +public class OrderNPCMsg extends ClientNetMsg { + + // 2 = manage this asset. 20 = manage entire city + private int objectType; + private int npcUUID; + private int buildingUUID; + private int unknown02; + private ArrayList patrolPoints; + private ArrayList sentryPoints; + private int patrolSize; + private int sentrySize; + + private int actionType; + private float buySellPercent; + + /** + * This is the general purpose constructor + */ + public OrderNPCMsg() { + super(Protocol.ORDERNPC); + this.actionType = 0; + this.unknown02 = 0; + this.npcUUID = 0; + this.buildingUUID = 0; + + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public OrderNPCMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.ORDERNPC, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + actionType = reader.getInt(); + if (this.actionType == 28){ + this.handleCityCommand(reader); + return; + } + unknown02 = reader.getInt(); + this.objectType = reader.getInt(); // Object Type Padding + npcUUID = reader.getInt(); + reader.getInt(); // Object Type Padding + buildingUUID = reader.getInt(); + this.buySellPercent = reader.getFloat(); + if (actionType > 6 && actionType < 13) + reader.getInt(); + + + + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(actionType); + writer.putInt(unknown02); + writer.putInt(GameObjectType.NPC.ordinal()); + writer.putInt(npcUUID); + writer.putInt(GameObjectType.Building.ordinal()); + writer.putInt(buildingUUID); + writer.putFloat(this.buySellPercent); + writer.putInt(0); + + + } + + private void handleCityCommand(ByteBufferReader reader){ + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + this.buildingUUID = reader.getInt(); + reader.get(); + reader.get(); + reader.getInt(); + patrolSize = reader.getInt(); + if (patrolSize > 0){ + this.patrolPoints = new ArrayList<>(); + for (int i = 0;i 0){ + this.sentryPoints = new ArrayList<>(); + for (int i = 0;i getPatrolPoints() { + return patrolPoints; + } + + public ArrayList getSentryPoints() { + return sentryPoints; + } + + public int getPatrolSize() { + return patrolSize; + } + + public void setPatrolSize(int patrolSize) { + this.patrolSize = patrolSize; + } + + public int getSentrySize() { + return sentrySize; + } + + public void setSentrySize(int sentrySize) { + this.sentrySize = sentrySize; + } + +} + +//Debug Info +//Run: Failed to make object TEMPLATE:135700 INSTANCE:1717987027141... (t=50.46) (r=7/4/2011 11:56:39) +//C:\ArcanePrime\Main_Branch\Shadowbane\Source\ArcObjectLoader.cpp(466):ERROR: ArcObjectLoader::Run: Failed to make object TEMPLATE:108760 INSTANCE:1717987027161... (t=50.46) (r=7/4/2011 11:56:39) +//C:\ArcanePrime\Main_Branch\Shadowbane\Source\ArcObjectLoader.cpp(466):ERROR: ArcObjectLoader::Run: Failed to make object TEMPLATE:108760 INSTANCE:1717987027177... (t=50.67) (r=7/4/2011 11:56:39) +//C:\ArcanePrime\Main_Branch\Shadowbane\Source\ArcObjectLoader.cpp(466):ERROR: ArcObjectLoader::Run: Failed to make object TEMPLATE:60040 INSTANCE:1717987027344... (t=50.87) (r=7/4/2011 11:56:39) +//C:\ArcanePrime\Main_Branch\Shadowbane\Source\ArcObjectLoader.cpp(466):ERROR: ArcObjectLoader::Run: Failed to make object TEMPLATE:3 INSTANCE:1717987027164... (t=50.88) (r=7/4/2011 11:56:39) + diff --git a/src/engine/net/client/msg/PassiveMessageTriggerMsg.java b/src/engine/net/client/msg/PassiveMessageTriggerMsg.java new file mode 100644 index 00000000..8d32f498 --- /dev/null +++ b/src/engine/net/client/msg/PassiveMessageTriggerMsg.java @@ -0,0 +1,65 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class PassiveMessageTriggerMsg extends ClientNetMsg { + + private byte animation; //not sure if it's animation, 0 or 1. + + /** + * This is the general purpose constructor. + */ + public PassiveMessageTriggerMsg(byte animation) { + super(Protocol.PASSIVEMESSAGETRIGGER); + this.animation = animation; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public PassiveMessageTriggerMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.PASSIVEMESSAGETRIGGER, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.put(this.animation); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.animation = reader.get(); + } + + public byte getAnimation() { + return this.animation; + } + + public void setAnimation(byte value) { + this.animation = value; + } + +} diff --git a/src/engine/net/client/msg/PerformActionMsg.java b/src/engine/net/client/msg/PerformActionMsg.java new file mode 100644 index 00000000..c955fa32 --- /dev/null +++ b/src/engine/net/client/msg/PerformActionMsg.java @@ -0,0 +1,298 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.math.Vector3f; +import engine.math.Vector3fImmutable; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class PerformActionMsg extends ClientNetMsg { + + protected int powerUsedID; + protected int numTrains; + protected int sourceType; + protected int sourceID; + protected int targetType; + protected int targetID; + + protected float targetX; + protected float targetY; + protected float targetZ; + protected int unknown04; //1, 2, 6 + protected int unknown05; + + protected int realTrains; //not serialized. Used for mob AI tracking. + + /** + * This is the general purpose constructor. + */ + public PerformActionMsg() { + super(Protocol.POWER); + } + + public PerformActionMsg(int powerUsedID, int numTrains, int sourceType, int sourceID, int targetType, int targetID, float targetX, float targetY, float targetZ, int unknown04, int unknown05) { + super(Protocol.POWER); + this.powerUsedID = powerUsedID; + this.numTrains = numTrains; + this.sourceType = sourceType; + this.sourceID = sourceID; + this.targetType = targetType; + this.targetID = targetID; + this.targetX = targetX; + this.targetY = targetY; + this.targetZ = targetZ; + this.unknown04 = unknown04; + this.unknown05 = unknown05; + + this.realTrains = this.numTrains; + } + + + + public PerformActionMsg(PerformActionMsg msg) { + super(Protocol.POWER); + this.powerUsedID = msg.powerUsedID; + this.numTrains = msg.numTrains; + this.sourceType = msg.sourceType; + this.sourceID = msg.sourceID; + this.targetType = msg.targetType; + this.targetID = msg.targetID; + this.targetX = msg.targetX; + this.targetY = msg.targetY; + this.targetZ = msg.targetZ; + this.unknown04 = msg.unknown04; + this.unknown05 = msg.unknown05; + this.realTrains = msg.realTrains; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public PerformActionMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.POWER, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.powerUsedID); + writer.putInt(this.numTrains); + + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.putInt(this.targetType); + writer.putInt(this.targetID); + + writer.putFloat(this.targetX); + writer.putFloat(this.targetY); + writer.putFloat(this.targetZ); + writer.putInt(this.unknown04); + writer.putInt(this.unknown05); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.powerUsedID = reader.getInt(); + this.numTrains = reader.getInt(); + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + + this.targetX = reader.getFloat(); + this.targetY = reader.getFloat(); + this.targetZ = reader.getFloat(); + this.unknown04 = reader.getInt(); + this.unknown05 = reader.getInt(); //2=FailToCast, 3=Miss, 4=Dodge, 5=Immune, 6=resisted, 7=targetDead, 8=PowerInterupted, 9=NoValidTargets, 10=NotBeenGrantedPower + this.realTrains = this.numTrains; + } + + /** + * @return the powerUsedID + */ + public int getPowerUsedID() { + return powerUsedID; + } + + /** + * @param powerUsedID + * the powerUsedID to set + */ + public void setPowerUsedID(int powerUsedID) { + this.powerUsedID = powerUsedID; + } + + /** + * @return the numTrains + */ + public int getNumTrains() { + return numTrains; + } + + /** + * @param numTrains + * the numTrains to set + */ + public void setNumTrains(int numTrains) { + this.numTrains = numTrains; + } + + /** + * @return the sourceType + */ + public int getSourceType() { + return sourceType; + } + + /** + * @param sourceType + * the sourceType to set + */ + public void setSourceType(int sourceType) { + this.sourceType = sourceType; + } + + /** + * @return the sourceID + */ + public int getSourceID() { + return sourceID; + } + + /** + * @param sourceID + * the sourceID to set + */ + public void setSourceID(int sourceID) { + this.sourceID = sourceID; + } + + /** + * @return the targetType + */ + public int getTargetType() { + return targetType; + } + + /** + * @param targetType + * the targetType to set + */ + public void setTargetType(int targetType) { + this.targetType = targetType; + } + + /** + * @return the targetID + */ + public int getTargetID() { + return targetID; + } + + /** + * @param targetID + * the targetID to set + */ + public void setTargetID(int targetID) { + this.targetID = targetID; + } + + /** + * @return the unknown01 + */ + public float getTargetX() { + return targetX; + } + + /** + */ + public void setTargetX(float targetX) { + this.targetX = targetX; + } + + /** + * @return the unknown02 + */ + public float getTargetY() { + return targetY; + } + + public void setTargetY(float targetY) { + this.targetY = targetY; + } + + /** + * @return the unknown03 + */ + public float getTargetZ() { + return targetZ; + } + + + public void setTargetZ(float targetZ) { + this.targetZ = targetZ; + } + + public void setTargetLoc(Vector3f targetLoc) { + this.targetX = targetLoc.x; + this.targetY = targetLoc.y; + this.targetZ = targetLoc.z; + } + + public Vector3fImmutable getTargetLoc() { + return new Vector3fImmutable(this.targetX, this.targetY, this.targetZ); + } + + /** + * @return the unknown04 + */ + public int getUnknown04() { + return unknown04; + } + + /** + * @param unknown04 + * the unknown04 to set + */ + public void setUnknown04(int unknown04) { + this.unknown04 = unknown04; + } + + /** + * @return the unknown05 + */ + public int getUnknown05() { + return unknown05; + } + + /** + * @param unknown05 + * the unknown05 to set + */ + public void setUnknown05(int unknown05) { + this.unknown05 = unknown05; + } + + public int getRealTrains() { + return this.realTrains; + } +} diff --git a/src/engine/net/client/msg/PetAttackMsg.java b/src/engine/net/client/msg/PetAttackMsg.java new file mode 100644 index 00000000..d25f029a --- /dev/null +++ b/src/engine/net/client/msg/PetAttackMsg.java @@ -0,0 +1,73 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class PetAttackMsg extends ClientNetMsg { + + private int targetType; + private int targetID; + + /** + * This is the general purpose constructor. + */ + public PetAttackMsg() { + super(Protocol.ARCPETATTACK); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public PetAttackMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ARCPETATTACK, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.targetType); + writer.putInt(this.targetID); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + } + + public int getTargetType() { + return this.targetType; + } + + public int getTargetID() { + return this.targetID; + } + + public void setTargetType(int value) { + this.targetType = value; + } + + public void setTargetID(int value) { + this.targetID = value; + } +} diff --git a/src/engine/net/client/msg/PetCmdMsg.java b/src/engine/net/client/msg/PetCmdMsg.java new file mode 100644 index 00000000..79215389 --- /dev/null +++ b/src/engine/net/client/msg/PetCmdMsg.java @@ -0,0 +1,68 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class PetCmdMsg extends ClientNetMsg { + + private int type; + + //1: stop attack + //2: dismiss + //3: toggle assist + //5: rest + + /** + * This is the general purpose constructor. + */ + public PetCmdMsg(int type) { + super(Protocol.ARCPETCMD); + this.type = type; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public PetCmdMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ARCPETCMD, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(type); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.type = reader.getInt(); + } + + public int getType() { + return this.type; + } + + public void setType(int value) { + this.type = value; + } +} diff --git a/src/engine/net/client/msg/PetMsg.java b/src/engine/net/client/msg/PetMsg.java new file mode 100644 index 00000000..eb681588 --- /dev/null +++ b/src/engine/net/client/msg/PetMsg.java @@ -0,0 +1,117 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Mob; + +public class PetMsg extends ClientNetMsg { + + private int type; //5 or 6 + private Mob pet; + + /** + * This is the general purpose constructor. + */ + public PetMsg(int type, Mob pet) { + super(Protocol.PET); + if (this.type != 6) + this.type = 5; + this.pet = pet; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public PetMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.PET, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.type); + + if (this.pet != null) { + writer.putInt(pet.getObjectType().ordinal()); + writer.putInt(pet.getObjectUUID()); + } else { + writer.putInt(0); + writer.putInt(0); + } + + if (type == 6) { + writer.putInt(0); + } else if (type == 5) { + if (pet != null){ + writer.putInt((int)(pet.getCurrentHitpoints() / pet.getHealthMax())); //suspect %health left + writer.putInt((int)(pet.getMana() / pet.getManaMax())); //suspect %mana left + writer.putInt((int)(pet.getStamina() / pet.getStaminaMax())); //suspect %stamina left + writer.putString(pet.getName()); + writer.putInt(0); + writer.put((byte)0); + }else{ + writer.putInt(0); //suspect %health left + writer.putInt(0); //suspect %mana left + writer.putInt(0); //suspect %stamina left + writer.putString("No Pet"); + writer.putInt(0); + writer.put((byte)0); + } + + } + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.type = reader.getInt(); + reader.getInt(); + int petID = reader.getInt(); + this.pet = Mob.getFromCache(petID); + if (this.type == 5) { + reader.getInt(); + } else if (this.type == 6) { + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getString(); + reader.getInt(); + reader.get(); + } + } + + public int getType() { + return this.type; + } + + public Mob getPet() { + return this.pet; + } + + public void setType(int value) { + this.type = value; + } + + public void setPet(Mob value) { + this.pet = value; + } +} diff --git a/src/engine/net/client/msg/PetitionReceivedMsg.java b/src/engine/net/client/msg/PetitionReceivedMsg.java new file mode 100644 index 00000000..0626ed88 --- /dev/null +++ b/src/engine/net/client/msg/PetitionReceivedMsg.java @@ -0,0 +1,274 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class PetitionReceivedMsg extends ClientNetMsg { + + // TODO pull these statics out into SBEmuStatics.java + private static final int PETITION_NEW = 1; + private static final int PETITION_CANCEL = 2; + + private static final int TYPE_GENERAL_HELP = 1; + private static final int TYPE_FEEDBACK = 2; + private static final int TYPE_STUCK = 3; + private static final int TYPE_HARASSMENT = 4; + private static final int TYPE_EXPLOIT = 5; + private static final int TYPE_BUG = 6; + private static final int TYPE_GAME_STOPPER = 7; + private static final int TYPE_TECH_SUPPORT = 8; + + private static final int SUBTYPE_EXPLOIT_DUPE = 1; + private static final int SUBTYPE_EXPLOIT_LEVELING = 2; + private static final int SUBTYPE_EXPLOIT_SKILL_GAIN = 3; + private static final int SUBTYPE_EXPLOIT_KILLING = 4; + private static final int SUBTYPE_EXPLOIT_POLICY = 5; + private static final int SUBTYPE_EXPLOIT_OTHER = 6; + private static final int SUBTYPE_TECH_VIDEO = 7; + private static final int SUBTYPE_TECH_SOUND = 8; + private static final int SUBTYPE_TECH_NETWORK = 9; + private static final int SUBTYPE_TECH_OTHER = 10; + + private int petition; + private int unknown01; + private int unknown02; + private byte unknownByte01; + private int unknown03; + private int unknown04; + private int unknown05; + private int unknown06; + private int type; + private int subType; + private String compType; + private String language; + private int unknown07; + private String message; + + /** + * This is the general purpose constructor. + */ + public PetitionReceivedMsg() { + super(Protocol.CUSTOMERPETITION); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public PetitionReceivedMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CUSTOMERPETITION, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.petition); + if (this.petition == PETITION_NEW) { + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + writer.put(this.unknownByte01); + writer.putInt(this.unknown03); + writer.putInt(this.unknown04); + writer.putInt(this.unknown05); + writer.putInt(this.unknown06); + writer.putInt(this.type); + writer.putInt(this.subType); + writer.putString(this.compType); + writer.putString(this.language); + writer.putInt(this.unknown07); + writer.putUnicodeString(message); + } else if (this.petition == PETITION_CANCEL) { + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + writer.put(this.unknownByte01); + writer.putInt(this.unknown03); + writer.putInt(this.unknown04); + } + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + petition = reader.getInt(); + if (petition == PETITION_NEW) { + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + this.unknownByte01 = reader.get(); + this.unknown03 = reader.getInt(); + this.unknown04 = reader.getInt(); + this.unknown05 = reader.getInt(); + this.unknown06 = reader.getInt(); + this.type = reader.getInt(); + this.subType = reader.getInt(); + this.compType = reader.getString(); + this.language = reader.getString(); + this.unknown07 = reader.getInt(); + this.message = reader.getUnicodeString(); + } else if (petition == PETITION_CANCEL) { + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + this.unknownByte01 = reader.get(); + this.unknown03 = reader.getInt(); + this.unknown04 = reader.getInt(); + } + } + + /** + * @return the petition + */ + public int getPetition() { + return petition; + } + + /** + * @param petition + * the petition to set + */ + public void setPetition(int petition) { + this.petition = petition; + } + + /** + * @return the type + */ + public int getType() { + return type; + } + + /** + * @param type + * the type to set + */ + public void setType(int type) { + this.type = type; + } + + /** + * @return the subType + */ + public int getSubType() { + return subType; + } + + /** + * @param subType + * the subType to set + */ + public void setSubType(int subType) { + this.subType = subType; + } + + /** + * @return the message + */ + public String getMessage() { + return message; + } + + /** + * @param message + * the message to set + */ + public void setMessage(String message) { + this.message = message; + } + + public int getUnknown01() { + return this.unknown01; + } + + public void setUnknown01(int value) { + this.unknown01 = value; + } + + public int getUnknown02() { + return this.unknown02; + } + + public void setUnknown02(int value) { + this.unknown02 = value; + } + + public int getUnknown03() { + return this.unknown03; + } + + public void setUnknown03(int value) { + this.unknown03 = value; + } + + public int getUnknown04() { + return this.unknown04; + } + + public void setUnknown04(int value) { + this.unknown04 = value; + } + + public int getUnknown05() { + return this.unknown05; + } + + public void setUnknown05(int value) { + this.unknown05 = value; + } + + public int getUnknown06() { + return this.unknown06; + } + + public void setUnknown06(int value) { + this.unknown06 = value; + } + + public int getUnknown07() { + return this.unknown07; + } + + public void setUnknown07(int value) { + this.unknown07 = value; + } + + public byte getUnknownByte01() { + return this.unknownByte01; + } + + public void setUnknownByte01(byte value) { + this.unknownByte01 = value; + } + + public String getCompType() { + return this.compType; + } + + public void setCompType(String value) { + this.compType = value; + } + + public String getLanguage() { + return this.language; + } + + public void setLanguage(String value) { + this.language = value; + } +} diff --git a/src/engine/net/client/msg/PlaceAssetMsg.java b/src/engine/net/client/msg/PlaceAssetMsg.java new file mode 100644 index 00000000..2ac5989f --- /dev/null +++ b/src/engine/net/client/msg/PlaceAssetMsg.java @@ -0,0 +1,593 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum; +import engine.math.Vector3fImmutable; +import engine.net.*; +import engine.net.client.ClientConnection; +import engine.net.client.Protocol; +import engine.objects.Zone; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class PlaceAssetMsg extends ClientNetMsg { + + /* + Client -> Server + //Type 1 + //940962DF 00000001 00000000 0A400000 03903DC1 00000000 00000000 00000000 3F800000 00000000 00000000 00000000 00 + //00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00 + //00000000 + //00000000 + //00000000 + //Type3 + //940962DF 00000003 00000000 00000000 00000000 00000000 00000000 00000000 3F800000 00000000 00000000 00000000 00 + //00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 01 + //00000000 + //00000001 + // 00000000 0013FF24 475765FD 417BD060 C794FF75 B33BBD2E 00000000 3F800000 00000000 <-placement info + //00000000 + + + Server -> Client + //Type 2 (Asset list / walls) + //940962DF 00000002 00000000 00000000 00000000 00000000 00000000 00000000 3F800000 00000000 00000000 00000000 01 + //475725EC 412BD080 C794DF86 44600000 44600000 44600000 43000000 43000000 43000000 445AC000 445AC000 01 + //00000000 + //00000000 + //00000006 <-building list for walls + // 00000000 0013FA74 00061A80 <-wall ID and cost + // 00000000 0013FBA0 000249F0 + // 00000000 0013FCCC 000186A0 + // 00000000 0013FDF8 0007A120 + // 00000000 0013FF24 0007A120 + // 00000000 004D5FD0 0007A120 + //Type 2 (Single asset) + //940962DF 00000002 00000000 00000000 00000000 00000000 00000000 00000000 3F800000 00000000 00000000 00000000 01 + //475725EC 412BD080 C794DF86 44600000 44600000 44600000 43000000 43000000 43000000 445AC000 445AC000 00 + //00000001 + // 00000000 00063060 00000001 <-blueprintUUID, 1 Building + //00000000 + //00000000 + //Type 0 (Response Error) + //940962DF 00000000 00000006 0000001b + //430061006e006e006f007400200070006c00610063006500200061007300730065007400200069006e00200077006100740065007200 + //00000000 00000000 00000000 00000000 00000000 3F800000 00000000 00000000 00000000 00 + //00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00 + //00000000 + //00000000 + //00000000 + //Type 4 (Response Success (place asset)) + //940962DF 00000000 00000002 0000000D 00460065007500640061006C0020004300680075007200630068 + //00000000 00000000 00000000 00000000 00000000 3F800000 00000000 00000000 00000000 00 + //00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00 + //00000000 + //00000000 + //00000000 + */ + + //UseItem(C->S)->Type 2(S->C)->Type 1(C->S) <-close placement window, no response + //UseItem(C->S)->Type 2(S->C)->Type 3(C->S)->Type 0(S->C) <-Attempt place asset, with error response + //UseItem(C->S)->Type 2(S->C)->Type 3(C->S)->Type 4(S->C) <-Attempt place asset, with success response + + /* Error msg codes + //1 "msg append here" <- what's in msg string. + //2 Conflict with "msg append here" + //3 Conflict between proposed assets + //4 Asset "msg append here" Cannot be isolated. + //5 Asset "msg append here" is outside the fortress zone. + //6 Cannot place the asset in water. + //7 Cannot place asset on land. + //8 NULL template for asset + //9 You must be a guild member to place this asset + //10 You must be a guild leader to place this asset + //11 No city asset template + //12 Only leaders of your guild can place fortresses + //13 Your guild cannot place fortress placements here + //14 This city architechture cannot be placed in this zone + //15 Cannot place assets in peace zone + //16 You do not belong to a guild + //17 Yours is not an errant or swarn guild + //18 There are no guild trees to be found + //19 NULL tree city asset + //20 You cannot place a bane circle on a tree your affiliated with + //21 This tree cannot be affected by bane circles + //22 Bane circles cannot effect dead trees + //23 A bane circle is already attached to nearest tree + //24 Banecircle is too far from guild + //25 Banecircle is too close to guild tree + //26 Cannot find deed in inventory + //27 Target object is not a deed + //28 You do not have enough gold to complete this request + //29 You apparently do not have access to some of these assets + //30 Insufficient deeds + //31 Unable to locate deed to this asset + //32 Unknown error occurred: 32 + //33 Unknown error occurred: 33 + //34 Unknown error occurred: 34 + //35 Unknown error occurred: 35 + //36 Unknown error occurred: 36 + //37 Unknown error occurred: 37 + //38 Unknown error occurred: 38 + //39 Too close to another tree + //40 Cannot place into occupied guild zone + //41 Cannot place outisde a guild zone + //42 Tree cannot support anymore shrines + //43 The city already has a shrine of that type + //44 You must be in a player guild to place a bane circle + //45 Tree cannot support anymore spires + //46 A spire of that type already exists + //47 Tree cannot support anymore barracks + //48 Assets (except walls) must be placed one at a time + //49 The city cannot support a warehouse at its current rank + //50 You can only have one warehouse + //51 This asset cannot be placed on city grid + //52 No city to associate asset with + //53 Buildings of war cannot be placed around a city grid unless there is an active bane + //54 You must belong to the nation of the Bane Circle or the Tree to place buildings of war. + //55 You must belong to a nation to place a bane circle + //56 The building of war must be placed closer to the city + //57 No building may be placed within this territory + //58 This territory is full, it can support no more then "msg append here" trees. + //59 No city to siege at this location. + //60 This scroll's rank is too low to bane this city + //61 The bane circle cannot support any more buildings of war + //62 The tree cannot support any more buildings of war + //63 Failure in guild tree claiming phase + //64 Your nation is already at war and your limit has been reached + //65 Unable to find a tree to target + //66 There is no bane circle to support this building of war + //67 There is no tree to support this building of war + //68 There is not tree or bane circle to support this building of war + //69 Trees must be placed within a territory + //70 Unknown error occurred: 38 + //71 This building of war may not be placed on a city grid by attackers + //72 You cannot place a bane circle while you are in a non player nation + //73 Only the guild leader or inner council may place a bane circle + //74 Only buildings of war may be placed during a bane + //75 This current vigor of the tree withstands your attempt to place a bane circle. Minutes remaining: "msg appended here" + //76 This tree cannot support towers or gatehouses + //77 This tree cannot support more towers or gatehouses + //78 This tree cannot support walls. + //79 This tree cannot support more walls. + */ + + private static final Map wallToCost; + static { + Map map = new HashMap<>(); + map.put(454700, 100000); //Straight Outer Wall + map.put(1309900, 100000); //Irekei Outer Straight Wall + map.put(1348900, 100000); //Invorri Outer Straight Wall + map.put(454650, 150000); //Outer Wall with Stairs + map.put(455000, 150000); //Outer Wall with Tower + map.put(454550, 150000); //Outer Wall Gate + map.put(455700, 150000); //Small Gate House + map.put(1309600, 150000); //Irekei Outer Wall with Stairs + map.put(1309300, 150000); //Irekei Outer Wall Gate + map.put(1331200, 150000); //Elven Straight Outer Wall + map.put(1330900, 150000); //Elven Outer Wall with Stairs + map.put(1332100, 150000); //Elven Outer Wall with Tower + map.put(1330300, 150000); //Elven Outer Wall Gate + map.put(1348600, 150000); //Invorri Outer Wall with Stairs + map.put(1348300, 150000); //Invorri Outer Wall Gate + map.put(454750, 300000); //Concave Tower + map.put(458100, 300000); //Artillery Tower + map.put(455300, 300000); //Tower Junction + map.put(454800, 300000); //Convex Tower (inside corner) + map.put(1310200, 300000); //Irekei Concave Tower + map.put(5070800, 300000); //Irekei Artillery Tower + map.put(1310500, 300000); //Irekei Convex Tower + map.put(1330600, 300000); //Elven Gate House + map.put(1331500, 300000); //Elven Concave Tower + map.put(5070200, 300000); //Elven Artillery Tower + map.put(1332400, 300000); //Elven Tower Junction + map.put(1331800, 300000); //Elven Convex Tower + map.put(1349200, 300000); //Invorri Concave Tower + map.put(5071400, 300000); //Invorri Artillery Tower + map.put(1349500, 300000); //Invorri Convex Tower + wallToCost = Collections.unmodifiableMap(map); + } + private static final Map wallToUseId; + static { + Map map = new HashMap<>(); + ///Feudal Outer Walls + map.put(454700, 1); //Straight Outer Wall + map.put(454650, 1); //Outer Wall with Stairs + map.put(455000, 1); //Outer Wall with Tower + map.put(454550, 1); //Outer Wall Gate + map.put(455700, 1); //Small Gate House + map.put(454750, 1); //Concave Tower + map.put(458100, 1); //Artillery Tower + map.put(455300, 1); //Tower Junction + map.put(454800, 1); //Convex Tower (inside corner) + //map.put(1, 454000, 1); //Gate House (giant gatehouse) NOT USE IN GAME + //Feudal Inner Walls + /* + map.put(454100, 2); //Inner Archway + map.put(454200, 2); //Inner Wall Corner + map.put(454250, 2); //Inner Wall Gate + map.put(454300, 2); //Inner Straight Wall + map.put(454350, 2); //Inner Wall T-Junction + map.put(454400, 2); //Inner Wall Cross Junction + map.put(454850, 2); //Tower-Inner Wall Junction (T-East) Stuck inside left + map.put(454900, 2); //Tower-Inner Wall Junction (T-South) stuck inside right + map.put(454950, 2); //Tower-Inner Wall Junction (4-way) stuck inside + */ + //Irekei Outer Walls + map.put(1309900, 3); //Irekei Outer Straight Wall + map.put(1309600, 3); //Irekei Outer Wall with Stairs + map.put(1309300, 3); //Irekei Outer Wall Gate + map.put(1310200, 3); //Irekei Concave Tower + map.put(5070800, 3); //Irekei Artillery Tower + map.put(1310500, 3); //Irekei Convex Tower + //Elven Outer Walls + map.put(1331200, 4); //Elven Straight Outer Wall + map.put(1330900, 4); //Elven Outer Wall with Stairs + map.put(1332100, 4); //Elven Outer Wall with Tower + map.put(1330300, 4); //Elven Outer Wall Gate + map.put(1330600, 4); //Elven Gate House + map.put(1331500, 4); //Elven Concave Tower + map.put(5070200, 4); //Elven Artillery Tower + map.put(1332400, 4); //Elven Tower Junction + map.put(1331800, 4); //Elven Convex Tower + //Invorri Outer Walls + map.put(1348900, 5); //Invorri Outer Straight Wall + map.put(1348600, 5); //Invorri Outer Wall with Stairs + map.put(1348300, 5); //Invorri Outer Wall Gate + map.put(1349200, 5); //Invorri Concave Tower + map.put(5071400, 5); //Invorri Artillery Tower + map.put(1349500, 5); //Invorri Convex Tower + wallToUseId = Collections.unmodifiableMap(map); + } + private static final Map> useIdToWallCostMaps; + static { + //autoloaded based on wallToUseId and wallToCost + Map> map = new HashMap<>(); + for (Map.Entry entry : wallToUseId.entrySet()) { + int wallId = entry.getKey(); + int useId = entry.getValue(); + int cost = 0; + Integer costCheck = wallToCost.get(wallId); + if (costCheck != null) { + cost = costCheck; + } else { + throw new Error("PlaceAssetMsg: WallId '" + wallId + "' has no cost in 'wallToCost' but exists in 'useIdToWall'."); + } + if (!map.containsKey(useId)) { + map.put(useId, new HashMap<>()); + } + map.get(useId).put(wallId, cost); + } + for (Map.Entry> entry : map.entrySet()) { + map.put(entry.getKey(), Collections.unmodifiableMap(entry.getValue())); + } + useIdToWallCostMaps = Collections.unmodifiableMap(map); + } + + private int actionType; //1,3 (recv), 0,2 (send) + private int msgID; //used on type 0, 0 on all other types + private String msg; //used on type 0 + private int contractType; //used on type 1 + private int contractID; //used on type 1 + private byte unknown01; //0x01 on type 2 (send city data). 0x00 otherwise + private float x; + private float y; + private float z; + private byte unknown02; //0x01 if data follow, 0x00 otherwise. Best guess + private ArrayList placementInfo; + + private static final int NONE = 0; + private static final int CLIENTREQ_UNKNOWN = 1; + private static final int SERVER_OPENWINDOW = 2; + private static final int CLIENTREQ_NEWBUILDING = 3; // Request to place asset + private static final int SERVER_CLOSEWINDOW = 4; + + /** + * This is the general purpose constructor. + */ + public PlaceAssetMsg() { + super(Protocol.PLACEASSET); + this.placementInfo = new ArrayList<>(); + this.actionType = SERVER_OPENWINDOW; + this.msgID = 0; + this.msg = ""; + this.contractType = 0; + this.contractID = 0; + this.unknown01 = (byte)0x00; + this.x = 0f; + this.y = 0f; + this.z = 0f; + this.unknown02 = (byte)0x01; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public PlaceAssetMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.PLACEASSET, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + writer.putInt(this.actionType); + + if (this.actionType == NONE) { + writer.putInt(this.msgID); + if (this.msgID != 0) + writer.putString(this.msg); + } else if (this.actionType == SERVER_CLOSEWINDOW) { + //writer.putInt(1); //Qty of assets placed?? A 0 will crash the client. Any value >0 seems to do the same thing. + writer.putInt(0); + } else { + writer.putInt(0); + } + + writer.putInt(this.contractType); + writer.putInt(this.contractID); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putFloat(1f); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + + if (this.actionType == SERVER_OPENWINDOW || (this.actionType == NONE && this.msgID == 0) ) { + //send Place Asset Msg + writer.put((byte)0x01); + writer.putFloat(x); + writer.putFloat(y); + writer.putFloat(z); + for (int i=0;i<3;i++) + writer.putFloat(896); // city Bounds full extent. + for (int i=0;i<3;i++) + writer.putFloat(128); //grid dimensions + PlacementInfo pi = (this.placementInfo.size() > 0) ? this.placementInfo.get(0) : null; + int buildingID = (pi != null) ? pi.getBlueprintUUID() : 0; + if (buildingID == 24200){ + writer.putFloat(875); + writer.putFloat(875); + }else{ + writer.putFloat(576); + writer.putFloat(576); + } + + + + if (buildingID < 6) { + //place wall lists + writer.put((byte)0x01); + writer.putInt(0); + writer.putInt(0); + Map buildings = getBuildingList(buildingID); + writer.putInt(buildings.size()); + for (int bID : buildings.keySet()) { + writer.putInt(0); + writer.putInt(bID); + writer.putInt(buildings.get(bID)); + } + } else { + //send individual building + writer.put((byte)0x00); + writer.putInt(1); + writer.putInt(0); + writer.putInt(buildingID); + writer.putInt(1); + writer.putInt(0); + writer.putInt(0); + } + } else { + //Send Server response to client placing asset + writer.put((byte)0x00); + for (int i=0;i<11;i++) + writer.putFloat(0f); + writer.put((byte)0x00); + writer.putInt(0); + if (this.placementInfo == null) + writer.putInt(0); + else{ + writer.putInt(this.placementInfo.size()); + for (PlacementInfo placementInfo : this.placementInfo){ + writer.putInt(0); + writer.putInt(placementInfo.blueprintUUID); + writer.putVector3f(placementInfo.loc); + writer.putFloat(placementInfo.w); + writer.putVector3f(placementInfo.rot); + } + } + + writer.putInt(0); + } + } + + private static Map getBuildingList(int buildingID) { + if (useIdToWallCostMaps.containsKey(buildingID)) { + return useIdToWallCostMaps.get(buildingID); + } + return new HashMap<>(0); + } + + public static int getWallCost(int blueprintUUID) { + if (wallToCost.containsKey(blueprintUUID)) { + return wallToCost.get(blueprintUUID); + } + Logger.warn("Cost of Wall '" + blueprintUUID + "' was requested but no cost is configured for that wallId."); + return 0; + } + + public static void sendPlaceAssetError(ClientConnection origin, int errorID, String stringData) { + + PlaceAssetMsg outMsg; + + outMsg = new PlaceAssetMsg(); + outMsg.actionType = 0; + outMsg.msgID = errorID; + outMsg.msg = stringData; + + Dispatch dispatch = Dispatch.borrow(origin.getPlayerCharacter(), outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + public static void sendPlaceAssetConfirmWall(ClientConnection origin, Zone zone) { + + PlaceAssetMsg outMsg = new PlaceAssetMsg(); + outMsg.actionType = 0; + outMsg.msgID = 0; + outMsg.msg = ""; + outMsg.x = zone.getLoc().x + 64; + outMsg.y = zone.getLoc().y; + outMsg.z = zone.getLoc().z + 64; + + + Dispatch dispatch = Dispatch.borrow(origin.getPlayerCharacter(), outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.placementInfo = new ArrayList<>(); + this.actionType = reader.getInt(); + reader.getInt(); + this.contractType = reader.getInt(); + this.contractID = reader.getInt(); + for (int i=0; i<7; i++) + reader.getInt(); + reader.get(); + for (int i=0; i<11; i++) + reader.getInt(); + reader.get(); + reader.getInt(); + int placementInfo = reader.getInt(); + for (int i=0;i getPlacementInfo() { + return this.placementInfo; + } + public PlacementInfo getFirstPlacementInfo() { + if (this.placementInfo.size() > 0) + return this.placementInfo.get(0); + return null; + } + + public void setActionType(int value) { + this.actionType = value; + } + public void setMsgID(int value) { + this.msgID = value; + } + public void setMsg(String value) { + this.msg = value; + } + public void setContractType(int value) { + this.contractType = value; + } + public void setContractID(int value) { + this.contractID = value; + } + public void setUnknown01(byte value) { + this.unknown01 = value; + } + public void setX(float value) { + this.x = value; + } + public void setY(float value) { + this.y = value; + } + public void setZ(float value) { + this.z = value; + } + public void setUnknown02(byte value) { + this.unknown02 = value; + } + public void addPlacementInfo(int ID) { + PlacementInfo pi = new PlacementInfo(ID, 0f, 0f, 0f, 0f, 0f, 0f, 0f); + this.placementInfo.add(pi); + } + + public static class PlacementInfo { + int blueprintUUID; + Vector3fImmutable loc; + float w; + Vector3fImmutable rot; + public PlacementInfo(int blueprintUUID, float locX, float locY, float locZ, float w, float rotX, float rotY, float rotZ) { + this.blueprintUUID = blueprintUUID; + this.loc = new Vector3fImmutable(locX, locY, locZ); + this.w = w; + this.rot = new Vector3fImmutable(rotX, rotY, rotZ); + } + public int getBlueprintUUID() { + return this.blueprintUUID; + } + public Vector3fImmutable getLoc() { + return this.loc; + } + public float getW() { + return this.w; + } + public Vector3fImmutable getRot() { + return this.rot; + } + public void setLoc(Vector3fImmutable loc) { + this.loc = loc; + } + } +} diff --git a/src/engine/net/client/msg/PowerProjectileMsg.java b/src/engine/net/client/msg/PowerProjectileMsg.java new file mode 100644 index 00000000..83a0c1d1 --- /dev/null +++ b/src/engine/net/client/msg/PowerProjectileMsg.java @@ -0,0 +1,92 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractWorldObject; + + +public class PowerProjectileMsg extends ClientNetMsg { + private AbstractWorldObject source; + private AbstractWorldObject target; + private float range = 10; + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + + public PowerProjectileMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ARCPOWERPROJECTILE, origin, reader); + } + + public PowerProjectileMsg() { + super(Protocol.ARCPOWERPROJECTILE); + } + + public PowerProjectileMsg(AbstractWorldObject source,AbstractWorldObject target) { + super(Protocol.ARCPOWERPROJECTILE); + this.source = source; + this.target = target; + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + + } + + // Pre-cache and configure values so they are available when we serialize + + public void configure() { + + if (this.source == null) + return; + + if (this.target == null) + return; + + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + + engine.math.Vector3fImmutable faceDir = this.source.getLoc().subtract2D(target.getLoc()).normalize(); + engine.math.Vector3fImmutable newLoc =faceDir.scaleAdd(range, target.getLoc()); + + newLoc = newLoc.setY(newLoc.getY() + range); + + writer.putInt(this.source.getObjectType().ordinal()); + writer.putInt(this.source.getObjectUUID()); + writer.putVector3f(newLoc); + + } + + public float getRange() { + return range; + } + + public void setRange(float range) { + this.range = range; + } + +} diff --git a/src/engine/net/client/msg/PromptRecallMsg.java b/src/engine/net/client/msg/PromptRecallMsg.java new file mode 100644 index 00000000..40e3bf26 --- /dev/null +++ b/src/engine/net/client/msg/PromptRecallMsg.java @@ -0,0 +1,59 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class PromptRecallMsg extends ClientNetMsg { + + private byte confirmation; + + /** + * This is the general purpose constructor. + */ + public PromptRecallMsg() { + super(Protocol.ARCPROMPTRECALL); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public PromptRecallMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ARCPROMPTRECALL, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.put((byte)0x01); // tried 0 and 1 + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.confirmation = reader.get(); + } + + public boolean getConfirmed() { + + return confirmation == 1; + } +} \ No newline at end of file diff --git a/src/engine/net/client/msg/RandomMsg.java b/src/engine/net/client/msg/RandomMsg.java new file mode 100644 index 00000000..e29d9932 --- /dev/null +++ b/src/engine/net/client/msg/RandomMsg.java @@ -0,0 +1,94 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class RandomMsg extends ClientNetMsg { + + private int max; + private int roll; + private int sourceType; + private int sourceID; + + /** + * This is the general purpose constructor. + */ + + public RandomMsg() { + super(Protocol.RANDOM); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public RandomMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.RANDOM, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.max); + writer.putInt(this.roll); + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.max = reader.getInt(); + this.roll = reader.getInt(); + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + } + + public int getMax() { + return this.max; + } + + public int getRoll() { + return this.roll; + } + + public int getSourceType() { + return this.sourceType; + } + + public int getSourceID() { + return this.sourceID; + } + + public void setMax(int value) { + this.max = value; + } + + public void setRoll(int value) { + this.roll = value; + } + + public void setSourceType(int value) { + this.sourceType = value; + } + + public void setSourceID(int value) { + this.sourceID = value; + } +} diff --git a/src/engine/net/client/msg/RecommendNationMsg.java b/src/engine/net/client/msg/RecommendNationMsg.java new file mode 100644 index 00000000..663e2797 --- /dev/null +++ b/src/engine/net/client/msg/RecommendNationMsg.java @@ -0,0 +1,92 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.PlayerCharacter; + + + +public class RecommendNationMsg extends ClientNetMsg { + + + private int guildID; + private byte ally; + private byte enemy; + + + + + public RecommendNationMsg(PlayerCharacter player) { + super(Protocol.RECOMMENDNATION); + + } + + public RecommendNationMsg() { + super(Protocol.RECOMMENDNATION); + } + + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public RecommendNationMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.RECOMMENDNATION, origin, reader); + } + //CALL THIS AFTER SANITY CHECKS AND BEFORE UPDATING HEALTH/GOLD. + + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); + this.guildID = reader.getInt(); + this.ally = reader.get(); + this.enemy = reader.get(); + } + + + // Precache and configure this message before we serialize it + + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + writer.putInt(GameObjectType.Guild.ordinal()); + writer.putInt(guildID); + writer.put(this.ally); + writer.put(this.enemy); + } + + public int getGuildID() { + return guildID; + } + + public byte getAlly() { + return ally; + } + + public byte getEnemy() { + return enemy; + } + +} diff --git a/src/engine/net/client/msg/RecvSummonsRequestMsg.java b/src/engine/net/client/msg/RecvSummonsRequestMsg.java new file mode 100644 index 00000000..bb034063 --- /dev/null +++ b/src/engine/net/client/msg/RecvSummonsRequestMsg.java @@ -0,0 +1,111 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class RecvSummonsRequestMsg extends ClientNetMsg { + + private int sourceType; + private int sourceID; + private String sourceName; + private String locationName; //where being summoned to + private boolean accepted; + + /** + * This is the general purpose constructor. + */ + public RecvSummonsRequestMsg(int sourceType, int sourceID, String sourceName, String locationName, boolean accepted) { + super(Protocol.ARCSUMMON); + this.sourceType = sourceType; + this.sourceID = sourceID; + this.sourceName = sourceName; + this.locationName = locationName; + this.accepted = accepted; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public RecvSummonsRequestMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ARCSUMMON, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.putString(this.sourceName); + writer.putString(this.locationName); + writer.put(this.accepted ? (byte)1 : (byte)0); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.sourceName = reader.getString(); + this.locationName = reader.getString(); + this.accepted = (reader.get() == 1) ? true : false; + } + + public int getSourceType() { + return this.sourceType; + } + + public int getSourceID() { + return this.sourceID; + } + + public String getSourceName() { + return this.sourceName; + } + + public String getLocationName() { + return this.locationName; + } + + public boolean accepted() { + return this.accepted; + } + + public void setSourceType(int value) { + this.sourceType = value; + } + + public void setSourceID(int value) { + this.sourceID = value; + } + + public void setSourceName(String value) { + this.sourceName = value; + } + + public void setLocationName(String value) { + this.locationName = value; + } + + public void setAccepted(boolean value) { + this.accepted = value; + } +} diff --git a/src/engine/net/client/msg/RecyclePowerMsg.java b/src/engine/net/client/msg/RecyclePowerMsg.java new file mode 100644 index 00000000..841efe4a --- /dev/null +++ b/src/engine/net/client/msg/RecyclePowerMsg.java @@ -0,0 +1,62 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class RecyclePowerMsg extends ClientNetMsg { + + protected int token; + + public RecyclePowerMsg(int token) { + super(Protocol.RECYCLEPOWER); + this.token = token; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public RecyclePowerMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.RECYCLEPOWER, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.token); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.token = reader.getInt(); + } + + public int getToken() { + return this.token; + } + + public void setToken(int value) { + this.token = value; + } + +} diff --git a/src/engine/net/client/msg/RefineMsg.java b/src/engine/net/client/msg/RefineMsg.java new file mode 100644 index 00000000..f7757783 --- /dev/null +++ b/src/engine/net/client/msg/RefineMsg.java @@ -0,0 +1,226 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.gameManager.SessionManager; +import engine.net.*; +import engine.net.client.ClientConnection; +import engine.net.client.Protocol; +import engine.objects.*; +import engine.server.MBServerStatics; + +import java.util.concurrent.ConcurrentHashMap; + +public class RefineMsg extends ClientNetMsg { + + private int npcType; + private int npcID; + private int unknown01; + private int type; + private int token; + private int unknown02; + + /** + * This is the general purpose constructor. + */ + public RefineMsg() { + super(Protocol.ARCUNTRAINABILITY); + } + + public RefineMsg(int npcType, int npcID, int type, int token) { + super(Protocol.ARCUNTRAINABILITY); + this.npcType = npcType; + this.npcID = npcID; + this.unknown01 = 1; + this.type = type; + this.token = token; + this.unknown02 = 0; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public RefineMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ARCUNTRAINABILITY, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.npcType); + writer.putInt(this.npcID); + writer.putInt(this.unknown01); + writer.putInt(this.type); + writer.putInt(this.token); + writer.putInt(this.unknown02); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.npcType = reader.getInt(); + this.npcID = reader.getInt(); + this.unknown01 = reader.getInt(); + this.type = reader.getInt(); + this.token = reader.getInt(); + this.unknown02 = reader.getInt(); + } + + public int getNpcType() { + return this.npcType; + } + + public int getNpcID() { + return this.npcID; + } + + public int getUnknown01() { + return this.unknown01; + } + + public int getType() { + return this.type; + } + + public int getToken() { + return this.token; + } + + public int getUnknown02() { + return this.unknown02; + } + + public void setUnknown01(int value) { + this.unknown01 = value; + } + + public void setType(int value) { + this.type = value; + } + + public void setToken(int value) { + this.token = value; + } + + public void setUnknown02(int value) { + this.unknown02 = value; + } + + public static void refine(RefineMsg msg, ClientConnection origin) { + if (origin == null) + return; + PlayerCharacter pc = SessionManager.getPlayerCharacter(origin); + if (pc == null) + return; + NPC npc = NPC.getFromCache(msg.npcID); + if (npc == null) + return; + int type = msg.type; + int token = msg.token; + boolean worked = false; + boolean skillPower = true; + if (type == 0) { //refine skill + worked = refineSkill(origin, pc, token, msg); + } else if (type == 1) { //refine power + worked = refinePower(origin, pc, token, msg); + } else if (type == 2) { //refine stat + worked = refineStat(origin, pc, token, msg); + skillPower = false; + } + + if (worked) { + + //update player + pc.applyBonuses(); + pc.getCharItemManager().RemoveEquipmentFromLackOfSkill(pc, true); + + //echo refine message back + + Dispatch dispatch = Dispatch.borrow(pc, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + + // if (type == 0 && token == 1488335491){ + // dispatch = Dispatch.borrow(pc, msg); + // DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + // } + + //resend refine screen + + RefinerScreenMsg refinerScreenMsg = new RefinerScreenMsg(skillPower, npc.getSellPercent(pc)); //TODO set npc cost + dispatch = Dispatch.borrow(pc, refinerScreenMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + } + } + + private static boolean refineSkill(ClientConnection origin, PlayerCharacter pc, int token, RefineMsg msg) { + CharacterSkill skill = null; + ConcurrentHashMap skills = pc.getSkills(); + for (CharacterSkill sk : skills.values()) { + if (sk == null) + continue; + SkillsBase sb = sk.getSkillsBase(); + if (sb == null) + continue; + if (sb.getToken() == token) + skill = sk; + } + //check if player has skill to refine + if (skill == null) + return false; + //check there's a train to refine + if (skill.getNumTrains() < 1) + return false; + + //TODO verify if any skills have this as prereq + + //TODO verify if any powers have this as a prereq + + //refine skill + return skill.refine(pc); + } + + private static boolean refinePower(ClientConnection origin, PlayerCharacter pc, int token, RefineMsg msg) { + CharacterPower power = null; + ConcurrentHashMap powers = pc.getPowers(); + if (!powers.containsKey(token)) + return false; + power = powers.get(token); + if (power == null) + return false; + if (power.getTrains() < 1) + return false; + + //TODO verify if any powers have this as a prereq + + return power.refine(pc); + } + + private static boolean refineStat(ClientConnection origin, PlayerCharacter pc, int token, RefineMsg msg) { + if (token == MBServerStatics.STAT_STR_ID) + return pc.refineStr(); + if (token == MBServerStatics.STAT_DEX_ID) + return pc.refineDex(); + if (token == MBServerStatics.STAT_CON_ID) + return pc.refineCon(); + if (token == MBServerStatics.STAT_INT_ID) + return pc.refineInt(msg); + if (token == MBServerStatics.STAT_SPI_ID) + return pc.refineSpi(); + return false; + } +} diff --git a/src/engine/net/client/msg/RefinerScreenMsg.java b/src/engine/net/client/msg/RefinerScreenMsg.java new file mode 100644 index 00000000..4895674a --- /dev/null +++ b/src/engine/net/client/msg/RefinerScreenMsg.java @@ -0,0 +1,117 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class RefinerScreenMsg extends ClientNetMsg { + + private int npcType; + private int npcID; + private int unknown01; //might be - 0: skills/powers, 2: stats + private float unknown02; //cost to refine + private int unknown03; + private int unknown04; + + /** + * This is the general purpose constructor. + */ + public RefinerScreenMsg(boolean skillPower, float cost) { + super(Protocol.ARCUNTRAINLIST); + if (skillPower) + this.unknown01 = 0; //skill/power screen + else + this.unknown01 = 2; //stat screen + this.unknown02 = cost; + this.unknown03 = 0; + this.unknown04 = 0; + } + + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public RefinerScreenMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ARCUNTRAINLIST, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.npcType); + writer.putInt(this.npcID); + writer.putInt(this.unknown01); + writer.putFloat(this.unknown02); + writer.putInt(this.unknown03); + writer.putInt(this.unknown04); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.npcType = reader.getInt(); + this.npcID = reader.getInt(); + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + this.unknown03 = reader.getInt(); + this.unknown04 = reader.getInt(); + } + + public int getNpcType() { + return this.npcType; + } + + public int getNpcID() { + return this.npcID; + } + + public int getUnknown01() { + return this.unknown01; + } + + public float getUnknown02() { + return this.unknown02; + } + + public int getUnknown03() { + return this.unknown03; + } + + public int getUnknown04() { + return this.unknown04; + } + + public void setUnknown01(int value) { + this.unknown01 = value; + } + + public void setUnknown02(int value) { + this.unknown02 = value; + } + + public void setUnknown03(int value) { + this.unknown03 = value; + } + + public void setUnknown04(int value) { + this.unknown04 = value; + } +} diff --git a/src/engine/net/client/msg/RejectTradeRequestMsg.java b/src/engine/net/client/msg/RejectTradeRequestMsg.java new file mode 100644 index 00000000..8b847e17 --- /dev/null +++ b/src/engine/net/client/msg/RejectTradeRequestMsg.java @@ -0,0 +1,113 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +/** + * Reject trade request + * + * @author Eighty + */ +public class RejectTradeRequestMsg extends ClientNetMsg { + + private int unknown01; //pad? + private long playerCompID; + private long targetCompID; + + /** + * This is the general purpose constructor + */ + public RejectTradeRequestMsg(int unknown01, long playerCompID, long targetCompID) { + super(Protocol.REQUESTTRADECANCEL); + this.unknown01 = unknown01; + this.playerCompID = playerCompID; + this.targetCompID = targetCompID; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public RejectTradeRequestMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.REQUESTTRADECANCEL, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + unknown01 = reader.getInt(); + playerCompID = reader.getLong(); + targetCompID = reader.getLong(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + writer.putInt(unknown01); + writer.putLong(playerCompID); + writer.putLong(targetCompID); + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the playerCompID + */ + public long getPlayerCompID() { + return playerCompID; + } + + /** + * @param playerCompID the playerCompID to set + */ + public void setPlayerCompID(long playerCompID) { + this.playerCompID = playerCompID; + } + + /** + * @return the targetCompID + */ + public long getTargetCompID() { + return targetCompID; + } + + /** + * @param targetCompID the targetCompID to set + */ + public void setTargetCompID(long targetCompID) { + this.targetCompID = targetCompID; + } + +} diff --git a/src/engine/net/client/msg/RemoveFriendMessage.java b/src/engine/net/client/msg/RemoveFriendMessage.java new file mode 100644 index 00000000..7e96f4c7 --- /dev/null +++ b/src/engine/net/client/msg/RemoveFriendMessage.java @@ -0,0 +1,67 @@ +/* +HashSet playerFriendSet = PlayerFriendsMap.get(playerUID); + playerFriendSet.add(friendUID); * Copyright 2013 MagicBane Emulator Project + * All Rights Reserved + */ +package engine.net.client.msg; + + +import engine.Enum.GameObjectType; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class RemoveFriendMessage extends ClientNetMsg { + + public int friendID; + + /** + * This is the general purpose constructor. + */ + public RemoveFriendMessage(int friendID) { + super(Protocol.REMOVEFRIEND); + this.friendID = friendID; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public RemoveFriendMessage(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.REMOVEFRIEND, origin, reader); + } + + /** + * Copy constructor + */ + public RemoveFriendMessage(RemoveFriendMessage msg) { + super(Protocol.REMOVEFRIEND); + } + + + + + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + //Do we even want to try this? + reader.getInt(); + this.friendID = reader.getInt(); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(GameObjectType.PlayerCharacter.ordinal()); + writer.putInt(this.friendID); + } +} diff --git a/src/engine/net/client/msg/RepairBuildingMsg.java b/src/engine/net/client/msg/RepairBuildingMsg.java new file mode 100644 index 00000000..a11cb39a --- /dev/null +++ b/src/engine/net/client/msg/RepairBuildingMsg.java @@ -0,0 +1,105 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class RepairBuildingMsg extends ClientNetMsg { + + private int type; + private int buildingID; + private int maxHP; + private int missingHealth; + private int strongBox; + private int repairCost; + + + + public RepairBuildingMsg(int buildingID, int maxHP, int missingHealth,int repairCost,int strongBox) { + super(Protocol.REPAIRBUILDING); + this.buildingID = buildingID; + this.maxHP = maxHP; + this.missingHealth = missingHealth; + this.repairCost = repairCost; + this.strongBox = strongBox; + } + + public RepairBuildingMsg() { + super(Protocol.REPAIRBUILDING); + } + + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public RepairBuildingMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.REPAIRBUILDING, origin, reader); + } + //CALL THIS AFTER SANITY CHECKS AND BEFORE UPDATING HEALTH/GOLD. + + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.type = reader.getInt(); + reader.getInt(); //Building Type + this.buildingID = reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + + } + + + // Precache and configure this message before we serialize it + + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + writer.putInt(0); + writer.putInt(GameObjectType.Building.ordinal()); + writer.putInt(this.buildingID); + writer.putInt(this.maxHP); + writer.putInt(this.strongBox); + writer.putInt(0); //? + writer.putInt(this.repairCost); + writer.putInt(this.missingHealth); + + } + + + + public int getBuildingID() { + return buildingID; + } + + public void setBuildingID(int buildingID) { + this.buildingID = buildingID; + } + public int getType() { + return type; + } +} diff --git a/src/engine/net/client/msg/RepairMsg.java b/src/engine/net/client/msg/RepairMsg.java new file mode 100644 index 00000000..4d91a28d --- /dev/null +++ b/src/engine/net/client/msg/RepairMsg.java @@ -0,0 +1,218 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Contract; +import engine.objects.NPC; + +import java.util.ArrayList; + +/** + * Sell to NPC window msg + * + * @author + */ +public class RepairMsg extends ClientNetMsg { + +//Item Types: +//1: weapon +//2: armor/cloth/shield +//5: scrolls +//8: potions +//10: charters +//13: Jewelry + + private int msgType; + //static 0 + private int unknown01; //0 or 10 + private int npcType; + private int npcID; + private NPC npc = null; + private int itemType; + private int itemID; + private int amountRepaired; + //static 0 + //static 0 //end repair req/ack here + //01 inc, 00 out + //item list + //skill list + //unk list + //static 0 //out + //static 0 //out + //static 10000 out? + //static 0 + + /** + * This is the general purpose constructor + */ + public RepairMsg() { + super(Protocol.REPAIROBJECT); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public RepairMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.REPAIROBJECT, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + + this.msgType = reader.getInt(); + reader.getInt(); + this.unknown01 = reader.getInt(); + this.npcType = reader.getInt(); + this.npcID = reader.getInt(); + this.itemType = reader.getInt(); + this.itemID = reader.getInt(); + this.amountRepaired = reader.getInt(); + reader.getInt(); + reader.getInt(); + + if (this.msgType == 1) { + reader.get(); //0x00 inc + int size = reader.getInt(); + for (int i=0;i list = c.getBuyItemType(); + writer.putInt(list.size()); + for (int l : list) + writer.putInt(l); + list = c.getBuySkillToken(); + writer.putInt(list.size()); + for (int l : list) + writer.putInt(l); + list = c.getBuyUnknownToken(); + writer.putInt(list.size()); + for (int l : list) + writer.putInt(l); + } else { + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + } + writer.putInt(0); + writer.putInt(0); + writer.putInt(10000); + writer.putInt(0); + } + } + + public int getMsgType() { + return this.msgType; + } + + public int getNPCType() { + return this.npcType; + } + + public int getNPCID() { + return this.npcID; + } + + public int getItemType() { + return this.itemType; + } + + public int getItemID() { + return this.itemID; + } + + public int getAmountRepaired() { + return this.amountRepaired; + } + + public NPC getNPC() { + return this.npc; + } + + public void setMsgType(int value) { + this.msgType = value; + } + + public void setNPCType(int value) { + this.npcType = value; + } + + public void setNPCID(int value) { + this.npcID = value; + } + + public void setItemType(int value) { + this.itemType = value; + } + + public void setItemID(int value) { + this.itemID = value; + } + + public void setAmountRrepaired(int value) { + this.amountRepaired = value; + } + + public void setNPC(NPC value) { + this.npc = value; + } + + public void setupRepairAck(int amountRepaired) { + this.unknown01 = 10; + this.amountRepaired = amountRepaired; + } + + public void setRepairWindowAck(NPC npc) { + this.unknown01 = 10; + this.npc = npc; + } +} diff --git a/src/engine/net/client/msg/ReqBankInventoryMsg.java b/src/engine/net/client/msg/ReqBankInventoryMsg.java new file mode 100644 index 00000000..5fc06443 --- /dev/null +++ b/src/engine/net/client/msg/ReqBankInventoryMsg.java @@ -0,0 +1,87 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractGameObject; + +/** + * Bank inventory requested + * + * @author Burfo + */ +public class ReqBankInventoryMsg extends ClientNetMsg { + + private int playerType; + private int playerID; + private long unknown01; // possibly NPC ID? + + /** + * This is the general purpose constructor. + */ + public ReqBankInventoryMsg(AbstractGameObject ago, long unknown01) { + super(Protocol.OKCOSTTOOPENBANK); + this.playerType = ago.getObjectType().ordinal(); + this.playerID = ago.getObjectUUID(); + this.unknown01 = unknown01; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ReqBankInventoryMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.OKCOSTTOOPENBANK, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + playerType = reader.getInt(); + playerID = reader.getInt(); + unknown01 = reader.getLong(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + writer.putInt(playerType); + writer.putInt(playerID); + writer.putLong(unknown01); + } + + public int getPlayerType() { + return playerType; + } + + public void setPlayerType(int playerType) { + this.playerType = playerType; + } + + public int getPlayerID() { + return playerID; + } + + public void setPlayerID(int playerID) { + this.playerID = playerID; + } + +} diff --git a/src/engine/net/client/msg/RequestBallListMessage.java b/src/engine/net/client/msg/RequestBallListMessage.java new file mode 100644 index 00000000..0483476e --- /dev/null +++ b/src/engine/net/client/msg/RequestBallListMessage.java @@ -0,0 +1,73 @@ +/* +HashSet playerFriendSet = PlayerFriendsMap.get(playerUID); + playerFriendSet.add(friendUID); * Copyright 2013 MagicBane Emulator Project + * All Rights Reserved + */ +package engine.net.client.msg; + + +import engine.Enum.GameObjectType; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.PlayerCharacter; + + +public class RequestBallListMessage extends ClientNetMsg { + + public int playerID; + public String errorMessage; + public int msgType; + + public static int REQUEST = 0; + + /** + * This is the general purpose constructor. + */ + public RequestBallListMessage(PlayerCharacter pc) { + super(Protocol.REQUESTBALLLIST); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public RequestBallListMessage(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.REQUESTBALLLIST, origin, reader); + } + + /** + * Copy constructor + */ + public RequestBallListMessage(RequestBallListMessage msg) { + super(Protocol.REQUESTBALLLIST); + } + + + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); + this.playerID = reader.getInt(); + this.msgType = reader.getInt(); + this.errorMessage = reader.getString(); + + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(GameObjectType.PlayerCharacter.ordinal()); + writer.putInt(this.playerID); + writer.putInt(this.msgType); + writer.putString(this.errorMessage); + } +} diff --git a/src/engine/net/client/msg/RequestEnterWorldMsg.java b/src/engine/net/client/msg/RequestEnterWorldMsg.java new file mode 100644 index 00000000..47af6075 --- /dev/null +++ b/src/engine/net/client/msg/RequestEnterWorldMsg.java @@ -0,0 +1,71 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class RequestEnterWorldMsg extends ClientNetMsg { + + private byte pad; + + /** + * This is the general purpose constructor. + */ + public RequestEnterWorldMsg() { + super(Protocol.ENTERWORLD); + this.pad = 0x00; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public RequestEnterWorldMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ENTERWORLD, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.put(this.pad); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.pad = reader.get(); + } + + /** + * @return the pad + */ + public byte getPad() { + return pad; + } + + /** + * @param pad + * the pad to set + */ + public void setPad(byte pad) { + this.pad = pad; + } + +} diff --git a/src/engine/net/client/msg/RespawnMsg.java b/src/engine/net/client/msg/RespawnMsg.java new file mode 100644 index 00000000..21419e47 --- /dev/null +++ b/src/engine/net/client/msg/RespawnMsg.java @@ -0,0 +1,100 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class RespawnMsg extends ClientNetMsg { + + protected int objectType; + protected int objectID; + protected float playerHealth; + protected int playerExp; + + /** + * This is the general purpose constructor. + */ + public RespawnMsg() { + super(Protocol.RESETAFTERDEATH); + this.objectType = 0; + this.objectID = 0; + this.playerHealth = 0; + this.playerExp = 0; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public RespawnMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.RESETAFTERDEATH, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.objectType); + writer.putInt(this.objectID); + writer.putFloat(this.playerHealth); + writer.putInt(this.playerExp); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + + this.objectType = reader.getInt(); + this.objectID = reader.getInt(); + this.playerHealth = reader.getFloat(); + this.playerExp = reader.getInt(); + } + + public int getObjectType() { + return this.objectType; + } + + public int getObjectID() { + return this.objectID; + } + + public float getPlayerHealth() { + return this.playerHealth; + } + + public int getPlayerExp() { + return this.playerExp; + } + + public void setObjectType(int value) { + this.objectType = value; + } + + public void setObjectID(int value) { + this.objectID = value; + } + + public void setPlayerHealth(float value) { + this.playerHealth = value; + } + + public void setPlayerExp(int value) { + this.playerExp = value; + } +} diff --git a/src/engine/net/client/msg/RespondLeaveWorldMsg.java b/src/engine/net/client/msg/RespondLeaveWorldMsg.java new file mode 100644 index 00000000..0268925f --- /dev/null +++ b/src/engine/net/client/msg/RespondLeaveWorldMsg.java @@ -0,0 +1,52 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class RespondLeaveWorldMsg extends ClientNetMsg { + + /** + * This is the general purpose constructor. + */ + public RespondLeaveWorldMsg() { + super(Protocol.LEAVEWORLD); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public RespondLeaveWorldMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.LEAVEWORLD, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + } + +} diff --git a/src/engine/net/client/msg/RotateObjectMsg.java b/src/engine/net/client/msg/RotateObjectMsg.java new file mode 100644 index 00000000..30a9677a --- /dev/null +++ b/src/engine/net/client/msg/RotateObjectMsg.java @@ -0,0 +1,61 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Mob; + +public class RotateObjectMsg extends ClientNetMsg { + + + + /** + * This is the general purpose constructor. + */ + public RotateObjectMsg(int type, Mob pet) { + super(Protocol.ROTATEMSG); + + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public RotateObjectMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ROTATEMSG, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); + reader.getInt(); + reader.getFloat(); + reader.getFloat(); + reader.getFloat(); + reader.getInt(); + } +} diff --git a/src/engine/net/client/msg/SafeModeMsg.java b/src/engine/net/client/msg/SafeModeMsg.java new file mode 100644 index 00000000..3286e0a5 --- /dev/null +++ b/src/engine/net/client/msg/SafeModeMsg.java @@ -0,0 +1,72 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class SafeModeMsg extends ClientNetMsg { + + private String message; + + /** + * Helper Constructor. + * + * This is the general purpose constructor. + */ + public SafeModeMsg() { + this("Safe Mode"); + } + + /** + * This is the general purpose constructor. + */ + public SafeModeMsg(String message) { + super(Protocol.SAFEMODE); + this.message = message; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public SafeModeMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.SAFEMODE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putString(this.message); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.message = reader.getString(); + } + + /** + * @return the message + */ + public String getMessage() { + return message; + } +} diff --git a/src/engine/net/client/msg/ScaleObjectMsg.java b/src/engine/net/client/msg/ScaleObjectMsg.java new file mode 100644 index 00000000..f363672b --- /dev/null +++ b/src/engine/net/client/msg/ScaleObjectMsg.java @@ -0,0 +1,99 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class ScaleObjectMsg extends ClientNetMsg { + + private long compID; + private float scaleX; + private float scaleY; + private float scaleZ; + + /** + * This is the general purpose constructor. + */ + public ScaleObjectMsg(long compID, float scaleX, float scaleY, float scaleZ) { + super(Protocol.SCALEOBJECT); + this.compID = compID; + this.scaleX = scaleX; + this.scaleY = scaleY; + this.scaleZ = scaleZ; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ScaleObjectMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.SCALEOBJECT, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putLong(this.compID); + writer.putFloat(this.scaleX); + writer.putFloat(this.scaleY); + writer.putFloat(this.scaleZ); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.compID = reader.getLong(); + this.scaleX = reader.getFloat(); + this.scaleY = reader.getFloat(); + this.scaleZ = reader.getFloat(); + } + + public long getCompID() { + return this.compID; + } + + public float getScaleX() { + return this.scaleX; + } + + public float getScaleY() { + return this.scaleY; + } + + public float getScaleZ() { + return this.scaleZ; + } + + public void setCompID(long value) { + this.compID = value; + } + + public void setScaleX(float value) { + this.scaleX = value; + } + + public void setScaleY(float value) { + this.scaleY = value; + } + + public void setScaleZ(float value) { + this.scaleZ = value; + } +} diff --git a/src/engine/net/client/msg/SelectCityMsg.java b/src/engine/net/client/msg/SelectCityMsg.java new file mode 100644 index 00000000..5e96a644 --- /dev/null +++ b/src/engine/net/client/msg/SelectCityMsg.java @@ -0,0 +1,93 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.AbstractNetMsg; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.PlayerCharacter; + + +public class SelectCityMsg extends ClientNetMsg { + + private PlayerCharacter pc; + private boolean isTeleport; + + /** + * This is the general purpose constructor. + */ + public SelectCityMsg(PlayerCharacter pc, boolean isTeleport) { + super(Protocol.SELECTCITY); + this.pc = pc; + this.isTeleport = isTeleport; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public SelectCityMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.SELECTCITY, origin, reader); + } + + /** + * Copy constructor + */ + public SelectCityMsg(SelectCityMsg msg) { + super(Protocol.SELECTCITY); + this.pc = msg.pc; + this.isTeleport = msg.isTeleport; + } + + /** + * @see AbstractNetMsg#getPowerOfTwoBufferSize() + */ + @Override + protected int getPowerOfTwoBufferSize() { + // Larger size for historically larger opcodes + return (12); + } + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + //Do we even want to try this? + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.put((byte)0); + } + + public PlayerCharacter pc() { + return this.pc; + } + + public boolean isTeleport() { + return this.isTeleport; + } + + public void setPC(PlayerCharacter pc) { + this.pc = pc; + } + + public void setIsTeleport(boolean value) { + this.isTeleport = value; + } +} diff --git a/src/engine/net/client/msg/SellToNPCMsg.java b/src/engine/net/client/msg/SellToNPCMsg.java new file mode 100644 index 00000000..0c28f003 --- /dev/null +++ b/src/engine/net/client/msg/SellToNPCMsg.java @@ -0,0 +1,115 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +/** + * Buy from NPC msg + * + * @author Eighty + */ +public class SellToNPCMsg extends ClientNetMsg { + + int npcType; + int npcID; + int itemType; + int itemID; + int unknown01; + + /** + * This is the general purpose constructor + */ + public SellToNPCMsg() { + super(Protocol.SELLOBJECT); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public SellToNPCMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.SELLOBJECT, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + this.npcType = reader.getInt(); + this.npcID = reader.getInt(); + this.itemType = reader.getInt(); + this.itemID = reader.getInt(); + this.unknown01 = reader.getInt(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + writer.putInt(this.npcType); + writer.putInt(this.npcID); + writer.putInt(this.itemType); + writer.putInt(this.itemID); + writer.putInt(this.unknown01); + } + + public int getNPCType() { + return this.npcType; + } + + public int getNPCID() { + return this.npcID; + } + + public int getItemType() { + return this.itemType; + } + + public int getItemID() { + return this.itemID; + } + + public int getUnknown01() { + return this.unknown01; + } + + public void setNPCType(int value) { + this.npcType = value; + } + + public void setNPCID(int value) { + this.npcID = value; + } + + public void setItemType(int value) { + this.itemType = value; + } + + public void setItemID(int value) { + this.itemID = value; + } + + public void setUnknown01(int value) { + this.unknown01 = value; + } +} diff --git a/src/engine/net/client/msg/SellToNPCWindowMsg.java b/src/engine/net/client/msg/SellToNPCWindowMsg.java new file mode 100644 index 00000000..b9a30c74 --- /dev/null +++ b/src/engine/net/client/msg/SellToNPCWindowMsg.java @@ -0,0 +1,289 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +import java.util.ArrayList; + + +/** + * Sell to NPC window msg + * + * @author Eighty + */ +public class SellToNPCWindowMsg extends ClientNetMsg { + +//Item Types: +//1: weapon +//2: armor/cloth/shield +//5: scrolls +//8: potions +//10: charters +//13: Jewelry + + + private int npcType; + private int npcID; + private byte unknownByte01; //so far always 0x00 + private byte unknownByte02; //0: show only specified, 1: show all + private ArrayList itemTypes; + private ArrayList skillTokens; + private ArrayList unknownArray02; + private int unknown01; //so far always 0 + private int unknown02; //so far always 0 on output + private int unknown03; //so far always 10000 + private int unknown04; //so far always 0 on output + private float unknown05; //suspect sell percentage, ex: 0.26 for 26% + private int unknown06; //suspect gold available on vendor + private int unknown07; //so far always 0 + + /** + * This is the general purpose constructor + */ + public SellToNPCWindowMsg(int npcType, int npcID, byte unknownByte02, + ArrayList itemTypes, ArrayList skillTokens, + ArrayList unknownArray02, float unknown05, int unknown06) { + super(Protocol.SHOPINFO); + this.npcType = npcType; + this.npcID = npcID; + this.unknownByte01 = (byte)0; + this.unknownByte02 = unknownByte02; + this.itemTypes = itemTypes; + this.skillTokens = skillTokens; + this.unknownArray02 = unknownArray02; + this.unknown01 = 0; + this.unknown02 = 0; + this.unknown03 = 10000; + this.unknown04 = 0; + this.unknown05 = unknown05; + this.unknown06 = unknown06; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public SellToNPCWindowMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.SHOPINFO, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + + this.itemTypes = new ArrayList<>(); + this.skillTokens = new ArrayList<>(); + this.unknownArray02 = new ArrayList<>(); + + this.npcType = reader.getInt(); + this.npcID = reader.getInt(); + this.unknownByte01 = reader.get(); + this.unknownByte02 = reader.get(); + int cnt = reader.getInt(); + for (int i=0; i getItemTypes() { + return this.itemTypes; + } + + public ArrayList getSkillTokens() { + return this.skillTokens; + } + + public ArrayList getUnknownArray02() { + return this.unknownArray02; + } + + public int getUnknown01() { + return unknown01; + } + + public int getUnknown02() { + return unknown02; + } + + public int getUnknown03() { + return unknown03; + } + + public int getUnknown04() { + return unknown04; + } + + public float getUnknown05() { + return unknown05; + } + + public int getUnknown06() { + return unknown06; + } + + public int getUnknown07() { + return unknown07; + } + + public void setNPCType(int value) { + this.npcType = value; + } + + public void setNPCID(int value) { + this.npcID = value; + } + + public void setUnknownByte01(byte value) { + this.unknownByte01 = value; + } + + public void setUnknownByte02(byte value) { + this.unknownByte02 = value; + } + + public void setItemTypes(ArrayList value) { + this.itemTypes = value; + } + + public void setskillTokens(ArrayList value) { + this.skillTokens = value; + } + + public void setUnknownArray02(ArrayList value) { + this.unknownArray02 = value; + } + + public void setUnknown01(int value) { + this.unknown01 = value; + } + + public void setUnknown02(int value) { + this.unknown02 = value; + } + + public void setUnknown03(int value) { + this.unknown03 = value; + } + + public void setUnknown04(int value) { + this.unknown04 = value; + } + + public void setUnknown05(float value) { + this.unknown05 = value; + } + + public void setUnknown06(int value) { + this.unknown06 = value; + } + + public void setUnknown07(int value) { + this.unknown07 = value; + } + + public void addItemType(int value) { + this.itemTypes.add(value); + } + + public void addSkillToken(int value) { + this.skillTokens.add(value); + } + + public void addUnknownArray02(int value) { + this.unknownArray02.add(value); + } + + public void setItemType(ArrayList value) { + this.itemTypes = value; + } + + public void setSkillTokens(ArrayList value) { + this.skillTokens = value; + } + + public void setUnknownArray(ArrayList value) { + this.unknownArray02 = value; + } + + public void setupOutput() { + this.unknownByte01 = (byte)0; + this.unknownByte02 = (byte)0; + this.unknown01 = 0; + this.unknown02 = 0; + this.unknown03 = 10000; + this.unknown04 = 0; + this.unknown07 = 0; + } +} diff --git a/src/engine/net/client/msg/SendBallEntryMessage.java b/src/engine/net/client/msg/SendBallEntryMessage.java new file mode 100644 index 00000000..8c32c9c3 --- /dev/null +++ b/src/engine/net/client/msg/SendBallEntryMessage.java @@ -0,0 +1,107 @@ +/* +HashSet playerFriendSet = PlayerFriendsMap.get(playerUID); + playerFriendSet.add(friendUID); * Copyright 2013 MagicBane Emulator Project + * All Rights Reserved + */ +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.PlayerCharacter; + + +public class SendBallEntryMessage extends ClientNetMsg { + + public int playerID; + public String description; + public int msgType; + public int ballColor; + public static final int ADDBALL = 4; + public static final int WHITEBALL = 2; + public static final int BLACKBALL = 1; + + /** + * This is the general purpose constructor. + */ + public SendBallEntryMessage(PlayerCharacter pc) { + super(Protocol.SENDBALLENTRY); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public SendBallEntryMessage(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.SENDBALLENTRY, origin, reader); + } + + /** + * Copy constructor + */ + public SendBallEntryMessage(SendBallEntryMessage msg) { + super(Protocol.SENDBALLENTRY); + } + + + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.msgType = reader.getInt(); + + switch (this.msgType){ + case ADDBALL: + this.readAddBall(reader); + break; + default: + break; + } + + } + + public void readAddBall(ByteBufferReader reader){ + reader.getInt(); + this.playerID = reader.getInt(); + + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + + reader.getInt(); // 1 ? + + reader.getInt(); + reader.getInt(); + + reader.getInt(); // source player type + reader.getInt(); // source player ID + + reader.getInt(); // targetType + reader.getInt(); // targetID (same as this.playerID) + this.description = reader.getString(); + reader.get(); + reader.get(); + reader.getInt(); // 100, max ball mark + reader.get(); + reader.get(); + reader.get(); + this.ballColor = reader.getInt(); + reader.get(); + + + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + } +} diff --git a/src/engine/net/client/msg/SendOwnPlayerMsg.java b/src/engine/net/client/msg/SendOwnPlayerMsg.java new file mode 100644 index 00000000..75b8362b --- /dev/null +++ b/src/engine/net/client/msg/SendOwnPlayerMsg.java @@ -0,0 +1,123 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.AbstractNetMsg; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Building; +import engine.objects.PlayerCharacter; +import engine.objects.Regions; +import engine.session.Session; +import org.pmw.tinylog.Logger; + +public class SendOwnPlayerMsg extends ClientNetMsg { + + private PlayerCharacter ch; + + /** + * This is the general purpose constructor. + * + * @param s + * Session from which the PlayerCharacter is obtained + */ + public SendOwnPlayerMsg(Session s) { + super(Protocol.PLAYERDATA); + + // Get all the character records for this account + ch = s.getPlayerCharacter(); + } + + /** + * This is the general purpose constructor. + * + * @param pc + * Playercharacter + */ + public SendOwnPlayerMsg(PlayerCharacter pc) { + super(Protocol.PLAYERDATA); + this.ch = pc; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public SendOwnPlayerMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.PLAYERDATA, origin, reader); + } + + /** + * @see AbstractNetMsg#getPowerOfTwoBufferSize() + */ + @Override + protected int getPowerOfTwoBufferSize() { + //Larger size for historically larger opcodes + return (17); // 2^17 == 131,072 + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + Regions region = this.ch.getRegion(); + //region loading seralization. serialzes building level and floor. -1 = not in building. + if (region == null){ + writer.putInt(-1); + writer.putInt(-1); + }else{ + Building regionBuilding = Regions.GetBuildingForRegion(region); + if (regionBuilding == null){ + writer.putInt(-1); + writer.putInt(-1); + }else{ + writer.putInt(region.getLevel()); + writer.putInt(region.getRoom()); + } + } + writer.putVector3f(ch.getLoc()); + try { + PlayerCharacter.serializeForClientMsgFull(this.ch,writer); + } catch (SerializationException e) { + Logger.error(e); + } + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + + int unknown1 = reader.getInt(); + int unknown2 = reader.getInt(); + + int unknown3 = reader.getInt(); + int unknown4 = reader.getInt(); + + int unknown5 = reader.getInt(); + int unknown6 = reader.getInt(); + int unknown7 = reader.getInt(); + + // TODO finish deserialization implementation. + } + + public PlayerCharacter getChar() { + return this.ch; + } +} diff --git a/src/engine/net/client/msg/SendSummonsRequestMsg.java b/src/engine/net/client/msg/SendSummonsRequestMsg.java new file mode 100644 index 00000000..d5991d56 --- /dev/null +++ b/src/engine/net/client/msg/SendSummonsRequestMsg.java @@ -0,0 +1,106 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class SendSummonsRequestMsg extends ClientNetMsg { + + private int powerToken; + private int sourceType; + private int sourceID; + private String targetName; + private int trains; + + /** + * This is the general purpose constructor. + */ + public SendSummonsRequestMsg() { + super(Protocol.POWERTARGNAME); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public SendSummonsRequestMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.POWERTARGNAME, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.powerToken); + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.putString(this.targetName); + writer.putInt(this.trains); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.powerToken = reader.getInt(); + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.targetName = reader.getString(); + this.trains = reader.getInt(); + } + + public int getPowerToken() { + return this.powerToken; + } + + public int getSourceType() { + return this.sourceType; + } + + public int getSourceID() { + return this.sourceID; + } + + public String getTargetName() { + return this.targetName; + } + + public int getTrains() { + return this.trains; + } + + public void setPowerToken(int value) { + this.powerToken = value; + } + + public void setSourceType(int value) { + this.sourceType = value; + } + + public void setSourceID(int value) { + this.sourceID = value; + } + + public void setTargetName(String value) { + this.targetName = value; + } + + public void setTrains(int value) { + this.trains = value; + } +} diff --git a/src/engine/net/client/msg/ServerInfoMsg.java b/src/engine/net/client/msg/ServerInfoMsg.java new file mode 100644 index 00000000..0dd387ab --- /dev/null +++ b/src/engine/net/client/msg/ServerInfoMsg.java @@ -0,0 +1,84 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.gameManager.ConfigManager; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.server.MBServerStatics; +import engine.server.login.LoginServer; + + +public class ServerInfoMsg extends ClientNetMsg { + + + /** + * This is the general purpose constructor. + */ + public ServerInfoMsg() { + super(Protocol.SELECTSERVER); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ServerInfoMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.SELECTSERVER, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + // writer.putInt(this.servers.size()); + // for (WorldServerInfoSnapshot wsis : this.servers) { + // wsis.serializeForClientMsg(writer); + // } + writer.putInt(1); + + writer.putInt(MBServerStatics.worldMapID); + writer.putString(ConfigManager.MB_WORLD_NAME.getValue()); + if (LoginServer.population < MBServerStatics.LOW_POPULATION) + writer.putInt(0); //Land Rush + else if (LoginServer.population < MBServerStatics.NORMAL_POPULATION) + writer.putInt(1); //Low pop + else if (LoginServer.population < MBServerStatics.HIGH_POPULATION) + writer.putInt(2); //Normal pop + else if (LoginServer.population < MBServerStatics.VERY_OVERPOPULATED_POPULATION) + writer.putInt(3); //High Pop + else if (LoginServer.population < MBServerStatics.FULL_POPULATION) + writer.putInt(4); //Very overpopulated pop + else + writer.putInt(5); //Full pop + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + int size = reader.getInt(); + for (int i = 0; i < size; i++) { + int ID = reader.getInt(); + String name = reader.getString(); + int pop = reader.getInt(); + } + } + + + +} diff --git a/src/engine/net/client/msg/SetCombatModeMsg.java b/src/engine/net/client/msg/SetCombatModeMsg.java new file mode 100644 index 00000000..e1ee8e18 --- /dev/null +++ b/src/engine/net/client/msg/SetCombatModeMsg.java @@ -0,0 +1,97 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +/** + * Attack from outside of combat mode. + * + * @author Eighty + */ +public class SetCombatModeMsg extends ClientNetMsg { + + private long playerCompID; + private boolean toggle; + + /** + * This is the general purpose constructor. + */ + public SetCombatModeMsg(long playerCompID, boolean toggle) { + super(Protocol.ARCCOMBATMODEATTACKING); + this.playerCompID = playerCompID; + this.toggle = toggle; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public SetCombatModeMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.ARCCOMBATMODEATTACKING, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putLong(playerCompID); + writer.put(toggle ? (byte) 0x01 : (byte) 0x00); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + this.playerCompID = reader.getLong(); + this.toggle = (reader.get() == 0x01) ? true : false; + } + + /** + * @return the playerCompID + */ + public long getPlayerCompID() { + return playerCompID; + } + + /** + * @return the toggle + */ + public boolean getToggle() { + return toggle; + } + + /** + * @param playerCompID + * the playerCompID to set + */ + public void setPlayerCompID(long playerCompID) { + this.playerCompID = playerCompID; + } + + /** + * @param toggle + * the toggle to set + */ + public void setToggle(boolean toggle) { + this.toggle = toggle; + } +} diff --git a/src/engine/net/client/msg/SetObjectValueMsg.java b/src/engine/net/client/msg/SetObjectValueMsg.java new file mode 100644 index 00000000..92bad166 --- /dev/null +++ b/src/engine/net/client/msg/SetObjectValueMsg.java @@ -0,0 +1,98 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractGameObject; + +public class SetObjectValueMsg extends ClientNetMsg { + private int targetType; + private int targetID; + private int msgType; + private AbstractGameObject ago; + + + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public SetObjectValueMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.SETOBJVAL, origin, reader); + } + + public SetObjectValueMsg() { + super(Protocol.SETOBJVAL); + } + public SetObjectValueMsg(AbstractGameObject ago,int type) { + super(Protocol.SETOBJVAL); + if (ago == null) + return; + this.msgType = type; + this.targetType = ago.getObjectType().ordinal(); + this.targetID = ago.getObjectUUID(); + this.ago = ago; + + } + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + writer.putInt(this.targetType); + writer.putInt(this.targetID); + writer.putInt(0); + writer.putInt(0); + writer.putInt(msgType); + writer.putInt(0); + } + + + + public AbstractGameObject getAgo() { + return ago; + } + + public void setAgo(AbstractGameObject ago) { + this.ago = ago; + } + + public int getTargetType() { + return targetType; + } + + public void setTargetType(int targetType) { + this.targetType = targetType; + } + + public int getTargetID() { + return targetID; + } + + public void setTargetID(int targetID) { + this.targetID = targetID; + } + +} diff --git a/src/engine/net/client/msg/ShowBankInventoryMsg.java b/src/engine/net/client/msg/ShowBankInventoryMsg.java new file mode 100644 index 00000000..dbd25f43 --- /dev/null +++ b/src/engine/net/client/msg/ShowBankInventoryMsg.java @@ -0,0 +1,115 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractCharacter; +import engine.objects.Item; +import engine.objects.PlayerCharacter; + +import java.util.ArrayList; + + +/** + * Bank inventory contents + * + * @author Burfo + */ +public class ShowBankInventoryMsg extends ClientNetMsg { + + PlayerCharacter pc; + long unknown01; + + /** + * This is the general purpose constructor. + */ + public ShowBankInventoryMsg(PlayerCharacter pc, long unknown01) { + super(Protocol.BANKINVENTORY); + this.pc = pc; + this.unknown01 = unknown01; + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + + ArrayList bank = pc.getCharItemManager().getBank(); + + writer.put((byte) 1); // static value + Item.putList(writer, bank, false, pc.getObjectUUID()); + writer.putInt(AbstractCharacter.getBankCapacity()); + + // TODO: Gold is sent last and has a slightly different structure. + // Everything is static except the 3 labeled lines + //TODO: f/Eighty: I don't think gold is sent separately. + // will need to check once transfer to bank is working. + /* + 00:00:00:00: + 07:00:00:00: + 00:00:20:0A:5C:46:29:14: comp id + 00:00:00:00:00:00:00:00:00:00:00:00: + 00:00:80:3F:00:00:80:3F:00:00:80:3F:00:00:80:3F: + 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00: + FF:FF:FF:FF:FF:FF:FF:FF: + 00:00:00:00: + 01: + 00:00:00:00: + 01: + 00:00:00:00:00:00:00:00: + 01: + 00:00:00:00:00:00:00:00: + 9C:1B:04:00 quantity of gold? + 00:00:00:00: + 00:00:00:00: + 00:00:00:00: + 04:00:00:00: + 00:00:00:00: + 00:00:00:00: + 01:00:00:00: + 00:00: + 00: + 58:02:00:00: unknown? + */ + + writer.putInt(pc.getObjectType().ordinal()); + writer.putInt(pc.getObjectUUID()); + writer.putLong(unknown01); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ShowBankInventoryMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.BANKINVENTORY, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + } + + @Override + protected int getPowerOfTwoBufferSize() { + // Larger size for historically larger opcodes + return 17; // 2^15 == 32,768 + } +} diff --git a/src/engine/net/client/msg/ShowMsg.java b/src/engine/net/client/msg/ShowMsg.java new file mode 100644 index 00000000..24701d53 --- /dev/null +++ b/src/engine/net/client/msg/ShowMsg.java @@ -0,0 +1,140 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.math.Vector3fImmutable; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class ShowMsg extends ClientNetMsg { + + private int targetType; + private int targetID; + private Vector3fImmutable unknown01; + private Vector3fImmutable unknown02; + private float range01; + private Vector3fImmutable unknown03; + private Vector3fImmutable unknown04; + private float range02; + + /** + * This is the general purpose constructor. + */ + public ShowMsg() { + super(Protocol.SHOWCOMBATINFO); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ShowMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.SHOWCOMBATINFO, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.targetType); + writer.putInt(this.targetID); + writer.putVector3f(this.unknown01); + writer.putVector3f(this.unknown02); + writer.putFloat(this.range01); + writer.putVector3f(this.unknown03); + writer.putVector3f(this.unknown04); + writer.putFloat(this.range02); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + this.unknown01 = reader.getVector3fImmutable(); + this.unknown02 = reader.getVector3fImmutable(); + this.range01 = reader.getFloat(); + this.unknown03 = reader.getVector3fImmutable(); + this.unknown04 = reader.getVector3fImmutable(); + this.range02 = reader.getFloat(); + } + + public int getTargetType() { + return this.targetType; + } + + public int getTargetID() { + return this.targetID; + } + + public Vector3fImmutable getUnknown01() { + return this.unknown01; + } + + public Vector3fImmutable getUnknown02() { + return this.unknown02; + } + + public Vector3fImmutable getUnknown03() { + return this.unknown03; + } + + public Vector3fImmutable getUnknown04() { + return this.unknown04; + } + + public float getRange01() { + return this.range01; + } + + public float getRange02() { + return this.range02; + } + + public void setTargetType(int value) { + this.targetType = value; + } + + public void setTargetID(int value) { + this.targetID = value; + } + + public void setUnknown01(Vector3fImmutable value) { + this.unknown01 = value; + } + + public void setUnknown02(Vector3fImmutable value) { + this.unknown02 = value; + } + + public void setUnknown03(Vector3fImmutable value) { + this.unknown03 = value; + } + + public void setUnknown04(Vector3fImmutable value) { + this.unknown04 = value; + } + + public void setRange01(float value) { + this.range01 = value; + } + + public void setRange02(float value) { + this.range02 = value; + } +} diff --git a/src/engine/net/client/msg/ShowVaultInventoryMsg.java b/src/engine/net/client/msg/ShowVaultInventoryMsg.java new file mode 100644 index 00000000..fc0d1be6 --- /dev/null +++ b/src/engine/net/client/msg/ShowVaultInventoryMsg.java @@ -0,0 +1,86 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.*; + +import java.util.ArrayList; + +/** + * Vault inventory contents + * @author Eighty + */ +public class ShowVaultInventoryMsg extends ClientNetMsg { + + PlayerCharacter pc; + int accountType; + int accountID; + + int npcType; + int npcID; + + + /** + * This is the general purpose constructor. + */ + public ShowVaultInventoryMsg(PlayerCharacter pc, Account account, NPC npc) { + super(Protocol.SHOWVAULTINVENTORY); + this.pc = pc; + this.accountType = account.getObjectType().ordinal(); + this.accountID = account.getObjectUUID(); + this.npcType = npc.getObjectType().ordinal(); + this.npcID = npc.getObjectUUID(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + + writer.putInt(accountType); + writer.putInt(accountID); + writer.putInt(npcType); + writer.putInt(npcID); + writer.putString(pc.getFirstName()); + + ArrayList vault = pc.getAccount().getVault(); + + Item.putList(writer, vault, false, pc.getObjectUUID()); + writer.putInt(AbstractCharacter.getVaultCapacity()); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public ShowVaultInventoryMsg(AbstractConnection origin, + ByteBufferReader reader) { + super(Protocol.SHOWVAULTINVENTORY, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + } + + @Override + protected int getPowerOfTwoBufferSize() { + return 17; + } +} diff --git a/src/engine/net/client/msg/SocialMsg.java b/src/engine/net/client/msg/SocialMsg.java new file mode 100644 index 00000000..fbeba2d3 --- /dev/null +++ b/src/engine/net/client/msg/SocialMsg.java @@ -0,0 +1,178 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class SocialMsg extends ClientNetMsg { + + private int sourceType; + private int sourceID; + private int unknown01; + private int unknown02; + private int targetType; + private int targetID; + private int social; + + /** + * This is the general purpose constructor. + */ + public SocialMsg() { + super(Protocol.SOCIALCHANNEL); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public SocialMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.SOCIALCHANNEL, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + writer.putInt(this.targetType); + writer.putInt(this.targetID); + writer.putInt(this.social); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + this.social = reader.getInt(); + } + + /** + * @return the sourceType + */ + public int getSourceType() { + return sourceType; + } + + /** + * @param sourceType + * the sourceType to set + */ + public void setSourceType(int sourceType) { + this.sourceType = sourceType; + } + + /** + * @return the sourceID + */ + public int getSourceID() { + return sourceID; + } + + /** + * @param sourceID + * the sourceID to set + */ + public void setSourceID(int sourceID) { + this.sourceID = sourceID; + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the unknown02 + */ + public int getUnknown02() { + return unknown02; + } + + /** + * @param unknown02 + * the unknown02 to set + */ + public void setUnknown02(int unknown02) { + this.unknown02 = unknown02; + } + + /** + * @return the targetType + */ + public int getTargetType() { + return targetType; + } + + /** + * @param targetType + * the targetType to set + */ + public void setTargetType(int targetType) { + this.targetType = targetType; + } + + /** + * @return the targetID + */ + public int getTargetID() { + return targetID; + } + + /** + * @param targetID + * the targetID to set + */ + public void setTargetID(int targetID) { + this.targetID = targetID; + } + + /** + * @return the social + */ + public int getSocial() { + return social; + } + + /** + * @param social + * the social to set + */ + public void setSocial(int social) { + this.social = social; + } + +} diff --git a/src/engine/net/client/msg/StuckCommandMsg.java b/src/engine/net/client/msg/StuckCommandMsg.java new file mode 100644 index 00000000..e089221c --- /dev/null +++ b/src/engine/net/client/msg/StuckCommandMsg.java @@ -0,0 +1,52 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class StuckCommandMsg extends ClientNetMsg { + + /** + * This is the general purpose constructor. + */ + public StuckCommandMsg() { + super(Protocol.STUCK); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public StuckCommandMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.STUCK, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + } + +} diff --git a/src/engine/net/client/msg/SyncMessage.java b/src/engine/net/client/msg/SyncMessage.java new file mode 100644 index 00000000..2d50e353 --- /dev/null +++ b/src/engine/net/client/msg/SyncMessage.java @@ -0,0 +1,146 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.gameManager.BuildingManager; +import engine.gameManager.DbManager; +import engine.gameManager.ZoneManager; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Building; +import engine.objects.Zone; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; + +public class SyncMessage extends ClientNetMsg { + + private int type; + private int size; + private int pad = 0; + private int objectType; + private int objectUUID; + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public SyncMessage(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CITYASSET, origin, reader); + } + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + //none yet + } + + public SyncMessage() { + super(Protocol.CITYASSET); + } + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + //lets do returns before writing so we don't send improper structures to the client + + Building tol = BuildingManager.getBuilding(this.objectUUID); + + if (tol == null){ + Logger.debug("TOL is null"); + return; + } + Zone zone = ZoneManager.findSmallestZone(tol.getLoc()); + if (zone == null){ + Logger.debug( "Zone is null"); + return; + } + ArrayList allCityAssets = DbManager.BuildingQueries.GET_ALL_BUILDINGS_FOR_ZONE(zone); + + // *** Refactor: collection created but never used? + + ArrayList canProtectAssets = new ArrayList<>(); + + for (Building b: allCityAssets){ + if (b.getBlueprintUUID() != 0) + canProtectAssets.add(b); + } + + // *** Refactor : Not sure what this synch message does + // Get the feeling it should be looping over upgradable + // assets. + writer.putInt(0); + writer.putInt(0); + writer.putInt(this.objectType); + writer.putInt(this.objectUUID); + writer.putInt(allCityAssets.size()); + for (Building b: allCityAssets){ + String name = b.getName(); + // if (name.equals("")) + // name = b.getBuildingSet().getName(); + writer.putInt(b.getObjectType().ordinal()); + writer.putInt(b.getObjectUUID()); + + writer.putString(b.getName()); // Blueprint name? + writer.putString(b.getGuild().getName()); + writer.putInt(20);// \/ Temp \/ + writer.putInt(b.getRank()); + writer.putInt(1); // symbol + writer.putInt(7); //TODO identify these Guild tags?? + writer.putInt(17); + writer.putInt(14); + writer.putInt(14); + writer.putInt(98);// /\ Temp /\ + + } + } + + public int getObjectType() { + return objectType; + } + + public void setObjectType(int value) { + this.objectType = value; + } + + public void setPad(int value) { + this.pad = value; + } + + public int getUUID() { + return objectUUID; + + } + + public int getPad() { + return pad; + } + public int getType() { + return type; + } + public void setType(int type) { + this.type = type; + } + + public int getSize() { + return size; + } + public void setSize(int size) { + this.size = size; + } +} diff --git a/src/engine/net/client/msg/TargetObjectMsg.java b/src/engine/net/client/msg/TargetObjectMsg.java new file mode 100644 index 00000000..3c567887 --- /dev/null +++ b/src/engine/net/client/msg/TargetObjectMsg.java @@ -0,0 +1,81 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class TargetObjectMsg extends ClientNetMsg { + + private int targetType; + private int targetID; + + /** + * This is the general purpose constructor. + */ + public TargetObjectMsg(int targetType, int targetID) { + super(Protocol.SETSELECTEDOBECT); + this.targetType = targetType; + this.targetID = targetID; + } + + /** + * This is the general purpose constructor. + */ + public TargetObjectMsg() { + super(Protocol.SETSELECTEDOBECT); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public TargetObjectMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.SETSELECTEDOBECT, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.targetType); + writer.putInt(this.targetID); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + } + + /** + * @return the targetType + */ + public int getTargetType() { + return targetType; + } + + /** + * @return the targetID + */ + public int getTargetID() { + return targetID; + } + +} diff --git a/src/engine/net/client/msg/TargetedActionMsg.java b/src/engine/net/client/msg/TargetedActionMsg.java new file mode 100644 index 00000000..5f01002a --- /dev/null +++ b/src/engine/net/client/msg/TargetedActionMsg.java @@ -0,0 +1,346 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.PlayerCharacter; + +public class TargetedActionMsg extends ClientNetMsg { + + public static int un2cnt = 65; + + //attack animations + //64: overhead RH swing 1h RH axe? + //65: overhead LH swing + //66: underhand RH uppercut + //67: shoulder high RH swing + //68: underhand RH swing + //69: sweeping LH swing + //70: overhead circle RH swing + //71: RH across body and back swing + //72: RH 1h overhead to 2h swing + //73: RH overhead to cross body swing (bm?) + //74: 2h low stab to cross body slash + //75: unarmed punch unarmed RH + //76: unarmed RH punch LH punch + //77: unarmed LH jab + //78: unarmed LH jab, RH uppercut + //79: kick + //80: roundhouse kick + //81: dagger RH stab dagger RH + //82: dagger LH stab + //83: dagger slash + //84: dagger hard stab + //85: Polearm/staff overhead swing Polearm, Staff + //86: Polearm/staff side swing + //87: Polearm/staff step into overhead swing + //88: swinging RH stab + //89: swinging LF stab + //90: swinging RH stab (faster) + //91: 1H slash across body and back (sword?) + //92: spear stab spear + //93: spear low stab step into + //94: spear swing leg stab + //95: unarmed overhead swing RH, underhand swing LH + //96: inverted weapon across body followed by roundhouse LH swing + //97: step back followed by overhead roundhouse swing 2H + //98: underhand slash (1h sword) 1H RH Sword + //99: fast LH swing (dagger or sword?) + //100: 1h swing RH (sword) 1h axe? + //101: 1h overhead swing (club or sword) + //102: fast 1h underhand swing (club or sword) + //103: step into RH slash 1h + //104: 1h overhead to cross body slash RH + //105: 2h overhead swing (axe, hammer, sword) 2H Axe, Hammer, Sword + //106: step into 2h swing + //107: step int 2h overhead swing + //108: step into 2h swing + //109: bow draw and fire bow + //110: crossbow draw and fire crossbow + //115: throwing axe/hammer? + //116: overhand throwing dagger? + //117: throwing dagger + + private int sourceType; + private int sourceID; + private int targetType; + private int targetID; + private float locX; + private float locZ; + private int unknown01 = 14; + private int unknown02 = 100; //source animation + private float unknown03 = 1f; + private float sourceStamina = 1f; // attackers stamina after attack + private int unknown05 = 6; //signify passive defense + private int unknown06 = 10; //target animation + private float newHealth = 10f; // health after damage + private float damage = 0f; // damage, 0 for miss + + /** + * This is the general purpose constructor. + */ + public TargetedActionMsg(int sourceType, int sourceID, int targetType, int targetID, float locX, float locZ, float sourceStamina, + float newHealth, float damage) { + super(Protocol.TARGETEDACTION); + this.sourceType = sourceType; + this.sourceID = sourceID; + this.targetType = targetType; + this.targetID = targetID; + this.sourceStamina = sourceStamina; + this.locX = locX; + this.locZ = locZ; + this.newHealth = newHealth; + this.damage = damage; + //this.unknown02 = TargetedActionMsg.un2cnt; + } + + /** + * This is a helper constructor. Designed to send an UPDATE only. Damage for + * this constructor is hard coded to ZERO. + */ + public TargetedActionMsg(PlayerCharacter pc) { + super(Protocol.TARGETEDACTION); + this.sourceType = pc.getObjectType().ordinal(); + this.sourceID = pc.getObjectUUID(); + this.targetType = pc.getObjectType().ordinal(); + this.targetID = pc.getObjectUUID(); + this.sourceStamina = pc.getStamina(); + this.locX = pc.getLoc().x; + this.locZ = pc.getLoc().z; + this.newHealth = pc.getCurrentHitpoints(); + this.damage = 0.0f; + } + + public TargetedActionMsg(PlayerCharacter pc,int unknown06) { + super(Protocol.TARGETEDACTION); + this.sourceType = pc.getObjectType().ordinal(); + this.sourceID = pc.getObjectUUID(); + this.targetType = pc.getObjectType().ordinal(); + this.targetID = pc.getObjectUUID(); + this.sourceStamina = pc.getStamina(); + this.locX = pc.getLoc().x; + this.locZ = pc.getLoc().z; + this.newHealth = pc.getCurrentHitpoints(); + this.damage = 0.0f; + } + + public TargetedActionMsg(AbstractCharacter source, AbstractWorldObject target, Float damage, int swingAnimation) { + super(Protocol.TARGETEDACTION); + if (source != null) { + this.sourceType = source.getObjectType().ordinal(); + this.sourceID = source.getObjectUUID(); + this.sourceStamina = source.getStamina(); + } else { + this.sourceType = 0; + this.sourceID = 0; + this.sourceStamina = 0; + } + if (target != null) { + this.targetType = target.getObjectType().ordinal(); + this.targetID = target.getObjectUUID(); + this.locX = target.getLoc().x; + this.locZ = target.getLoc().z; + this.newHealth = target.getHealth(); + this.damage = damage; + + } else { + this.targetType = 0; + this.targetID = 0; + this.locX = 50000f; + this.locZ = -50000f; + this.newHealth = 1f; + this.damage = damage; + } + this.unknown02 = swingAnimation; + //this.unknown02 = TargetedActionMsg.un2cnt; + } + + public TargetedActionMsg(AbstractCharacter source, AbstractWorldObject target, Float damage, int swingAnimation, int dead) { + super(Protocol.TARGETEDACTION); + if (source != null) { + this.sourceType = source.getObjectType().ordinal(); + this.sourceID = source.getObjectUUID(); + this.sourceStamina = source.getStamina(); + } else { + this.sourceType = 0; + this.sourceID = 0; + this.sourceStamina = 0; + } + if (target != null) { + this.targetType = target.getObjectType().ordinal(); + this.targetID = target.getObjectUUID(); + this.locX = target.getLoc().x; + this.locZ = target.getLoc().z; + this.newHealth = target.getCurrentHitpoints(); + this.damage = damage; + this.unknown06 = dead; + + } else { + this.targetType = 0; + this.targetID = 0; + this.locX = 50000f; + this.locZ = -50000f; + this.newHealth = 1f; + this.damage = damage; + } + this.unknown02 = swingAnimation; + //this.unknown02 = TargetedActionMsg.un2cnt; + } + + /** + * Added in an attempt to have mobs fall over after death. + */ + + public TargetedActionMsg(AbstractWorldObject target, boolean kill) { + this(null, target, 0f, 75); + if (kill){ + this.newHealth = -1f; + this.sourceType = 0x101; + this.sourceID = 0x101; + } + } + + /** + * This constructor can be used to create CombatMessages that indicate a block or parry has occurred.
+ *
+ * Set passiveAnimation to 21 for block.
+ * Set passiveAnimation to 22 for parry.
+ */ + public TargetedActionMsg(AbstractCharacter source, int swingAnimation, AbstractWorldObject target, int passiveAnimation) { + this(source, target, 0.0f, swingAnimation); + this.unknown05 = passiveAnimation; + this.unknown06 = 0; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public TargetedActionMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.TARGETEDACTION, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.putInt(this.targetType); + writer.putInt(this.targetID); + writer.putFloat(this.locX); + writer.putFloat(this.locZ); + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + writer.putFloat(this.unknown03); + writer.putFloat(this.sourceStamina); + writer.putInt(this.unknown05); + // writer.putInt(this.unknown06); + + if (this.newHealth < 0) + writer.putInt(55); + else if(damage != 0 && this.unknown05 < 20) + writer.putInt(60); + else + writer.putInt(this.unknown06); + writer.putFloat(this.newHealth); + writer.putFloat(this.damage); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + this.locX = reader.getFloat(); + this.locZ = reader.getFloat(); + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + this.unknown03 = reader.getFloat(); + this.sourceStamina = reader.getFloat(); + this.unknown05 = reader.getInt(); + this.unknown06 = reader.getInt(); + this.newHealth = reader.getFloat(); + this.damage = reader.getFloat(); + } + + /** + * @return the sourceType + */ + public int getSourceType() { + return sourceType; + } + + /** + * @return the sourceID + */ + public int getSourceID() { + return sourceID; + } + + /** + * @return the targetType + */ + public int getTargetType() { + return targetType; + } + + /** + * @return the targetID + */ + public int getTargetID() { + return targetID; + } + + public void setSourceType(int value) { + this.sourceType = value; + } + + public void setSourceID(int value) { + this.sourceID = value; + } + + public void setTargetType(int value) { + this.targetType = value; + } + + public void setTargetID(int value) { + this.targetID = value; + } + + public float getDamage() { + return damage; + } +} diff --git a/src/engine/net/client/msg/TaxCityMsg.java b/src/engine/net/client/msg/TaxCityMsg.java new file mode 100644 index 00000000..3a7a9859 --- /dev/null +++ b/src/engine/net/client/msg/TaxCityMsg.java @@ -0,0 +1,115 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.exception.SerializationException; +import engine.gameManager.BuildingManager; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Building; +import engine.objects.GuildTag; + + +public class TaxCityMsg extends ClientNetMsg { + + + private int buildingID; + private int msgType; + + + public TaxCityMsg(Building building, int msgType) { + super(Protocol.TAXCITY); + this.buildingID = building.getObjectUUID(); + this.msgType = msgType; + + } + + public TaxCityMsg() { + super(Protocol.TAXCITY); + } + + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public TaxCityMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.TAXCITY, origin, reader); + } + //CALL THIS AFTER SANITY CHECKS AND BEFORE UPDATING HEALTH/GOLD. + + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.msgType = reader.getInt(); + reader.getInt(); //object Type.. always building + this.buildingID = reader.getInt(); + reader.getInt(); + reader.getInt(); + + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + + } + + + // Precache and configure this message before we serialize it + + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + + writer.putInt(0); + writer.putInt(GameObjectType.Building.ordinal()); + writer.putInt(this.buildingID); + Building building = BuildingManager.getBuildingFromCache(this.buildingID); + + writer.putInt(0); + writer.putFloat(.2f); + GuildTag._serializeForDisplay(building.getGuild().getGuildTag(),writer); + GuildTag._serializeForDisplay(building.getGuild().getGuildTag(),writer); + + + + } + + public int getGuildID() { + return buildingID; + } + + public int getMsgType() { + return msgType; + } + + public void setMsgType(int msgType) { + this.msgType = msgType; + } + +} diff --git a/src/engine/net/client/msg/TaxResourcesMsg.java b/src/engine/net/client/msg/TaxResourcesMsg.java new file mode 100644 index 00000000..a6a91650 --- /dev/null +++ b/src/engine/net/client/msg/TaxResourcesMsg.java @@ -0,0 +1,123 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Building; + +import java.util.HashMap; + + +public class TaxResourcesMsg extends ClientNetMsg { + + + private int buildingID; + private int msgType; + private HashMap resources; + private float taxPercent; + + + public TaxResourcesMsg(Building building, int msgType) { + super(Protocol.TAXRESOURCES); + this.buildingID = building.getObjectUUID(); + this.msgType = msgType; + + } + + public TaxResourcesMsg() { + super(Protocol.TAXRESOURCES); + } + + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public TaxResourcesMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.TAXRESOURCES, origin, reader); + } + //CALL THIS AFTER SANITY CHECKS AND BEFORE UPDATING HEALTH/GOLD. + + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.msgType = reader.getInt(); + reader.getInt(); //object Type.. always building + this.buildingID = reader.getInt(); + HashMap resourcesTemp = new HashMap<>(); + int size = reader.getInt(); + for (int i=0;i getResources() { + return resources; + } + + public float getTaxPercent() { + return taxPercent; + } + +} diff --git a/src/engine/net/client/msg/TeleportRepledgeListMsg.java b/src/engine/net/client/msg/TeleportRepledgeListMsg.java new file mode 100644 index 00000000..2379bdd2 --- /dev/null +++ b/src/engine/net/client/msg/TeleportRepledgeListMsg.java @@ -0,0 +1,112 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.AbstractNetMsg; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.City; +import engine.objects.PlayerCharacter; + +import java.util.ArrayList; + + +public class TeleportRepledgeListMsg extends ClientNetMsg { + + private PlayerCharacter player; + private boolean isTeleport; + ArrayList cities; + + /** + * This is the general purpose constructor. + */ + public TeleportRepledgeListMsg(PlayerCharacter player, boolean isTeleport) { + super(Protocol.SENDCITYENTRY); + this.player = player; + this.isTeleport = isTeleport; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public TeleportRepledgeListMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.SENDCITYENTRY, origin, reader); + } + + /** + * Copy constructor + */ + public TeleportRepledgeListMsg(TeleportRepledgeListMsg msg) { + super(Protocol.SENDCITYENTRY); + this.player = msg.player; + this.isTeleport = msg.isTeleport; + } + + /** + * @see AbstractNetMsg#getPowerOfTwoBufferSize() + */ + @Override + protected int getPowerOfTwoBufferSize() { + // Larger size for historically larger opcodes + return (16); + } + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + //Do we even want to try this? + } + + // Pre-caches and configures message so data is avaiable + // when we serialize. + + public void configure() { + + if (isTeleport) + cities = City.getCitiesToTeleportTo(player); + else + cities = City.getCitiesToRepledgeTo(player); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + if (isTeleport) + writer.putInt(2); //teleport? + else + writer.putInt(1); //repledge? + + for (int i=0;i<3;i++) + writer.putInt(0); + + writer.putInt(cities.size()); + + for (City city : cities) + City.serializeForClientMsg(city,writer); + } + + public PlayerCharacter getPlayer() { + return this.player; + } + + public boolean isTeleport() { + return this.isTeleport; + } + +} diff --git a/src/engine/net/client/msg/TeleportToPointMsg.java b/src/engine/net/client/msg/TeleportToPointMsg.java new file mode 100644 index 00000000..c94de917 --- /dev/null +++ b/src/engine/net/client/msg/TeleportToPointMsg.java @@ -0,0 +1,222 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractWorldObject; + +public class TeleportToPointMsg extends ClientNetMsg { + + private int sourceType; + private int sourceUUID; + private float endLat; + private float endLon; + private float endAlt; + private int targetType; + private int targetUUID; + private int unknown01; + private int unknown02; + + /** + * This is the general purpose constructor. + */ + public TeleportToPointMsg(AbstractWorldObject ago, float endLat, float endAlt, float endLon, long targetID, int unknown01, int unknown02) { + super(Protocol.TELEPORT); + + this.sourceType = ago.getObjectType().ordinal(); + this.sourceUUID = ago.getObjectUUID(); + this.endLat = endLat; + this.endAlt = endAlt; + this.endLon = endLon; + if (targetID != 0){ + this.targetType = GameObjectType.Building.ordinal(); + this.targetUUID = (int) targetID; + }else{ + this.targetType = 0; + this.targetUUID = 0; + } + + + + this.unknown01 = unknown01; + this.unknown02 = unknown02; + + if (ago.getRegion() != null){ + this.targetType = GameObjectType.Building.ordinal(); + this.targetUUID = ago.getRegion().parentBuildingID; + this.unknown01 = ago.getRegion().level; + this.unknown02 = ago.getRegion().room; + } + } + + /** + * This is the general purpose constructor. + */ + public TeleportToPointMsg() { + super(Protocol.TELEPORT); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public TeleportToPointMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.TELEPORT, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.sourceType); + writer.putInt(this.sourceUUID); + writer.putFloat(this.endLat); + writer.putFloat(this.endAlt); + writer.putFloat(this.endLon); + writer.putInt(this.targetType); + writer.putInt(this.targetUUID); + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.sourceType = reader.getInt(); + this.sourceUUID = reader.getInt(); + this.endLat = reader.getInt(); + this.endAlt = reader.getInt(); + this.endLon = reader.getInt(); + this.targetType = reader.getInt(); + this.targetUUID = reader.getInt(); + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + } + + + /** + * @return the endLat + */ + public float getEndLat() { + return endLat; + } + + /** + * @param endLat + * the endLat to set + */ + public void setEndLat(float endLat) { + this.endLat = endLat; + } + + /** + * @return the endLon + */ + public float getEndLon() { + return endLon; + } + + /** + * @param endLon + * the endLon to set + */ + public void setEndLon(float endLon) { + this.endLon = endLon; + } + + /** + * @return the endAlt + */ + public float getEndAlt() { + return endAlt; + } + + /** + * @param endAlt + * the endAlt to set + */ + public void setEndAlt(float endAlt) { + this.endAlt = endAlt; + } + + + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the unknown02 + */ + public int getUnknown02() { + return unknown02; + } + + /** + * @param unknown02 + * the unknown02 to set + */ + public void setUnknown02(int unknown02) { + this.unknown02 = unknown02; + } + + public int getSourceType() { + return sourceType; + } + + public void setSourceType(int sourceType) { + this.sourceType = sourceType; + } + + public int getSourceUUID() { + return sourceUUID; + } + + public void setSourceUUID(int sourceUUID) { + this.sourceUUID = sourceUUID; + } + + public int getTargetType() { + return targetType; + } + + public void setTargetType(int targetType) { + this.targetType = targetType; + } + + public int getTargetUUID() { + return targetUUID; + } + + public void setTargetUUID(int targetUUID) { + this.targetUUID = targetUUID; + } + +} diff --git a/src/engine/net/client/msg/TerritoryChangeMessage.java b/src/engine/net/client/msg/TerritoryChangeMessage.java new file mode 100644 index 00000000..57f74369 --- /dev/null +++ b/src/engine/net/client/msg/TerritoryChangeMessage.java @@ -0,0 +1,95 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.PlayerCharacter; +import engine.objects.Realm; + +public class TerritoryChangeMessage extends ClientNetMsg { + + + + private Realm realm; + private PlayerCharacter realmOwner; + + + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public TerritoryChangeMessage(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.TERRITORYCHANGE, origin, reader); + } + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + + + } + + public TerritoryChangeMessage() { + super(Protocol.TERRITORYCHANGE); + } + + public TerritoryChangeMessage(PlayerCharacter guildLeader, Realm realm) { + super(Protocol.TERRITORYCHANGE); + + this.realm = realm; + this.realmOwner = guildLeader; + } + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + + writer.putString(realm.getRealmName()); + if(this.realmOwner != null){ + writer.putString(this.realmOwner.getCombinedName()); + writer.putInt(PlayerCharacter.GetPlayerRealmTitle(this.realmOwner)); + writer.putInt(1); + writer.put((byte)1); + writer.put((byte)1); + writer.putInt(realm.getCharterType()); + if (this.realmOwner != null && this.realmOwner.getGuild() != null) + writer.putString(this.realmOwner.getGuild().getName()); + else + writer.putString("None"); + writer.put((byte)0); + }else{ + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.put((byte)1); + writer.put((byte)1); + writer.putInt(0); + writer.putInt(0); + writer.put((byte)0); + } + + + + } + + + + +} diff --git a/src/engine/net/client/msg/ToggleCombatMsg.java b/src/engine/net/client/msg/ToggleCombatMsg.java new file mode 100644 index 00000000..56e7de20 --- /dev/null +++ b/src/engine/net/client/msg/ToggleCombatMsg.java @@ -0,0 +1,85 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class ToggleCombatMsg extends ClientNetMsg { + + private int sourceType; + private int sourceID; + private boolean toggleCombat; + + /** + * This is the general purpose constructor. + */ + public ToggleCombatMsg() { + super(Protocol.COMBATMODE); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ToggleCombatMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.COMBATMODE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.put(this.toggleCombat ? (byte) 0x01 : (byte) 0x00); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.toggleCombat = (reader.get() == (byte) 0x01) ? true : false; + } + + public int getSourceType() { + return this.sourceType; + } + + public int getSourceID() { + return this.sourceID; + } + + public boolean toggleCombat() { + return this.toggleCombat; + } + + public void setSourceType(int value) { + this.sourceType = value; + } + + public void setSourceID(int value) { + this.sourceID = value; + } + + public void setToggleCombat(boolean value) { + this.toggleCombat = value; + } +} diff --git a/src/engine/net/client/msg/ToggleLfgRecruitingMsg.java b/src/engine/net/client/msg/ToggleLfgRecruitingMsg.java new file mode 100644 index 00000000..2dfd611c --- /dev/null +++ b/src/engine/net/client/msg/ToggleLfgRecruitingMsg.java @@ -0,0 +1,96 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class ToggleLfgRecruitingMsg extends ClientNetMsg { + + private int sourceType; + private int sourceID; + private byte toggleLfgRecruiting; + private byte unknown01; + + /** + * This is the general purpose constructor. + */ + public ToggleLfgRecruitingMsg() { + super(Protocol.MODIFYGUILDSTATE); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ToggleLfgRecruitingMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.MODIFYGUILDSTATE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.put(this.toggleLfgRecruiting); + writer.put(this.unknown01); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.toggleLfgRecruiting = reader.get(); + this.unknown01 = reader.get(); + } + + public int getSourceType() { + return this.sourceType; + } + + public int getSourceID() { + return this.sourceID; + } + + public byte toggleLfgRecruiting() { + return this.toggleLfgRecruiting; + } + + public byte getUnknown01() { + return this.unknown01; + } + + public void setSourceType(int value) { + this.sourceType = value; + } + + public void setSourceID(int value) { + this.sourceID = value; + } + + public void setToggleLfgRecruiting(byte value) { + this.toggleLfgRecruiting = value; + } + + public void setUnknown01(byte value) { + this.unknown01 = value; + } + +} diff --git a/src/engine/net/client/msg/ToggleSitStandMsg.java b/src/engine/net/client/msg/ToggleSitStandMsg.java new file mode 100644 index 00000000..90446503 --- /dev/null +++ b/src/engine/net/client/msg/ToggleSitStandMsg.java @@ -0,0 +1,84 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class ToggleSitStandMsg extends ClientNetMsg { + + private int sourceType; + private int sourceID; + private boolean toggleSitStand; + + /** + * This is the general purpose constructor. + */ + public ToggleSitStandMsg() { + super(Protocol.TOGGLESITSTAND); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ToggleSitStandMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.TOGGLESITSTAND, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.put(this.toggleSitStand ? (byte) 0x01 : (byte) 0x00); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.toggleSitStand = (reader.get() == (byte) 0x01) ? true : false; + } + + public int getSourceType() { + return this.sourceType; + } + + public int getSourceID() { + return this.sourceID; + } + + public boolean toggleSitStand() { + return this.toggleSitStand; + } + + public void setSourceType(int value) { + this.sourceType = value; + } + + public void setSourceID(int value) { + this.sourceID = value; + } + + public void setToggleSitStand(boolean value) { + this.toggleSitStand = value; + } +} diff --git a/src/engine/net/client/msg/TrackArrowMsg.java b/src/engine/net/client/msg/TrackArrowMsg.java new file mode 100644 index 00000000..9655be18 --- /dev/null +++ b/src/engine/net/client/msg/TrackArrowMsg.java @@ -0,0 +1,61 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class TrackArrowMsg extends ClientNetMsg { + + protected float direction; + + public TrackArrowMsg(float direction) { + super(Protocol.ARCTRACKOBJECT); + this.direction = direction; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public TrackArrowMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ARCTRACKOBJECT, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putFloat(this.direction); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.direction = reader.getFloat(); + } + + public float getDirection() { + return this.direction; + } + + public void setDirection(float value) { + this.direction = value; + } +} diff --git a/src/engine/net/client/msg/TrackWindowMsg.java b/src/engine/net/client/msg/TrackWindowMsg.java new file mode 100644 index 00000000..1241320b --- /dev/null +++ b/src/engine/net/client/msg/TrackWindowMsg.java @@ -0,0 +1,148 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.net.AbstractConnection; +import engine.net.AbstractNetMsg; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.*; + +import java.util.HashSet; + + +public class TrackWindowMsg extends ClientNetMsg { + + private int powerToken; + private PlayerCharacter source = null; + private HashSet characters = new HashSet<>(); + + /** + * This is the general purpose constructor. + */ + public TrackWindowMsg(int powerToken, HashSet characters) { + super(Protocol.ARCTRACKINGLIST); + this.powerToken = powerToken; + this.characters = characters; + } + + public TrackWindowMsg(TrackWindowMsg trackWindowMsg) { + super(Protocol.ARCTRACKINGLIST); + this.powerToken = trackWindowMsg.powerToken; + this.source = trackWindowMsg.source; + this.characters = trackWindowMsg.characters; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public TrackWindowMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ARCTRACKINGLIST, origin, reader); + } + + /** + * @see AbstractNetMsg#getPowerOfTwoBufferSize() + */ + @Override + protected int getPowerOfTwoBufferSize() { + // Larger size for historically larger opcodes + return (13); + } + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.powerToken); + writer.putInt(characters.size()); + for (AbstractCharacter ac : characters) { + boolean isGroup = false; + if (this.source != null && ac.getObjectType().equals(GameObjectType.PlayerCharacter)) { + if (Group.sameGroup((PlayerCharacter)ac, this.source)) + isGroup = true; + } + AbstractCharacter.serializeForTrack(ac,writer, isGroup); + } + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.powerToken = reader.getInt(); + + int size = reader.getInt(); + for (int i=0;i getCharacters() { + return this.characters; + } + + public void setPowerToken(int value) { + this.powerToken = value; + } + + public void setCharacters(HashSet value) { + this.characters = value; + } + + public void addCharacter(PlayerCharacter value) { + if (value != null) + this.characters.add(value); + } + + public void clearChracters() { + this.characters.clear(); + } + + public void setSource(PlayerCharacter value) { + this.source = value; + } +} diff --git a/src/engine/net/client/msg/TradeRequestMsg.java b/src/engine/net/client/msg/TradeRequestMsg.java new file mode 100644 index 00000000..25e64ab4 --- /dev/null +++ b/src/engine/net/client/msg/TradeRequestMsg.java @@ -0,0 +1,130 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractGameObject; + +/** + * Request trade + * + * @author Eighty + */ +public class TradeRequestMsg extends ClientNetMsg { + + private int unknown01; //pad? + private int playerType; + private int playerID; + private int sourceType; + private int sourceID; + + + /** + * This is the general purpose constructor + */ + public TradeRequestMsg(int unknown01, AbstractGameObject player, AbstractGameObject target) { + super(Protocol.REQUESTTOTRADE); + this.unknown01 = unknown01; + this.sourceType = player.getObjectType().ordinal(); + this.sourceID = player.getObjectUUID(); + this.playerType = target.getObjectType().ordinal(); + this.playerID = target.getObjectUUID(); + + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public TradeRequestMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.REQUESTTOTRADE, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + unknown01 = reader.getInt(); + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.playerType = reader.getInt(); + this.playerID = reader.getInt(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + writer.putInt(unknown01); + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.putInt(this.playerType); + writer.putInt(this.playerID); + + + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + + public int getPlayerType() { + return playerType; + } + + public void setPlayerType(int playerType) { + this.playerType = playerType; + } + + public int getPlayerID() { + return playerID; + } + + public void setPlayerID(int playerID) { + this.playerID = playerID; + } + + public int getSourceType() { + return sourceType; + } + + public void setSourceType(int sourceType) { + this.sourceType = sourceType; + } + + public int getSourceID() { + return sourceID; + } + + public void setSourceID(int sourceID) { + this.sourceID = sourceID; + } + +} diff --git a/src/engine/net/client/msg/TrainMsg.java b/src/engine/net/client/msg/TrainMsg.java new file mode 100644 index 00000000..0c81450a --- /dev/null +++ b/src/engine/net/client/msg/TrainMsg.java @@ -0,0 +1,361 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum; +import engine.Enum.ProtectionState; +import engine.exception.MsgSendException; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.gameManager.PowersManager; +import engine.gameManager.SessionManager; +import engine.net.*; +import engine.net.client.ClientConnection; +import engine.net.client.Protocol; +import engine.objects.*; +import engine.powers.PowersBase; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.util.concurrent.ConcurrentHashMap; + + +public class TrainMsg extends ClientNetMsg { + + private int npcType; + private int npcID; + private int unknown01 = 1; + private int trainCost01; //why two trainer costs? + private int trainCost02; //why two trainer costs? + private boolean isSkill; //true: skill; false: power + private int token; + private boolean unknown02 = true; + private String ok = ""; + private int unknown03 = 0; + + /** + * This is the general purpose constructor. + */ + public TrainMsg() { + super(Protocol.TRAINSKILL); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public TrainMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.TRAINSKILL, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.npcType); + writer.putInt(this.npcID); + writer.putInt(this.unknown01); + writer.putInt(trainCost01); + writer.putInt(trainCost02); + writer.put((this.isSkill == true) ? (byte)0x01 : (byte)0x00); + writer.putInt(this.token); + writer.put((this.unknown02 == true) ? (byte)0x01 : (byte)0x00); + writer.putString(this.ok); + writer.putInt(this.unknown03); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.npcType = reader.getInt(); + this.npcID = reader.getInt(); + this.unknown01 = reader.getInt(); + this.trainCost01 = reader.getInt(); + this.trainCost02 = reader.getInt(); + this.isSkill = (reader.get() == (byte)0x01) ? true : false; + this.token = reader.getInt(); + this.unknown02 = (reader.get() == (byte)0x01) ? true : false; + this.ok = reader.getString(); + this.unknown03 = reader.getInt(); + } + + public int getToken() { + return this.token; + } + + public boolean isSkill() { + return this.isSkill; + } + + public int getNpcType() { + return this.npcType; + } + + public int getNpcID() { + return this.npcID; + } + + public static void train(TrainMsg msg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter playerCharacter = SessionManager.getPlayerCharacter(origin); + Dispatch dispatch; + + if (playerCharacter == null) + return; + + NPC npc = NPC.getFromCache(msg.npcID); + + if (npc == null) + return; + + if (origin.trainLock.tryLock()){ + try{ + Item gold = playerCharacter.getCharItemManager().getGoldInventory(); + + if (gold == null) + return; + + if (!gold.validForInventory(origin, playerCharacter, playerCharacter.getCharItemManager())) + return; + + boolean canTrain = false; + if (msg.isSkill) { + + //Get skill + SkillsBase sb = DbManager.SkillsBaseQueries.GET_BASE_BY_TOKEN(msg.token); + ConcurrentHashMap skills = playerCharacter.getSkills(); + + if (sb == null || skills == null) + return; + + CharacterSkill sk = skills.get(sb.getName()); + + if (sk == null) + return; + + if (sk.getSkillsBase().getToken() == 40661438){ + int maxValue = 15; + + + if (MaxSkills.MaxSkillsSet.get(252647) != null) + for (MaxSkills maxSkills: MaxSkills.MaxSkillsSet.get(252647)){ + if (maxSkills.getSkillToken() != sk.getToken()) + continue; + + if (maxSkills.getSkillLevel() > npc.getLevel()) + continue; + maxValue += maxSkills.getMaxSkillPercent(); + } + if (maxValue> sk.getModifiedAmountBeforeMods()){ + canTrain = true; + } + + } + if (canTrain == false) + if (npc.getContract() != null && npc.getContract().getExtraRune() != 0){ + int maxValue = 15; + + + if (MaxSkills.MaxSkillsSet.get(npc.getContract().getExtraRune()) != null) + for (MaxSkills maxSkills: MaxSkills.MaxSkillsSet.get(npc.getContract().getExtraRune())){ + if (maxSkills.getSkillToken() != sk.getToken()) + continue; + + if (maxSkills.getSkillLevel() > npc.getLevel()) + continue; + maxValue += maxSkills.getMaxSkillPercent(); + } + if (maxValue > sk.getModifiedAmountBeforeMods()){ + canTrain = true; + } + + + } + if (canTrain == false){ + int maxValue = 15; + if (MaxSkills.MaxSkillsSet.get(npc.getContractID()) != null) + for (MaxSkills maxSkills: MaxSkills.MaxSkillsSet.get(npc.getContractID())){ + if (maxSkills.getSkillToken() != sk.getToken()) + continue; + + if (maxSkills.getSkillLevel() > npc.getLevel()) + continue; + maxValue += maxSkills.getMaxSkillPercent(); + } + if (maxValue > sk.getModifiedAmountBeforeMods()){ + canTrain = true; + } + } + + if (canTrain == false){ + int maxValue = 15; + if (MaxSkills.MaxSkillsSet.get(npc.getContract().getClassID()) != null) + for (MaxSkills maxSkills: MaxSkills.MaxSkillsSet.get(npc.getContract().getClassID())){ + if (maxSkills.getSkillToken() != sk.getToken()) + continue; + + if (maxSkills.getSkillLevel() > npc.getLevel()) + continue; + maxValue += maxSkills.getMaxSkillPercent(); + } + if (maxValue > sk.getModifiedAmountBeforeMods()){ + canTrain = true; + } + } + + if (canTrain == false){ + int maxValue = 15; + if (MaxSkills.MaxSkillsSet.get(npc.extraRune2) != null) + for (MaxSkills maxSkills: MaxSkills.MaxSkillsSet.get(npc.getContract().getClassID())){ + if (maxSkills.getSkillToken() != sk.getToken()) + continue; + + if (maxSkills.getSkillLevel() > npc.getLevel()) + continue; + maxValue += maxSkills.getMaxSkillPercent(); + } + if (maxValue > sk.getModifiedAmountBeforeMods()){ + canTrain = true; + } + } + + if (canTrain == false){ + ChatManager.chatSystemError(playerCharacter, "NPC cannot train that skill any higher"); + return; + } + + float cost = sk.getTrainingCost(playerCharacter, npc); + float profitCost = cost * npc.getSellPercent(playerCharacter); + + profitCost += .5f; + if (profitCost > playerCharacter.getCharItemManager().getGoldInventory().getNumOfItems()) + return; + Building b = npc.getBuilding(); + if (b != null && b.getProtectionState().equals(ProtectionState.NPC)) + b = null; + + if (b != null && b.getStrongboxValue() + (profitCost - cost) > b.getMaxGold()){ + ErrorPopupMsg.sendErrorPopup(playerCharacter, 206); + return; + } + + if (playerCharacter.getCharItemManager().getGoldInventory().getNumOfItems() - profitCost < 0) + return; + + if (playerCharacter.getCharItemManager().getGoldInventory().getNumOfItems() - profitCost > MBServerStatics.PLAYER_GOLD_LIMIT) + return; + + + //attempt to train + if (sk.train(playerCharacter)) { + playerCharacter.getCharItemManager().buyFromNPC(b, (int)profitCost, (int)(profitCost - cost)); + + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + //update trainer window + + if (npc != null) { + TrainerInfoMsg tim = new TrainerInfoMsg(msg.npcType, msg.npcID, npc.getSellPercent(playerCharacter)); + tim.setTrainPercent(npc.getSellPercent(playerCharacter)); + dispatch = Dispatch.borrow(playerCharacter, tim); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + } + + } else { + //Get Power + int token = msg.token; + + if (MBServerStatics.POWERS_DEBUG) { + ChatManager.chatSayInfo(playerCharacter, "Training Power: " + + Integer.toHexString(msg.token) + " (" + msg.token + ')'); + System.out.println("Training Power: " + + Integer.toHexString(msg.token) + " (" + msg.token + ')'); + } + + PowersBase pb = PowersManager.getPowerByToken(token); + ConcurrentHashMap powers = playerCharacter.getPowers(); + if (pb == null || powers == null) + return; + + if (pb.isWeaponPower) + return; + CharacterPower cp = null; + if (powers.containsKey(token)) + cp = powers.get(token); + if (cp == null) + return; + + //attempt to train + float cost = (int) cp.getTrainingCost(playerCharacter, npc); + float profitCost = cost * npc.getSellPercent(playerCharacter); + profitCost += .5f; + if (profitCost > playerCharacter.getCharItemManager().getGoldInventory().getNumOfItems()){ + // ChatManager.chatSystemError(pc, "You do not have enough gold to train this skill."); + return; + } + + Building b = npc.getBuilding(); + + if (b != null && b.getProtectionState().equals(ProtectionState.NPC)) + b = null; + + if (b != null && b.getStrongboxValue() + (profitCost - cost) > b.getMaxGold()){ + ErrorPopupMsg.sendErrorPopup(playerCharacter, 206); + return; + } + if (cp.train(playerCharacter)) { + + if (!playerCharacter.getCharItemManager().buyFromNPC(b, (int)profitCost, (int)(profitCost - cost))) + ChatManager.chatSystemError(playerCharacter, "Failed to Withdrawl gold from inventory. Contact CCR"); + + //train succeeded + + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + //update trainer window + + if (npc != null) { + TrainerInfoMsg tim = new TrainerInfoMsg(msg.npcType, msg.npcID, npc.getSellPercent(playerCharacter)); + tim.setTrainPercent(npc.getSellPercent(playerCharacter)); + dispatch = Dispatch.borrow(playerCharacter, tim); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + } + } + }catch(Exception e){ + Logger.error(e); + }finally{ + origin.trainLock.unlock(); + } + + + } + + + + + + + + + } + + public static float getTrainPercent(NPC npc) { + return 0f; + } +} diff --git a/src/engine/net/client/msg/TrainerInfoMsg.java b/src/engine/net/client/msg/TrainerInfoMsg.java new file mode 100644 index 00000000..f44712dd --- /dev/null +++ b/src/engine/net/client/msg/TrainerInfoMsg.java @@ -0,0 +1,101 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class TrainerInfoMsg extends ClientNetMsg { + + private int objectType; + private int objectID; + private float trainPercent; + + /** + * This is the general purpose constructor. + */ + public TrainerInfoMsg(int objectType, int objectID, float trainPercent) { + super(Protocol.TRAINERLIST); + this.objectType = objectType; + this.objectID = objectID; + this.trainPercent = trainPercent; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public TrainerInfoMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.TRAINERLIST, origin, reader); + } + + /** + * Copy constructor + */ + public TrainerInfoMsg(TrainerInfoMsg msg) { + super(Protocol.TRAINERLIST); + this.objectType = msg.objectType; + this.objectID = msg.objectID; + this.trainPercent = msg.trainPercent; + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.get(); + this.objectType = reader.getInt(); + this.objectID = reader.getInt(); + this.trainPercent = reader.getFloat(); + reader.getInt(); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.put((byte)0); + writer.putInt(this.objectType); + writer.putInt(this.objectID); + writer.putFloat(this.trainPercent); + writer.putInt(0); + } + + public int getObjectType() { + return this.objectType; + } + + public int getObjectID() { + return this.objectID; + } + + public float getTrainPercent() { + return this.trainPercent; + } + + public void setObjectType(int value) { + this.objectType = value; + } + + public void setObjectID(int value) { + this.objectID = value; + } + + public void setTrainPercent(float value) { + this.trainPercent = value; + } +} diff --git a/src/engine/net/client/msg/TransferAssetMsg.java b/src/engine/net/client/msg/TransferAssetMsg.java new file mode 100644 index 00000000..0f354801 --- /dev/null +++ b/src/engine/net/client/msg/TransferAssetMsg.java @@ -0,0 +1,99 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class TransferAssetMsg extends ClientNetMsg { + + private int objectType; + private int objectID; + private int targetType; + private int targetID; + private int pad = 0; + +/** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ +public TransferAssetMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.TRANSFERASSET, origin, reader); +} +/** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ +@Override +protected void _deserialize(ByteBufferReader reader) { + this.pad = reader.getInt(); + this.objectType = reader.getInt(); + this.objectID = reader.getInt(); + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + reader.getShort(); +} + +/** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ +@Override +protected void _serialize(ByteBufferWriter writer) throws SerializationException { + writer.putInt(this.pad); + writer.putInt(this.objectType); + writer.putInt(this.objectID); + writer.putInt(this.targetType); + writer.putInt(this.targetID); + writer.putShort((short)0); + +} + +public int getObjectType() { + return objectType; +} + +public void setObjectType(int value) { +this.objectType = value; +} + +public void setPad(int value) { +this.pad = value; +} + +public int getPad() { +return pad; +} + +public int getTargetType() { + return targetType; +} +public void setTargetObject(int targetObject) { + this.targetType = targetObject; +} +public int getTargetID() { + return targetID; +} +public void setTargetID(int targetID) { + this.targetID = targetID; +} +public int getObjectID() { + return objectID; +} +public void setObjectID(int objectID) { + this.objectID = objectID; +} + + +} \ No newline at end of file diff --git a/src/engine/net/client/msg/TransferBuildingMsg.java b/src/engine/net/client/msg/TransferBuildingMsg.java new file mode 100644 index 00000000..63a7f408 --- /dev/null +++ b/src/engine/net/client/msg/TransferBuildingMsg.java @@ -0,0 +1,97 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class TransferBuildingMsg extends ClientNetMsg { + + private int objectType; + private int objectID; + private int targetType; + private int targetID; + private int pad = 0; + +/** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ +public TransferBuildingMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.TRANSFERASSET, origin, reader); +} +/** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ +@Override +protected void _deserialize(ByteBufferReader reader) { + this.pad = reader.getInt(); + this.objectType = reader.getInt(); + this.objectID = reader.getInt(); + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); +} + +/** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ +@Override +protected void _serialize(ByteBufferWriter writer) throws SerializationException { + writer.putInt(this.pad); + writer.putInt(this.objectType); + writer.putInt(this.objectID); + writer.putInt(this.targetType); + writer.putInt(this.targetID); + +} + +public int getObjectType() { + return objectType; +} + +public void setObjectType(int value) { +this.objectType = value; +} + +public void setPad(int value) { +this.pad = value; +} + +public int getPad() { +return pad; +} + +public int getTargetType() { + return targetType; +} +public void setTargetObject(int targetObject) { + this.targetType = targetObject; +} +public int getTargetID() { + return targetID; +} +public void setTargetID(int targetID) { + this.targetID = targetID; +} +public int getObjectID() { + return objectID; +} +public void setObjectID(int objectID) { + this.objectID = objectID; +} + + +} \ No newline at end of file diff --git a/src/engine/net/client/msg/TransferGoldFromInventoryToVaultMsg.java b/src/engine/net/client/msg/TransferGoldFromInventoryToVaultMsg.java new file mode 100644 index 00000000..de31e72e --- /dev/null +++ b/src/engine/net/client/msg/TransferGoldFromInventoryToVaultMsg.java @@ -0,0 +1,105 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +/** + * Transfer gold from inventory to vault + * + * @author Eighty + */ + +public class TransferGoldFromInventoryToVaultMsg extends ClientNetMsg { + + private int playerID; + private int accountID; + private int npcID; + + private int amount; + + /** + * This is the general purpose constructor + */ + public TransferGoldFromInventoryToVaultMsg() { + super(Protocol.GOLDTOVAULT); + + } + public TransferGoldFromInventoryToVaultMsg(int playerID, int npcID, int accountID, int amount) { + super(Protocol.GOLDTOVAULT); + this.playerID = playerID; + this.npcID = npcID; + this.accountID = accountID; + this.amount = amount; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public TransferGoldFromInventoryToVaultMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.GOLDTOVAULT, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + reader.getInt(); + this.playerID = reader.getInt(); + reader.getInt(); + this.accountID = reader.getInt(); + reader.getInt(); + this.npcID = reader.getInt(); + this.amount = reader.getInt(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + writer.putInt(GameObjectType.PlayerCharacter.ordinal()); + writer.putInt(this.playerID); + writer.putInt(GameObjectType.Account.ordinal()); + writer.putInt(this.accountID); + writer.putInt(GameObjectType.NPC.ordinal()); + writer.putInt(this.npcID); + + writer.putInt(this.amount); + } + + + /** + * @return the amount + */ + public int getAmount() { + return amount; + } + + /** + * @param amount the amount to set + */ + public void setAmount(int amount) { + this.amount = amount; + } + +} diff --git a/src/engine/net/client/msg/TransferGoldFromVaultToInventoryMsg.java b/src/engine/net/client/msg/TransferGoldFromVaultToInventoryMsg.java new file mode 100644 index 00000000..580b8769 --- /dev/null +++ b/src/engine/net/client/msg/TransferGoldFromVaultToInventoryMsg.java @@ -0,0 +1,132 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +/** + * Transfer gold from vault to inventory + * + * @author Eighty + */ +public class TransferGoldFromVaultToInventoryMsg extends ClientNetMsg { + + private long unknown01; + private long playerCompID; + private long accountCompID; + private int amount; + + /** + * This is the general purpose constructor + */ + public TransferGoldFromVaultToInventoryMsg(long unknown01, long playerCompID, long accountCompID, int amount) { + super(Protocol.TRANSFERGOLDFROMVAULTTOINVENTORY); + this.unknown01 = unknown01; + this.playerCompID = playerCompID; + this.accountCompID = accountCompID; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public TransferGoldFromVaultToInventoryMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.TRANSFERGOLDFROMVAULTTOINVENTORY, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + unknown01 = reader.getLong(); + playerCompID = reader.getLong(); + accountCompID = reader.getLong(); + amount = reader.getInt(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + writer.putLong(unknown01); + writer.putLong(playerCompID); + writer.putLong(accountCompID); + writer.putInt(amount); + } + + /** + * @return the unknown01 + */ + public long getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 the unknown01 to set + */ + public void setUnknown01(long unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the playerCompID + */ + public long getPlayerCompID() { + return playerCompID; + } + + /** + * @param playerCompID the playerCompID to set + */ + public void setPlayerCompID(long playerCompID) { + this.playerCompID = playerCompID; + } + + /** + * @return the unknown02 + */ + public long getAccountCompID() { + return accountCompID; + } + + /** + * @param unknown02 the unknown02 to set + */ + public void setAccountCompID(long accountCompID) { + this.accountCompID = accountCompID; + } + + /** + * @return the amount + */ + public int getAmount() { + return amount; + } + + /** + * @param amount the amount to set + */ + public void setAmount(int amount) { + this.amount = amount; + } + +} diff --git a/src/engine/net/client/msg/TransferGoldToFromBuildingMsg.java b/src/engine/net/client/msg/TransferGoldToFromBuildingMsg.java new file mode 100644 index 00000000..488c8627 --- /dev/null +++ b/src/engine/net/client/msg/TransferGoldToFromBuildingMsg.java @@ -0,0 +1,149 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +/** + * Transfer gold to/from building strongbox/reserve + * + * @author Eighty + */ + +public class TransferGoldToFromBuildingMsg extends ClientNetMsg { + + private int direction; //Maybe? 1 and 2 + private int failReason; + private int objectType; + private int objectID; + private int amount; + private int unknown01; + private int unknown02; + + /** + * This is the general purpose constructor + */ + public TransferGoldToFromBuildingMsg() { + super(Protocol.TRANSFERGOLDTOFROMBUILDING); + this.direction = 0; + this.failReason = 0; + this.objectType = 0; + this.objectID = 0; + this.amount = 0; + this.unknown01 = 0; + this.unknown02 = 0; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public TransferGoldToFromBuildingMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.TRANSFERGOLDTOFROMBUILDING, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + this.direction = reader.getInt(); + this.failReason = reader.getInt(); + this.objectType = reader.getInt(); + this.objectID = reader.getInt(); + this.amount = reader.getInt(); + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + writer.putInt(this.direction); + writer.putInt(this.failReason); + writer.putInt(this.objectType); + writer.putInt(this.objectID); + writer.putInt(this.amount); + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + + } + + public int getDirection() { + return this.direction; + } + + public int getAmount() { + return this.amount; + } + + public int getUnknown01() { + return this.unknown01; + } + + public int getUnknown02() { + return this.unknown02; + } + + public void setDirection(int value) { + this.direction = value; + } + + + public void setAmount(int value) { + this.amount = value; + } + + public void setUnknown01(int value) { + this.unknown01 = value; + } + + public void setUnknown02(int value) { + this.unknown02 = value; + } + + public void setFailReason(int failReason) { + this.failReason = failReason; + } + + public int getFailReason() { + return failReason; + } + + + public int getObjectType() { + return objectType; + } + + public void setObjectType(int objectType) { + this.objectType = objectType; + } + + public int getObjectID() { + return objectID; + } + + public void setObjectID(int objectID) { + this.objectID = objectID; + } +} diff --git a/src/engine/net/client/msg/TransferItemFromBankToInventoryMsg.java b/src/engine/net/client/msg/TransferItemFromBankToInventoryMsg.java new file mode 100644 index 00000000..b68c7df4 --- /dev/null +++ b/src/engine/net/client/msg/TransferItemFromBankToInventoryMsg.java @@ -0,0 +1,220 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +/** + * Transfer item from bank to inventory + * + * @author Eighty + */ + +public class TransferItemFromBankToInventoryMsg extends ClientNetMsg { + + private long playerCompID1; + private long playerCompID2; + private int type; + private int objectUUID; + private int unknown1; + private int unknown2; + private int numItems; + private byte unknown4; + + /** + * This is the general purpose constructor + */ + public TransferItemFromBankToInventoryMsg(long playerCompID1, + long playerCompID2, int type, int objectUUID, int unknown1, + int unknown2, int numItems, byte unknown4) { + super(Protocol.TRANSFERITEMFROMBANK); + this.playerCompID1 = playerCompID1; + this.playerCompID2 = playerCompID2; + this.type = type; + this.objectUUID = objectUUID; + this.unknown1 = unknown1; + this.unknown2 = unknown2; + this.numItems = numItems; + this.unknown4 = unknown4; + } + + public TransferItemFromBankToInventoryMsg(TransferItemFromInventoryToBankMsg msg) { + super(Protocol.TRANSFERITEMFROMBANK); + this.playerCompID1 = msg.getPlayerCompID1(); + this.playerCompID2 = msg.getPlayerCompID2(); + this.type = msg.getType(); + this.objectUUID = msg.getUUID(); + this.unknown1 = msg.getUnknown1(); + this.unknown2 = msg.getUnknown2(); + this.numItems = msg.getNumItems(); + this.unknown4 = msg.getUnknown4(); + } + + /** + * This is the general purpose constructor + */ + public TransferItemFromBankToInventoryMsg() { + super(Protocol.TRANSFERITEMFROMBANK); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public TransferItemFromBankToInventoryMsg(AbstractConnection origin, + ByteBufferReader reader) { + super(Protocol.TRANSFERITEMFROMBANK, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + playerCompID1 = reader.getLong(); + playerCompID2 = reader.getLong(); + type = reader.getInt(); + objectUUID = reader.getInt(); + unknown1 = reader.getInt(); + unknown2 = reader.getInt(); + numItems = reader.getInt(); + unknown4 = reader.get(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + writer.putLong(playerCompID1); + writer.putLong(playerCompID2); + writer.putInt(type); + writer.putInt(objectUUID); + writer.putInt(unknown1); + writer.putInt(unknown2); + writer.putInt(numItems); + writer.put(unknown4); + } + + /** + * @return the playerCompID1 + */ + public long getPlayerCompID1() { + return playerCompID1; + } + + /** + * @param playerCompID1 the playerCompID1 to set + */ + public void setPlayerCompID1(long playerCompID1) { + this.playerCompID1 = playerCompID1; + } + + /** + * @return the playerCompID2 + */ + public long getPlayerCompID2() { + return playerCompID2; + } + + /** + * @param playerCompID2 the playerCompID2 to set + */ + public void setPlayerCompID2(long playerCompID2) { + this.playerCompID2 = playerCompID2; + } + + /** + * @return the type + */ + public int getType() { + return type; + } + + /** + * @param type the type to set + */ + public void setType(int type) { + this.type = type; + } + + /** + * @return the objectUUID + */ + public int getUUID() { + return objectUUID; + } + + /** + * @return the unknown1 + */ + public int getUnknown1() { + return unknown1; + } + + /** + * @param unknown1 the unknown1 to set + */ + public void setUnknown1(int unknown1) { + this.unknown1 = unknown1; + } + + /** + * @return the unknown2 + */ + public int getUnknown2() { + return unknown2; + } + + /** + * @param unknown2 the unknown2 to set + */ + public void setUnknown2(int unknown2) { + this.unknown2 = unknown2; + } + + /** + * @return the numItems + */ + public int getNumItems() { + return numItems; + } + + /** + * @param numItems the numItems to set + */ + public void setNumItems(int numItems) { + this.numItems = numItems; + } + + /** + * @return the unknown4 + */ + public byte getUnknown4() { + return unknown4; + } + + /** + * @param unknown4 the unknown4 to set + */ + public void setUnknown4(byte unknown4) { + this.unknown4 = unknown4; + } + +} diff --git a/src/engine/net/client/msg/TransferItemFromEquipToInventoryMsg.java b/src/engine/net/client/msg/TransferItemFromEquipToInventoryMsg.java new file mode 100644 index 00000000..7aef05b3 --- /dev/null +++ b/src/engine/net/client/msg/TransferItemFromEquipToInventoryMsg.java @@ -0,0 +1,91 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractGameObject; + +public class TransferItemFromEquipToInventoryMsg extends ClientNetMsg { + + private int playerType; + private int playerUUID; + private int slotNumber; + + /** + * This is the general purpose constructor. + */ + public TransferItemFromEquipToInventoryMsg(AbstractGameObject ago, int slotNumber) { + super(Protocol.UNEQUIP); + this.playerType = ago.getObjectType().ordinal(); + this.playerUUID = ago.getObjectUUID(); + this.slotNumber = slotNumber; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public TransferItemFromEquipToInventoryMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.UNEQUIP, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.playerType = reader.getInt(); + this.playerUUID = reader.getInt(); + this.slotNumber = reader.getInt(); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + writer.putInt(this.playerType); + writer.putInt(this.playerUUID); + writer.putInt(this.slotNumber); + } + + + + /** + * @return the slotNumber + */ + public int getSlotNumber() { + return slotNumber; + } + + public int getPlayerType() { + return playerType; + } + + public void setPlayerType(int playerType) { + this.playerType = playerType; + } + + public int getPlayerUUID() { + return playerUUID; + } + + public void setPlayerUUID(int playerUUID) { + this.playerUUID = playerUUID; + } + +} diff --git a/src/engine/net/client/msg/TransferItemFromInventoryToBankMsg.java b/src/engine/net/client/msg/TransferItemFromInventoryToBankMsg.java new file mode 100644 index 00000000..124f684e --- /dev/null +++ b/src/engine/net/client/msg/TransferItemFromInventoryToBankMsg.java @@ -0,0 +1,220 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +/** + * Transfer item from inventory to bank + * + * @author Eighty + */ + +public class TransferItemFromInventoryToBankMsg extends ClientNetMsg { + + private long playerCompID1; + private long playerCompID2; + private int type; + private int objectUUID; + private int unknown1; + private int unknown2; + private int numItems; + private byte unknown4; + + /** + * This is the general purpose constructor + */ + public TransferItemFromInventoryToBankMsg(long playerCompID1, + long playerCompID2, int type, int objectUUID, int unknown1, + int unknown2, int numItems, byte unknown4) { + super(Protocol.TRANSFERITEMTOBANK); + this.playerCompID1 = playerCompID1; + this.playerCompID2 = playerCompID2; + this.type = type; + this.objectUUID = objectUUID; + this.unknown1 = unknown1; + this.unknown2 = unknown2; + this.numItems = numItems; + this.unknown4 = unknown4; + } + + public TransferItemFromInventoryToBankMsg(TransferItemFromBankToInventoryMsg msg) { + super(Protocol.TRANSFERITEMTOBANK); + this.playerCompID1 = msg.getPlayerCompID1(); + this.playerCompID2 = msg.getPlayerCompID2(); + this.type = msg.getType(); + this.objectUUID = msg.getUUID(); + this.unknown1 = msg.getUnknown1(); + this.unknown2 = msg.getUnknown2(); + this.numItems = msg.getNumItems(); + this.unknown4 = msg.getUnknown4(); + } + + /** + * This is the general purpose constructor + */ + public TransferItemFromInventoryToBankMsg() { + super(Protocol.TRANSFERITEMTOBANK); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public TransferItemFromInventoryToBankMsg(AbstractConnection origin, + ByteBufferReader reader) { + super(Protocol.TRANSFERITEMTOBANK, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + playerCompID1 = reader.getLong(); + playerCompID2 = reader.getLong(); + type = reader.getInt(); + objectUUID = reader.getInt(); + unknown1 = reader.getInt(); + unknown2 = reader.getInt(); + numItems = reader.getInt(); + unknown4 = reader.get(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + writer.putLong(playerCompID1); + writer.putLong(playerCompID2); + writer.putInt(type); + writer.putInt(objectUUID); + writer.putInt(unknown1); + writer.putInt(unknown2); + writer.putInt(numItems); + writer.put(unknown4); + } + + /** + * @return the playerCompID1 + */ + public long getPlayerCompID1() { + return playerCompID1; + } + + /** + * @param playerCompID1 the playerCompID1 to set + */ + public void setPlayerCompID1(long playerCompID1) { + this.playerCompID1 = playerCompID1; + } + + /** + * @return the playerCompID2 + */ + public long getPlayerCompID2() { + return playerCompID2; + } + + /** + * @param playerCompID2 the playerCompID2 to set + */ + public void setPlayerCompID2(long playerCompID2) { + this.playerCompID2 = playerCompID2; + } + + /** + * @return the type + */ + public int getType() { + return type; + } + + /** + * @param type the type to set + */ + public void setType(int type) { + this.type = type; + } + + /** + * @return the objectUUID + */ + public int getUUID() { + return objectUUID; + } + + /** + * @return the unknown1 + */ + public int getUnknown1() { + return unknown1; + } + + /** + * @param unknown1 the unknown1 to set + */ + public void setUnknown1(int unknown1) { + this.unknown1 = unknown1; + } + + /** + * @return the unknown2 + */ + public int getUnknown2() { + return unknown2; + } + + /** + * @param unknown2 the unknown2 to set + */ + public void setUnknown2(int unknown2) { + this.unknown2 = unknown2; + } + + /** + * @return the numItems + */ + public int getNumItems() { + return numItems; + } + + /** + * @param numItems the numItems to set + */ + public void setNumItems(int numItems) { + this.numItems = numItems; + } + + /** + * @return the unknown4 + */ + public byte getUnknown4() { + return unknown4; + } + + /** + * @param unknown4 the unknown4 to set + */ + public void setUnknown4(byte unknown4) { + this.unknown4 = unknown4; + } + +} diff --git a/src/engine/net/client/msg/TransferItemFromInventoryToEquipMsg.java b/src/engine/net/client/msg/TransferItemFromInventoryToEquipMsg.java new file mode 100644 index 00000000..223d7a54 --- /dev/null +++ b/src/engine/net/client/msg/TransferItemFromInventoryToEquipMsg.java @@ -0,0 +1,221 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractCharacter; + +public class TransferItemFromInventoryToEquipMsg extends ClientNetMsg { + + private int sourceType; + private int sourceID; + private int pad1; + private int itemBase; + private int type; + private int objectUUID; + private int slotNumber; + private int pad2; + private float unknown1, unknown2; + + /** + * This is the general purpose constructor. + */ + public TransferItemFromInventoryToEquipMsg() { + super(Protocol.EQUIP); + } + + public TransferItemFromInventoryToEquipMsg(AbstractCharacter source, int slot, int itemBaseID) { + super(Protocol.EQUIP); + this.sourceType = source.getObjectType().ordinal(); + this.sourceID = source.getObjectUUID(); + this.slotNumber = slot; + this.itemBase = itemBaseID; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public TransferItemFromInventoryToEquipMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.EQUIP, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + pad1 = reader.getInt(); + itemBase = reader.getInt(); + type = reader.getInt(); + objectUUID = reader.getInt(); + slotNumber = reader.getInt(); + pad2 = reader.getInt(); + unknown1 = reader.getFloat(); + unknown2 = reader.getFloat(); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.putInt(pad1); + writer.putInt(itemBase); + writer.putInt(type); + writer.putInt(objectUUID); + writer.putInt(slotNumber); + writer.putInt(pad2); + writer.putFloat(unknown1); + writer.putFloat(unknown1); + } + + + + /** + * @return the pad1 + */ + public int getPad1() { + return pad1; + } + + /** + * @param pad1 + * the pad1 to set + */ + public void setPad1(int pad1) { + this.pad1 = pad1; + } + + /** + * @return the itemBase + */ + public int getItemBase() { + return itemBase; + } + + /** + * @param itemBase + * the itemBase to set + */ + public void setItemBase(int itemBase) { + this.itemBase = itemBase; + } + + /** + * @return the type + */ + public int getType() { + return type; + } + + /** + * @param type + * the type to set + */ + public void setType(int type) { + this.type = type; + } + + /** + * @return the objectUUID + */ + public int getUUID() { + return objectUUID; + } + + /** + * @return the slotNumber + */ + public int getSlotNumber() { + return slotNumber; + } + + /** + * @param slotNumber + * the slotNumber to set + */ + public void setSlotNumber(int slotNumber) { + this.slotNumber = slotNumber; + } + + /** + * @return the pad2 + */ + public int getPad2() { + return pad2; + } + + /** + * @param pad2 + * the pad2 to set + */ + public void setPad2(int pad2) { + this.pad2 = pad2; + } + + /** + * @return the unknown1 + */ + public float getUnknown1() { + return unknown1; + } + + /** + * @param unknown1 + * the unknown1 to set + */ + public void setUnknown1(float unknown1) { + this.unknown1 = unknown1; + } + + /** + * @return the unknown2 + */ + public float getUnknown2() { + return unknown2; + } + + /** + * @param unknown2 + * the unknown2 to set + */ + public void setUnknown2(float unknown2) { + this.unknown2 = unknown2; + } + + public int getSourceType() { + return sourceType; + } + + public void setSourceType(int sourceType) { + this.sourceType = sourceType; + } + + public int getSourceID() { + return sourceID; + } + + public void setSourceID(int sourceID) { + this.sourceID = sourceID; + } + +} diff --git a/src/engine/net/client/msg/TransferItemFromInventoryToVaultMsg.java b/src/engine/net/client/msg/TransferItemFromInventoryToVaultMsg.java new file mode 100644 index 00000000..e1c5bc47 --- /dev/null +++ b/src/engine/net/client/msg/TransferItemFromInventoryToVaultMsg.java @@ -0,0 +1,207 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +/** + * Transfer item from inventory to vault + * + * @author Eighty + */ + +public class TransferItemFromInventoryToVaultMsg extends ClientNetMsg { + + private int unknown01; + private int unknown02; + private long playerCompID; + private int type; + private int objectUUID; + private int unknown03; + private int unknown04; + + /** + * This is the general purpose constructor + */ + public TransferItemFromInventoryToVaultMsg(long playerCompID, + int unknown01, int unknown02, int type, int objectUUID, int unknown03, + int unknown04) { + super(Protocol.ITEMTOVAULT); + this.playerCompID = playerCompID; + this.unknown01 = unknown01; + this.unknown02 = unknown02; + this.type = type; + this.objectUUID = objectUUID; + this.unknown03 = unknown03; + this.unknown04 = unknown04; + } + + public TransferItemFromInventoryToVaultMsg(TransferItemFromVaultToInventoryMsg msg) { + super(Protocol.ITEMTOVAULT); + this.playerCompID = msg.getPlayerCompID(); + this.unknown01 = msg.getUnknown01(); + this.unknown02 = msg.getUnknown02(); + this.type = msg.getType(); + this.objectUUID = msg.getUUID(); + this.unknown03 = msg.getUnknown03(); + this.unknown04 = msg.getUnknown04(); + } + + /** + * This is the general purpose constructor + */ + public TransferItemFromInventoryToVaultMsg() { + super(Protocol.ITEMTOVAULT); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public TransferItemFromInventoryToVaultMsg(AbstractConnection origin, + ByteBufferReader reader) { + super(Protocol.ITEMTOVAULT, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + playerCompID = reader.getLong(); + unknown01 = reader.getInt(); + unknown02 = reader.getInt(); + type = reader.getInt(); + objectUUID = reader.getInt(); + unknown03 = reader.getInt(); + unknown04 = reader.getInt(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + writer.putLong(playerCompID); + writer.putInt(unknown01); + writer.putInt(unknown02); + writer.putInt(type); + writer.putInt(objectUUID); + writer.putInt(unknown03); + writer.putInt(unknown04); + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the unknown02 + */ + public int getUnknown02() { + return unknown02; + } + + /** + * @param unknown02 + * the unknown02 to set + */ + public void setUnknown02(int unknown02) { + this.unknown02 = unknown02; + } + + /** + * @return the playerCompID + */ + public long getPlayerCompID() { + return playerCompID; + } + + /** + * @param playerCompID + * the playerCompID to set + */ + public void setPlayerCompID(long playerCompID) { + this.playerCompID = playerCompID; + } + + /** + * @return the type + */ + public int getType() { + return type; + } + + /** + * @param type + * the type to set + */ + public void setType(int type) { + this.type = type; + } + + /** + * @return the objectUUID + */ + public int getUUID() { + return objectUUID; + } + + /** + * @return the unknown03 + */ + public int getUnknown03() { + return unknown03; + } + + /** + * @param unknown03 + * the unknown03 to set + */ + public void setUnknown03(int unknown03) { + this.unknown03 = unknown03; + } + + /** + * @return the unknown04 + */ + public int getUnknown04() { + return unknown04; + } + + /** + * @param unknown04 + * the unknown04 to set + */ + public void setUnknown04(int unknown04) { + this.unknown04 = unknown04; + } + +} diff --git a/src/engine/net/client/msg/TransferItemFromVaultToInventoryMsg.java b/src/engine/net/client/msg/TransferItemFromVaultToInventoryMsg.java new file mode 100644 index 00000000..dcef2821 --- /dev/null +++ b/src/engine/net/client/msg/TransferItemFromVaultToInventoryMsg.java @@ -0,0 +1,200 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +/** + * Transfer item from Vault to inventory + * + * @author Eighty + */ + +public class TransferItemFromVaultToInventoryMsg extends ClientNetMsg { + + private int unknown01; + private int unknown02; + private long playerCompID; + private int type; + private int objectUUID; + private int unknown03; + private int unknown04; + + /** + * This is the general purpose constructor + */ + public TransferItemFromVaultToInventoryMsg(int unknown01, int unknown02, + long playerCompID, int type, int objectUUID, int unknown03, int unknown04) { + super(Protocol.TRANSFERITEMFROMVAULTTOINVENTORY); + this.unknown01 = unknown01; + this.unknown02 = unknown02; + this.playerCompID = playerCompID; + this.type = type; + this.objectUUID = objectUUID; + this.unknown03 = unknown03; + this.unknown04 = unknown04; + } + + public TransferItemFromVaultToInventoryMsg(TransferItemFromInventoryToVaultMsg msg) { + super(Protocol.TRANSFERITEMFROMVAULTTOINVENTORY); + this.playerCompID = msg.getPlayerCompID(); + this.unknown01 = msg.getUnknown01(); + this.unknown02 = msg.getUnknown02(); + this.type = msg.getType(); + this.objectUUID = msg.getUUID(); + this.unknown03 = msg.getUnknown03(); + this.unknown04 = msg.getUnknown04(); + } + + /** + * This is the general purpose constructor + */ + public TransferItemFromVaultToInventoryMsg() { + super(Protocol.TRANSFERITEMFROMVAULTTOINVENTORY); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public TransferItemFromVaultToInventoryMsg(AbstractConnection origin, + ByteBufferReader reader) { + super(Protocol.TRANSFERITEMFROMVAULTTOINVENTORY, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + unknown01 = reader.getInt(); + unknown02 = reader.getInt(); + playerCompID = reader.getLong(); + type = reader.getInt(); + objectUUID = reader.getInt(); + unknown03 = reader.getInt(); + unknown04 = reader.getInt(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + writer.putInt(unknown01); + writer.putInt(unknown02); + writer.putLong(playerCompID); + writer.putInt(type); + writer.putInt(objectUUID); + writer.putInt(unknown03); + writer.putInt(unknown04); + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the unknown02 + */ + public int getUnknown02() { + return unknown02; + } + + /** + * @param unknown02 the unknown02 to set + */ + public void setUnknown02(int unknown02) { + this.unknown02 = unknown02; + } + + /** + * @return the playerCompID + */ + public long getPlayerCompID() { + return playerCompID; + } + + /** + * @param playerCompID the playerCompID to set + */ + public void setPlayerCompID(long playerCompID) { + this.playerCompID = playerCompID; + } + + /** + * @return the type + */ + public int getType() { + return type; + } + + /** + * @param type the type to set + */ + public void setType(int type) { + this.type = type; + } + + /** + * @return the objectUUID + */ + public int getUUID() { + return objectUUID; + } + + /** + * @return the unknown03 + */ + public int getUnknown03() { + return unknown03; + } + + /** + * @param unknown03 the unknown03 to set + */ + public void setUnknown03(int unknown03) { + this.unknown03 = unknown03; + } + + /** + * @return the unknown04 + */ + public int getUnknown04() { + return unknown04; + } + + /** + * @param unknown04 the unknown04 to set + */ + public void setUnknown04(int unknown04) { + this.unknown04 = unknown04; + } + +} diff --git a/src/engine/net/client/msg/UncommitToTradeMsg.java b/src/engine/net/client/msg/UncommitToTradeMsg.java new file mode 100644 index 00000000..d3429f29 --- /dev/null +++ b/src/engine/net/client/msg/UncommitToTradeMsg.java @@ -0,0 +1,95 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +/** + * Uncommit to trade + * + * @author Eighty + */ +public class UncommitToTradeMsg extends ClientNetMsg { + + private int unknown01; + private long playerCompID; + + /** + * This is the general purpose constructor + */ + public UncommitToTradeMsg(int unknown01, long playerCompID) { + super(Protocol.TRADEUNCONFIRM); + this.unknown01 = unknown01; + this.playerCompID = playerCompID; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public UncommitToTradeMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.TRADEUNCONFIRM, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + unknown01 = reader.getInt(); + playerCompID = reader.getLong(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + writer.putInt(unknown01); + writer.putLong(playerCompID); + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the playerCompID + */ + public long getPlayerCompID() { + return playerCompID; + } + + /** + * @param playerCompID the playerCompID to set + */ + public void setPlayerCompID(long playerCompID) { + this.playerCompID = playerCompID; + } + +} diff --git a/src/engine/net/client/msg/Unknown1Msg.java b/src/engine/net/client/msg/Unknown1Msg.java new file mode 100644 index 00000000..29fa9ad5 --- /dev/null +++ b/src/engine/net/client/msg/Unknown1Msg.java @@ -0,0 +1,79 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class Unknown1Msg extends ClientNetMsg { + + private int targetType; + private int targetID; + + /** + * This is the general purpose constructor. + */ + public Unknown1Msg(int targetType, int targetID) { + super(Protocol.UNKNOWN1); + this.targetType = targetType; + this.targetID = targetID; + } + + /** + * This is the general purpose constructor. + */ + public Unknown1Msg() { + super(Protocol.UNKNOWN1); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public Unknown1Msg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.UNKNOWN1, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + } + + /** + * @return the targetType + */ + public int getTargetType() { + return targetType; + } + + /** + * @return the targetID + */ + public int getTargetID() { + return targetID; + } + +} diff --git a/src/engine/net/client/msg/UnknownMsg.java b/src/engine/net/client/msg/UnknownMsg.java new file mode 100644 index 00000000..f806125f --- /dev/null +++ b/src/engine/net/client/msg/UnknownMsg.java @@ -0,0 +1,155 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class UnknownMsg extends ClientNetMsg { + + private int unknown01; + private int unknown02; + private int unknown03; + private short unknown04; + private byte unknown05; + + /** + * This is the general purpose constructor. + */ + public UnknownMsg() { + super(Protocol.UNKNOWN); + this.unknown01 = 0x40A5BDB0; + this.unknown02 = 0x342AA9F0; + this.unknown03 = 0; + this.unknown04 = (short) 0; + this.unknown05 = (byte) 0; + + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public UnknownMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.UNKNOWN, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + writer.putInt(this.unknown03); + writer.putShort(this.unknown04); + writer.put(this.unknown05); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getFloat(); + reader.getString(); + reader.getString(); + reader.getInt(); + reader.get(); + reader.getInt(); + reader.getInt(); + int unknownSize =reader.getInt(); + for (int i = 0; i < unknownSize;i++) + reader.getInt(); + reader.getInt(); + reader.getString(); + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the unknown02 + */ + public int getUnknown02() { + return unknown02; + } + + /** + * @param unknown02 + * the unknown02 to set + */ + public void setUnknown02(int unknown02) { + this.unknown02 = unknown02; + } + + /** + * @return the unknown03 + */ + public int getUnknown03() { + return unknown03; + } + + /** + * @param unknown03 + * the unknown03 to set + */ + public void setUnknown03(int unknown03) { + this.unknown03 = unknown03; + } + + /** + * @return the unknown04 + */ + public short getUnknown04() { + return unknown04; + } + + /** + * @param unknown04 + * the unknown04 to set + */ + public void setUnknown04(short unknown04) { + this.unknown04 = unknown04; + } + + /** + * @return the unknown05 + */ + public byte getUnknown05() { + return unknown05; + } + + /** + * @param unknown05 + * the unknown05 to set + */ + public void setUnknown05(byte unknown05) { + this.unknown05 = unknown05; + } + +} diff --git a/src/engine/net/client/msg/UnloadObjectsMsg.java b/src/engine/net/client/msg/UnloadObjectsMsg.java new file mode 100644 index 00000000..4e2c0770 --- /dev/null +++ b/src/engine/net/client/msg/UnloadObjectsMsg.java @@ -0,0 +1,96 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractGameObject; +import org.pmw.tinylog.Logger; + +import java.util.HashMap; + + +public class UnloadObjectsMsg extends ClientNetMsg { + + private HashMap objectList = new HashMap<>(); + + /** + * This is the general purpose constructor. + */ + public UnloadObjectsMsg() { + super(Protocol.FORGETOBJECTS); + init(); + } + + private void init() { + objectList = new HashMap<>(); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public UnloadObjectsMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.FORGETOBJECTS, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + if (this.objectList == null){ + writer.putInt(0); + return; + } + + writer.putInt(this.objectList.size()); + for (int objectUUID: this.objectList.keySet()){ + writer.putInt(this.objectList.get(objectUUID)); + writer.putInt(objectUUID); + } + + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + init(); + int size = reader.getInt(); +// for (int i = 0; i < size; i++) +// this.objectList.add(reader.getLong()); + Logger.info( "Client telling server to unload objects.. ??"); + } + + public HashMap getObjectList() { + return this.objectList; + } + + public void addObject(AbstractGameObject value) { + this.objectList.put(value.getObjectUUID(), value.getObjectType().ordinal()); + } + + public int size() { + return this.objectList.size(); + } + + @Override + protected int getPowerOfTwoBufferSize() { + return 13; + } +} diff --git a/src/engine/net/client/msg/UpdateCharOrMobMessage.java b/src/engine/net/client/msg/UpdateCharOrMobMessage.java new file mode 100644 index 00000000..7607ba5d --- /dev/null +++ b/src/engine/net/client/msg/UpdateCharOrMobMessage.java @@ -0,0 +1,178 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractCharacter; + +public class UpdateCharOrMobMessage extends ClientNetMsg { + + private int type; + private int npcType; + private int npcID; + + private int playerType; + private int playerID; + private int size; + private int subRace; + + private int pad = 0; + private int objectType; + private int objectUUID; + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public UpdateCharOrMobMessage(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.UPDATECHARORMOB, origin, reader); + } + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + + } + + public UpdateCharOrMobMessage(AbstractCharacter tar, int type, int subRaceID) { + super(Protocol.UPDATECHARORMOB); + this.playerType = tar.getObjectType().ordinal(); + this.playerID = tar.getObjectUUID(); + this.type = type; + this.subRace = subRaceID; + } + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + writer.putInt(this.type); + if (this.type == 2){ + writer.putInt(this.playerType); + writer.putInt(this.playerID); + writer.putInt(this.subRace); + writer.putInt(-600065291); + return; + } + writer.putInt(this.playerType); + writer.putInt(this.playerID); + writer.put((byte)1); + writer.putInt(0); + writer.putInt(this.subRace); + writer.putInt(this.playerType); + writer.putInt(this.playerID); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putFloat(1); + writer.putFloat(1); + writer.putFloat(1); + writer.putFloat(1); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putLong(-1); + writer.putInt(0); + writer.put((byte)1); + writer.putInt(0); + writer.put((byte)1); + writer.putInt(0); + writer.putInt(0); + writer.put((byte)1); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(4); + writer.putInt(0); + writer.putInt(0); + writer.putInt(1); + writer.putShort((short)0); + writer.put((byte)0); + + + } + + public int getObjectType() { + return objectType; + } + + public void setObjectType(int value) { + this.objectType = value; + } + + public void setPad(int value) { + this.pad = value; + } + + public int getUUID() { + return objectUUID; + + } + + public int getPad() { + return pad; + } + public int getType() { + return type; + } + public void setType(int type) { + this.type = type; + } + public int getSize() { + return size; + } + public void setSize(int size) { + this.size = size; + } + + public int getSubRace() { + return subRace; + } + public void setSubRace(int subRace) { + this.subRace = subRace; + } + public int getNpcType() { + return npcType; + } + public void setNpcType(int npcType) { + this.npcType = npcType; + } + public int getNpcID() { + return npcID; + } + public void setNpcID(int npcID) { + this.npcID = npcID; + } + public int getPlayerType() { + return playerType; + } + public void setPlayerType(int playerType) { + this.playerType = playerType; + } + public int getPlayerID() { + return playerID; + } + public void setPlayerID(int playerID) { + this.playerID = playerID; + } +} diff --git a/src/engine/net/client/msg/UpdateClientAlliancesMsg.java b/src/engine/net/client/msg/UpdateClientAlliancesMsg.java new file mode 100644 index 00000000..ed74bbaa --- /dev/null +++ b/src/engine/net/client/msg/UpdateClientAlliancesMsg.java @@ -0,0 +1,95 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Guild; + + +public class UpdateClientAlliancesMsg extends ClientNetMsg { + + + private int guildID; + + + public UpdateClientAlliancesMsg(Guild guild) { + super(Protocol.UPDATECLIENTALLIANCES); + this.guildID = guild.getObjectUUID(); + + } + + public UpdateClientAlliancesMsg() { + super(Protocol.UPDATECLIENTALLIANCES); + } + + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public UpdateClientAlliancesMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.UPDATECLIENTALLIANCES, origin, reader); + } + //CALL THIS AFTER SANITY CHECKS AND BEFORE UPDATING HEALTH/GOLD. + + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.get(); + } + + + // Precache and configure this message before we serialize it + + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + + Guild guild = Guild.getGuild(this.guildID); + + writer.putInt(guild.getAllyList().size()); + + for (Guild allies: guild.getAllyList()){ + writer.putInt(GameObjectType.Guild.ordinal()); + writer.putInt(allies.getObjectUUID()); + } + + writer.putInt(guild.getEnemyList().size()); + for (Guild enemies: guild.getEnemyList()){ + writer.putInt(GameObjectType.Guild.ordinal()); + writer.putInt(enemies.getObjectUUID()); + } + writer.putInt(0); + writer.putInt(0); + writer.put((byte)1); + } + + public int getGuildID() { + return guildID; + } + +} diff --git a/src/engine/net/client/msg/UpdateEffectsMsg.java b/src/engine/net/client/msg/UpdateEffectsMsg.java new file mode 100644 index 00000000..b5248cac --- /dev/null +++ b/src/engine/net/client/msg/UpdateEffectsMsg.java @@ -0,0 +1,86 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractWorldObject; +import engine.objects.Effect; + +import java.util.ArrayList; + +public class UpdateEffectsMsg extends ClientNetMsg { + + AbstractWorldObject awo; + + /** + * This is the general purpose constructor. + */ + public UpdateEffectsMsg() { + super(Protocol.UPDATEEFFECTS); + this.awo = null; + } + + /** + * This is the general purpose constructor. + */ + public UpdateEffectsMsg(AbstractWorldObject awo) { + super(Protocol.UPDATEEFFECTS); + this.awo = awo; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public UpdateEffectsMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.UPDATEEFFECTS, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + if (awo == null) { + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + } else { + writer.putInt(awo.getObjectType().ordinal()); + writer.putInt(awo.getObjectUUID()); + + ArrayList effects = new ArrayList<>(awo.getEffects().values()); + writer.putInt(effects.size()); + for (Effect effect : effects) + effect.serializeForClientMsg(writer); + } + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + } + + public AbstractWorldObject getAwo() { + return this.awo; + } + + public void setAwo(AbstractWorldObject awo) { + this.awo = awo; + } +} diff --git a/src/engine/net/client/msg/UpdateFriendStatusMessage.java b/src/engine/net/client/msg/UpdateFriendStatusMessage.java new file mode 100644 index 00000000..b0f6bfb7 --- /dev/null +++ b/src/engine/net/client/msg/UpdateFriendStatusMessage.java @@ -0,0 +1,78 @@ +/* +HashSet playerFriendSet = PlayerFriendsMap.get(playerUID); + playerFriendSet.add(friendUID); * Copyright 2013 MagicBane Emulator Project + * All Rights Reserved + */ +package engine.net.client.msg; + + +import engine.Enum.GameObjectType; +import engine.gameManager.SessionManager; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.PlayerCharacter; + + +public class UpdateFriendStatusMessage extends ClientNetMsg { + + public int statusType; + public PlayerCharacter player; + public boolean online = true; + + + + /** + * This is the general purpose constructor. + */ + public UpdateFriendStatusMessage(PlayerCharacter player) { + super(Protocol.UPDATEFRIENDSTATUS); + this.player = player; + this.online = SessionManager.getPlayerCharacterByID(player.getObjectUUID()) != null ? true : false; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public UpdateFriendStatusMessage(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.UPDATEFRIENDSTATUS, origin, reader); + } + + /** + * Copy constructor + */ + public UpdateFriendStatusMessage(UpdateFriendStatusMessage msg) { + super(Protocol.UPDATEFRIENDSTATUS); + } + + + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + //message is serialize only, no need for deserialize. + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + this.statusType = reader.getInt(); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(GameObjectType.PlayerCharacter.ordinal()); + writer.putInt(player.getObjectUUID()); + writer.putString(player.getCombinedName()); + writer.putInt(online ? 0 : 1); + writer.putInt(player.friendStatus.ordinal()); + } +} diff --git a/src/engine/net/client/msg/UpdateGoldMsg.java b/src/engine/net/client/msg/UpdateGoldMsg.java new file mode 100644 index 00000000..cb3fc5be --- /dev/null +++ b/src/engine/net/client/msg/UpdateGoldMsg.java @@ -0,0 +1,119 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractWorldObject; +import engine.objects.CharacterItemManager; +import engine.objects.Item; +import engine.objects.PlayerCharacter; + +/** + * Update gold in inventory and/or bank + * + * @author Eighty + */ + +public class UpdateGoldMsg extends ClientNetMsg { + + private AbstractWorldObject looter; + CharacterItemManager itemManager; + private Item goldInventory; + private Item goldBank; + private int tradeGold = 0; + + + /** + * This is the general purpose constructor + */ + public UpdateGoldMsg(AbstractWorldObject player) { + super(Protocol.UPDATEGOLDVALUE); + this.looter = player; + } + + + /** + * This is the general purpose constructor + */ + public UpdateGoldMsg() { + super(Protocol.UPDATEGOLDVALUE); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public UpdateGoldMsg(AbstractConnection origin, + ByteBufferReader reader) { + super(Protocol.UPDATEGOLDVALUE, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + + } + + // Pre-cache and set values so they are available when we + // serialize the data. + + public void configure() { + + if (this.looter != null && this.looter.getObjectType() == GameObjectType.PlayerCharacter){ + itemManager = ((PlayerCharacter)looter).getCharItemManager(); + goldInventory = itemManager.getGoldInventory(); + this.tradeGold = itemManager.getGoldTrading(); + goldBank = itemManager.getGoldBank(); + }else{ + itemManager = null; + goldInventory = null; + goldBank = null; + } + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + + if (looter == null){ + writer.putInt(0); + writer.putInt(0); + }else{ + writer.putInt(looter.getObjectType().ordinal()); + writer.putInt(looter.getObjectUUID()); + } + + if (goldInventory != null && goldInventory.getNumOfItems() - this.tradeGold > 0) { + writer.put((byte) 1); + Item.serializeForClientMsgWithoutSlot(goldInventory,writer); + } else + writer.put((byte) 0); + + if (goldBank != null && goldBank.getNumOfItems() != 0) { + writer.put((byte) 1); + Item.serializeForClientMsgWithoutSlot(goldBank,writer); + } else + writer.put((byte) 0); + } + +} diff --git a/src/engine/net/client/msg/UpdateInventoryMsg.java b/src/engine/net/client/msg/UpdateInventoryMsg.java new file mode 100644 index 00000000..c9480eb8 --- /dev/null +++ b/src/engine/net/client/msg/UpdateInventoryMsg.java @@ -0,0 +1,108 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.ItemType; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Item; + +import java.util.ArrayList; + +public class UpdateInventoryMsg extends ClientNetMsg { + + private ArrayList toAdd; + private ArrayList bank; + + /** + * This is the general purpose constructor. + */ + public UpdateInventoryMsg(ArrayList inventory,ArrayList bank, Item gold, boolean add) { + super(Protocol.UPDATECLIENTINVENTORIES); + toAdd = inventory; + if (gold != null) + toAdd.add(gold); + + this.bank = bank; + + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public UpdateInventoryMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.UPDATECLIENTINVENTORIES, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + putList(writer, this.toAdd); + if (bank != null) + putList(writer, this.bank); + else + writer.putInt(0); + } + + public static void putList(ByteBufferWriter writer, ArrayList list) { + int indexPosition = writer.position(); + writer.putInt(0); + + int serialized = 0; + + + for (Item item : list) { + if (item.getItemBase().getType().equals(ItemType.GOLD)) { + if (item.getNumOfItems() == 0) + continue; + } + Item.serializeForClientMsgWithoutSlot(item,writer); + ++serialized; + } + + writer.putIntAt(serialized, indexPosition); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + } + + public ArrayList getToAdd() { + return this.toAdd; + } + + + public void setToAdd(ArrayList value) { + this.toAdd = value; + } + + + public void addToInventory(Item value) { + this.toAdd.add(value); + } + + + @Override + protected int getPowerOfTwoBufferSize() { + return 20; + } +} diff --git a/src/engine/net/client/msg/UpdateObjectMsg.java b/src/engine/net/client/msg/UpdateObjectMsg.java new file mode 100644 index 00000000..364ac4f4 --- /dev/null +++ b/src/engine/net/client/msg/UpdateObjectMsg.java @@ -0,0 +1,136 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.*; + +public class UpdateObjectMsg extends ClientNetMsg { + private int msgType; + private AbstractWorldObject ago; + + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public UpdateObjectMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.UPDATEOBJECT, origin, reader); + } + + public UpdateObjectMsg() { + super(Protocol.UPDATEOBJECT); + } + + public UpdateObjectMsg(AbstractWorldObject ago,int type) { + super(Protocol.UPDATEOBJECT); + if (ago == null) + return; + this.msgType = type; + this.ago = ago; + + + } + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + writer.putInt(this.msgType); + switch (this.msgType){ + case 2: + updateName(writer); + break; + case 3: + updateRank(writer); + break; + case 4: + derank(writer); + break; + case 5: + updateGuild(writer); + break; + + default: + break; + + } + } + + private void updateName(ByteBufferWriter writer){ + + writer.putInt(ago.getObjectType().ordinal()); + writer.putInt(ago.getObjectUUID()); + writer.putString(ago.getName()); + writer.putInt(0); + } + + private void updateRank(ByteBufferWriter writer){ + writer.putInt(ago.getObjectType().ordinal()); + writer.putInt(ago.getObjectUUID()); + writer.putFloat(ago.getHealthMax()); + writer.putFloat(ago.getCurrentHitpoints()); + writer.put((byte)1); + writer.putInt(0); + + } + + private void updateGuild(ByteBufferWriter writer){ + + writer.putInt(ago.getObjectType().ordinal()); + writer.putInt(ago.getObjectUUID()); + + switch (ago.getObjectType()){ + case Building: + Guild guild = ((Building)ago).getGuild(); + GuildTag._serializeForDisplay(guild.getGuildTag(),writer); + GuildTag._serializeForDisplay(guild.getNation().getGuildTag(),writer); + writer.putInt(0); + + break; + default: + break; + } + + + } + + private void derank(ByteBufferWriter writer){ + writer.putInt(0); + writer.putInt(0); + writer.put((byte)0); + writer.putInt(ago.getObjectType().ordinal()); + writer.putInt(ago.getObjectUUID()); + writer.putInt(0); + } + + + + public AbstractGameObject getAgo() { + return ago; + } + + +} diff --git a/src/engine/net/client/msg/UpdateStateMsg.java b/src/engine/net/client/msg/UpdateStateMsg.java new file mode 100644 index 00000000..184234d9 --- /dev/null +++ b/src/engine/net/client/msg/UpdateStateMsg.java @@ -0,0 +1,222 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.GameObjectType; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractCharacter; +import engine.objects.PlayerCharacter; + +public class UpdateStateMsg extends ClientNetMsg { + + private int charType; + private int charUUID; + private int activity; //1 dead,2 unconscious, 3 Sleeping, 4 resting,5 casting,6 IDLE,7 casting? , 8 nothing 9 unknown, + private int speed; // 1 low, 2 high (walk,run) + private int aware; // 1 low, 2 high (combat off,combat on) + + private int mode; // 0 unknown, 1 water, 2 ground, 3 flight. + private int fighting; // 1 disengaged, 2 engaged. + private int headlights; // LFGroup/LFGuild/Recruiting Icons + + /** + * This is the general purpose constructor. + */ + public UpdateStateMsg() { + super(Protocol.UPDATESTATE); + this.fighting = 1; + this.headlights = 0; + } + + public UpdateStateMsg(AbstractCharacter ac) { + super(Protocol.UPDATESTATE); + this.fighting = 1; + this.headlights = 0; + setPlayer(ac); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public UpdateStateMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.UPDATESTATE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.charType); + writer.putInt(this.charUUID); + writer.putInt(this.activity); + writer.putInt(this.speed); + writer.putInt(this.aware); + writer.putInt(this.mode); + writer.putInt(this.fighting); + writer.putInt(this.headlights); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.charType = reader.getInt(); + this.charUUID = reader.getInt(); + this.activity = reader.getInt(); + this.speed = reader.getInt(); + this.aware = reader.getInt(); + + this.mode = reader.getInt(); + this.fighting = reader.getInt(); + this.headlights = reader.getInt(); + } + + /** + * Sets this Msg's charUUID, sitStand, walkRun and combatToggle parameters + * based on supplied AbstractCharacter + * + * @param ac + */ + public final void setPlayer(AbstractCharacter ac) { + + PlayerCharacter player; + + this.charType = ac.getObjectType().ordinal(); + this.charUUID = ac.getObjectUUID(); + this.activity = ac.getIsSittingAsInt(); + this.speed = ac.getIsWalkingAsInt(); + this.aware = ac.getIsCombatAsInt(); + + if (ac.getObjectType() == GameObjectType.PlayerCharacter) { + player = (PlayerCharacter)ac; + this.headlights = player.getHeadlightsAsInt(); + } else this.headlights = 0; + + this.mode = ac.getIsFlightAsInt(); + } + + /** + * @return the charUUID + */ + public int getPlayerUUID() { + return charUUID; + } + + /** + * @return the sitStand + */ + public int getActivity() { + return activity; + } + + /** + * @param activity + * the sitStand to set + */ + public void setActivity(int activity) { + this.activity = activity; + } + + /** + * @return the walkRun + */ + public int getSpeed() { + return speed; + } + + /** + * @param speed + * the walkRun to set + */ + public void setSpeed(int speed) { + this.speed = speed; + } + + /** + * @return the combatToggle + */ + public int getAware() { + return aware; + } + + /** + * @param aware + * the combatToggle to set + */ + public void setAware(int aware) { + this.aware = aware; + } + + /** + * @return the unknown01 + */ + public int getMode() { + return mode; + } + + /** + * @param mode + * the unknown01 to set + */ + public void setMode(int mode) { + this.mode = mode; + } + + /** + * @return the unknown02 + */ + public int getFighting() { + return fighting; + } + + /** + * @param fighting + * the unknown02 to set + */ + public void setFighting(int fighting) { + this.fighting = fighting; + } + + /** + * @return the unknown03 + */ + public int getHeadlights() { + return headlights; + } + + /** + * @param headlights + * the headlights to set + */ + public void setHeadlights(int headlights) { + this.headlights = headlights; + } + +} diff --git a/src/engine/net/client/msg/UpdateTradeWindowMsg.java b/src/engine/net/client/msg/UpdateTradeWindowMsg.java new file mode 100644 index 00000000..13c40adc --- /dev/null +++ b/src/engine/net/client/msg/UpdateTradeWindowMsg.java @@ -0,0 +1,95 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Item; +import engine.objects.PlayerCharacter; + +import java.util.ArrayList; + +/** + * Update trade window message. Send item info to other player. + * @author Eighty + */ +public class UpdateTradeWindowMsg extends ClientNetMsg { + + PlayerCharacter pc1; + PlayerCharacter pc2; + + /** + * This is the general purpose constructor. + */ + public UpdateTradeWindowMsg(PlayerCharacter pc1, PlayerCharacter pc2) { + super(Protocol.UPDATETRADEWINDOW); + this.pc1 = pc1; + this.pc2 = pc2; + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + + writer.putInt(pc1.getObjectType().ordinal()); + writer.putInt(pc1.getObjectUUID()); + + writer.putInt(pc2.getObjectType().ordinal()); + writer.putInt(pc2.getObjectUUID()); + + + + ArrayList trading1 = new ArrayList<>(); + + for (int itemID : pc1.getCharItemManager().getTrading()){ + Item item = Item.getFromCache(itemID); + if (item == null) + continue; + trading1.add(item); + } + + ArrayList trading2 = new ArrayList<>(); + for (int itemID : pc2.getCharItemManager().getTrading()){ + Item item = Item.getFromCache(itemID); + if (item == null) + continue; + trading2.add(item); + } + Item.putTradingList(pc1,writer, trading1, false, pc1.getObjectUUID(),false,null); + Item.putTradingList(pc2,writer, trading2, false, pc2.getObjectUUID(),false,null); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public UpdateTradeWindowMsg(AbstractConnection origin, + ByteBufferReader reader) { + super(Protocol.UPDATETRADEWINDOW, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + } + + @Override + protected int getPowerOfTwoBufferSize() { + return 16; + } +} diff --git a/src/engine/net/client/msg/UpdateVaultMsg.java b/src/engine/net/client/msg/UpdateVaultMsg.java new file mode 100644 index 00000000..f06c6b25 --- /dev/null +++ b/src/engine/net/client/msg/UpdateVaultMsg.java @@ -0,0 +1,69 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Account; + +/** + * Vault inventory contents + * @author Eighty + */ +public class UpdateVaultMsg extends ClientNetMsg { + + private int accountType; + private int accountID; + + + /** + * This is the general purpose constructor. + */ + public UpdateVaultMsg(Account account) { + super(Protocol.CLIENTUPDATEVAULT); + this.accountType = account.getObjectType().ordinal(); + this.accountID = account.getObjectUUID(); + } + + /** + * Serializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + writer.putInt(accountType); + writer.putInt(accountID); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public UpdateVaultMsg(AbstractConnection origin, + ByteBufferReader reader) { + super(Protocol.CLIENTUPDATEVAULT, origin, reader); + } + + /** + * Deserializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + } + + @Override + protected int getPowerOfTwoBufferSize() { + return 17; + } +} diff --git a/src/engine/net/client/msg/UpgradeAssetMessage.java b/src/engine/net/client/msg/UpgradeAssetMessage.java new file mode 100644 index 00000000..1091944b --- /dev/null +++ b/src/engine/net/client/msg/UpgradeAssetMessage.java @@ -0,0 +1,92 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.AbstractNetMsg; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + + +public class UpgradeAssetMessage extends ClientNetMsg { + + private int unknown01; + private int unknown02; + private int buildingUUID; + + + /** + * This is the general purpose constructor. + */ + public UpgradeAssetMessage() { + super(Protocol.UPGRADEASSET); + + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public UpgradeAssetMessage(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.UPGRADEASSET, origin, reader); + } + + /** + * @see AbstractNetMsg#getPowerOfTwoBufferSize() + */ + @Override + protected int getPowerOfTwoBufferSize() { + //Larger size for historically larger opcodes + return (16); // 2^16 == 64k + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + reader.getInt(); // Object Type Padding + this.buildingUUID = reader.getInt(); + } + + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + public int getUnknown01() { + return unknown01; + } + + public void setUnknown02(int unknown02) { + this.unknown02 = unknown02; + } + + public int getUnknown02() { + return unknown02; + } + + public int getBuildingUUID() { + return buildingUUID; + } +} diff --git a/src/engine/net/client/msg/UseCharterMsg.java b/src/engine/net/client/msg/UseCharterMsg.java new file mode 100644 index 00000000..4387fedd --- /dev/null +++ b/src/engine/net/client/msg/UseCharterMsg.java @@ -0,0 +1,105 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.ItemType; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Item; +import engine.objects.PlayerCharacter; + + +public class UseCharterMsg extends ClientNetMsg { + + private int unknown01; + private int unknown02; + private int unknown03; + private String type; + private int unknown04; + private int unknown05; + private int unknown06; + private boolean close = false; + private PlayerCharacter player; + private int charterUUID; + + /** + * This is the general purpose constructor. + */ + public UseCharterMsg() { + super(Protocol.ACTIVATECHARTER); + } + + public UseCharterMsg(PlayerCharacter player, boolean close) { + super(Protocol.ACTIVATECHARTER); + this.close = close; + this.player = player; + + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public UseCharterMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ACTIVATECHARTER, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + + public void configure() { + + if (close) { + for (Item i : player.getInventory()) { + if (i.getItemBase().getType().equals(ItemType.GUILDCHARTER)) { + charterUUID = i.getObjectUUID(); + break; + } + } + } + } + + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + writer.putInt(this.unknown03); + writer.putString(this.type); + writer.putInt(this.unknown04); + writer.putInt(this.unknown05); + writer.putInt(this.unknown06); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + this.unknown03 = reader.getInt(); + this.type = reader.getString(); + this.unknown04 = reader.getInt(); + this.unknown05 = reader.getInt(); + this.unknown06 = reader.getInt(); + } + + /** + * @return the unknown01 + */ + public void setUnknown02(int unknown02) { + this.unknown02 = unknown02; + } +} diff --git a/src/engine/net/client/msg/VendorDialogMsg.java b/src/engine/net/client/msg/VendorDialogMsg.java new file mode 100644 index 00000000..2c3b80cc --- /dev/null +++ b/src/engine/net/client/msg/VendorDialogMsg.java @@ -0,0 +1,905 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.DispatchChannel; +import engine.Enum.GuildHistoryType; +import engine.exception.MsgSendException; +import engine.gameManager.BuildingManager; +import engine.gameManager.DbManager; +import engine.gameManager.GuildManager; +import engine.gameManager.SessionManager; +import engine.math.Vector3fImmutable; +import engine.net.*; +import engine.net.client.ClientConnection; +import engine.net.client.Protocol; +import engine.objects.*; +import engine.server.MBServerStatics; + +import java.util.ArrayList; +import java.util.concurrent.ThreadLocalRandom; + +public class VendorDialogMsg extends ClientNetMsg { + + public static final int MSG_TYPE_VENDOR = 0; + public static final int MSG_TYPE_TRAINER = 1; + public static int cnt = 1; + private int messageType; + private String language; + private int vendorObjectType; + private int vendorObjectID; + private int unknown01; + private int unknown02; + private int unknown03; + private int unknown04; + private String dialogType = "TrainerDialog"; + private String intro = "FighterIntro"; + private String introCode = " [ FighterIntro ] "; + private String merchantCode = " [ Merchant options ] "; + private int menuType = 1; // 0: close, 1: normal, 2: train, 3: untrain + private VendorDialog vd; + + /** + * This is the general purpose constructor. + */ + public VendorDialogMsg(int messageType, int vendorObjectType, int vendorObjectID, String dialogType, String intro, int menuType) { + super(Protocol.VENDORDIALOG); + this.messageType = messageType; + this.vendorObjectType = vendorObjectType; + this.vendorObjectID = vendorObjectID; + this.dialogType = dialogType; + this.intro = intro; + this.introCode = " [ " + intro + " ] "; + this.menuType = menuType; + } + + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public VendorDialogMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.VENDORDIALOG, origin, reader); + } + + public static void replyDialog(VendorDialogMsg msg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter playerCharacter = SessionManager.getPlayerCharacter(origin); + + if (playerCharacter == null) + return; + + if (playerCharacter.getTimeStamp("lastvendorwindow") > System.currentTimeMillis()) { + return; + } + + // Get NPC that player is talking to + NPC npc = NPC.getFromCache(msg.vendorObjectID); + int npcClassID; + + if (npc == null) + return; + + // test within talking range + + if (playerCharacter.getLoc().distanceSquared2D(npc.getLoc()) > MBServerStatics.NPC_TALK_RANGE * MBServerStatics.NPC_TALK_RANGE) { + ErrorPopupMsg.sendErrorPopup(playerCharacter, 14); + return; + } + + // Restrict disc trainers to only characters who have + // tht disc applied. + + npcClassID = npc.getContract().getClassID(); + + if (npc.getContract() != null && + ApplyRuneMsg.isDiscipline(npcClassID)) { + + if (playerCharacter.getRune(npcClassID) == null) { + ErrorPopupMsg.sendErrorPopup(playerCharacter, 49); + return; + } + + } + playerCharacter.setLastNPCDialog(npc); + + VendorDialog vd = null; + Contract contract = npc.getContract(); + + if (contract == null) + vd = VendorDialog.getHostileVendorDialog(); + else if (npc.getBuilding() != null) { + if (BuildingManager.IsPlayerHostile(npc.getBuilding(), playerCharacter)) + vd = VendorDialog.getHostileVendorDialog(); + else vd = contract.getVendorDialog(); + } + else + vd = contract.getVendorDialog(); + if (vd == null) + vd = VendorDialog.getHostileVendorDialog(); + + if (msg.messageType == 1 || msg.unknown03 == vd.getObjectUUID()) { + msg.updateMessage(3, vd); + } + else { + if (VendorDialogMsg.handleSpecialCase(msg, npc, playerCharacter, vd, origin)) + return; + + vd = VendorDialog.getVendorDialog(msg.unknown03); + msg.updateMessage(3, vd); + } + + Dispatch dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + } + + // protected void serializeMerchantMenu(ByteBufferWriter writer) { + // writer.putInt(2); + // writer.putInt(14); + // writer.putInt(0); + // writer.putInt(0); + // writer.put((byte) 0); + // writer.put((byte) 1); + // writer.putString(" [ Merchant options ] "); + // for (int i = 0; i < 4; i++) + // writer.putInt(0); + // writer.putInt(10); + // writer.putInt(0); + // writer.putInt(0); + // writer.put((byte) 0); + // writer.put((byte) 1); + // writer.putString("Done"); + // for (int i = 0; i < 8; i++) + // writer.putInt(0); + // } + + // protected void serializeForTrain(ByteBufferWriter writer, boolean train) { + // writer.putInt(0); + // writer.putInt(0x364AF0D0); // 0x325695C0 + // writer.putInt(this.unknown03); + // writer.putInt(1); + // writer.put((byte) 0); + // writer.putInt(this.unknown03); + // writer.put((byte) 0); + // writer.putInt(0); + // writer.putInt(0); + // writer.putInt(2); // 2 + // if (train) + // writer.putInt(3); // 3=buy/sell/trade, 4=untrain, 5 closes + // else + // writer.putInt(4); //refine + // writer.putInt(10); // 10 + // writer.putInt(0); + // writer.putInt(0); + // writer.putInt(0); + // } + + // protected void serializeClose(ByteBufferWriter writer) { + // writer.putInt(0); + // writer.putInt(0x364AF0D0); // 0x325695C0 + // writer.putInt(this.unknown03); + // writer.putInt(1); + // writer.put((byte) 0); + // writer.putInt(this.unknown03); + // writer.put((byte) 0); + // writer.putInt(0); + // writer.putInt(0); + // writer.putInt(2); // 2 + // writer.putInt(5); // 3=buy/sell/trade, 4=untrain, 5 closes + // writer.putInt(10); // 10 + // writer.putInt(0); + // writer.putInt(0); + // writer.putInt(0); + // } + + // Handles special case menu selections, such as promote, train, ect. + private static boolean handleSpecialCase(VendorDialogMsg msg, NPC npc, PlayerCharacter playerCharacter, VendorDialog vd, ClientConnection origin) + throws MsgSendException { + + Dispatch dispatch; + int menuID = msg.unknown03; // aka menuoptions.optionID + Vector3fImmutable loc; + + switch (menuID) { + case 0: // Close Dialog + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 180: // Promote to class + VendorDialogMsg.promote(playerCharacter, npc); + msg.updateMessage(4, 0); // <-0 closes dialog + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 1000: // TrainSkill skills and powers + msg.updateMessage(4, 2); // <-2 sends trainer screen + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 1001: // Open Bank + getBank(playerCharacter, npc, origin); + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 1002: // Open Vault + getVault(playerCharacter, npc, origin); + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 1003: //Refine + msg.updateMessage(4, 3); // <-3 sends refine screen + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + + // Mainland Teleports + case 100011: // teleport me to Aeldreth + City city = City.getCity(25); + if (city != null) + handleTeleport(playerCharacter, city.getLoc(), 10, 75, false); + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 100012: // teleport me to SDR + city = City.getCity(24); + if (city != null) + handleTeleport(playerCharacter, city.getLoc(), 10, 75, true); + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 100013: // teleport me to Erkeng Hold + city = City.getCity(26); + if (city != null) + handleTeleport(playerCharacter, city.getLoc(), 10, 75, true); + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 100014: // teleport me to Khan + city = City.getCity(36); + if (city != null) + handleTeleport(playerCharacter, city.getLoc(), 1, 75, true); + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + + case 100015: // teleport me to Maelstrom + loc = new Vector3fImmutable(105100f, 40f, -25650f); + handleTeleport(playerCharacter, loc, 10, 75, false); + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 100016: // teleport me to Oblivion + loc = new Vector3fImmutable(108921f, 167f, -51590f); + handleTeleport(playerCharacter, loc, 10, 75, false); + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 100017: // teleport me to Vander's Doom + loc = new Vector3fImmutable(42033f, 46f, -54471f); + handleTeleport(playerCharacter, loc, 10, 75, false); + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 100018: // teleport me to The Sinking Isle + loc = new Vector3fImmutable(67177f, 36f, -31940f); + handleTeleport(playerCharacter, loc, 10, 75, false); + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + + // MainLand Repledges + case 100030: // repledge me to Aeldreth + city = City.getCity(25); + if (city != null) + handleRepledge(playerCharacter, city, 10, 55, false); + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 100031: // repledge me to Sea Dog's Rest + city = City.getCity(24); + if (city != null) + handleRepledge(playerCharacter, city, 10, 75, true); + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 100032: // repledge me to Erkeng Hold + city = City.getCity(26); + if (city != null) + handleRepledge(playerCharacter, city, 10, 55, false); + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 100033: // repledge me to Khan\'Of Srekel + city = City.getCity(36); + if (city != null) + handleRepledge(playerCharacter, city, 1, 75, true); + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 100035: // repledge me to Starkholm + city = City.getCity(27); + if (city != null) + handleRepledge(playerCharacter, city, 1, 20, false); + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + + // Noob Isle Teleports + case 100040: // teleport me to Starkholm + city = City.getCity(27); + if (city != null) + handleTeleport(playerCharacter, city.getLoc(), 1, 20, true); + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 100041: // teleport me to All-Father's Rest + city = City.getCity(28); + if (city != null) + handleTeleport(playerCharacter, city.getLoc(), 1, 20, true); + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 100042: // teleport me to Hengest + city = City.getCity(33); + if (city != null) + handleTeleport(playerCharacter, city.getLoc(), 1, 20, true); + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 100043: // teleport me to Hrimdal + city = City.getCity(30); + if (city != null) + handleTeleport(playerCharacter, city.getLoc(), 1, 20, true); + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 100044: // teleport me to Hothor's Doom + city = City.getCity(29); + if (city != null) + handleTeleport(playerCharacter, city.getLoc(), 1, 20, true); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 100045: // teleport me to Scraefahl + city = City.getCity(32); + if (city != null) + handleTeleport(playerCharacter, city.getLoc(), 1, 20, false); + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + case 100046: // teleport me to Valkirch + city = City.getCity(31); + if (city != null) + handleTeleport(playerCharacter, city.getLoc(), 1, 20, true); + msg.updateMessage(4, 0); + dispatch = Dispatch.borrow(playerCharacter, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + + + + default: + } + return false; + } + + private static boolean finishMessage(VendorDialogMsg msg, ClientConnection origin) throws MsgSendException { + msg.updateMessage(4, 0); + Dispatch dispatch = Dispatch.borrow(origin.getPlayerCharacter(), msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + return true; + } + + private static void handleTeleport(PlayerCharacter pc, Vector3fImmutable loc, int minLevel, int maxLevel, boolean useSquare) { + if (pc == null) + return; + + int level = pc.getLevel(); + if (level >= minLevel && level <= maxLevel) { + if (useSquare) + loc = getSquare(loc); + pc.teleport(loc); + pc.setSafeMode(); + // PowersManager.applyPower(pc, pc, new Vector3f(0f, + // 0f, 0f), -1661758934, 40, false); + } + else { + if (level < minLevel) + ErrorPopupMsg.sendErrorPopup(pc, 74); + else + ErrorPopupMsg.sendErrorPopup(pc, 139); + + } + } + + private static void handleRepledge(PlayerCharacter pc, City city, int minLevel, int maxLevel, boolean useSquare) { + if (pc == null || city == null) + return; + + Vector3fImmutable loc = city.getLoc(); + int level = pc.getLevel(); + if (level >= minLevel && level <= maxLevel) { + // set guild + Guild guild = city.getGuild(); + if (guild != null) { + // teleport player + if (useSquare) + loc = getSquare(loc); + pc.teleport(loc); + pc.setSafeMode(); + // PowersManager.applyPower(pc, pc, new + // Vector3f(0f, 0f, 0f), -1661758934, 40, false); + + // join guild + GuildManager.joinGuild(pc, guild, GuildHistoryType.JOIN); + + pc.resetGuildStatuses(); + + if (guild.isNPCGuild()) + pc.setFullMember(true); + + if (useSquare) + loc = loc.add(30, 0, 0); + pc.setBindLoc(loc); + } + else { + // guild not found, just teleport + if (useSquare) + loc = getSquare(loc); + pc.teleport(loc); + pc.setSafeMode(); + // PowersManager.applyPower(pc, pc, new + // Vector3f(0f, 0f, 0f), -1661758934, 50, false); + } + } + else { + + if (level < minLevel) + ErrorPopupMsg.sendErrorPopup(pc, 74); + else + ErrorPopupMsg.sendErrorPopup(pc, 139); + } + } + + // randomly place around a tol using a square (+- 30 units in x and z + // direction) + public static Vector3fImmutable getSquare(Vector3fImmutable cityLoc) { + Vector3fImmutable loc = cityLoc; + // get direction + int roll = ThreadLocalRandom.current().nextInt(4); + if (roll == 0) { // north + loc = loc.add((ThreadLocalRandom.current().nextInt(60) - 30), 0, -30); + } + else if (roll == 1) { // south + loc = loc.add((ThreadLocalRandom.current().nextInt(60) - 30), 0, 30); + } + else if (roll == 2) { // east + loc = loc.add(30, 0, (ThreadLocalRandom.current().nextInt(60) - 30)); + } + else { // west + loc = loc.add(-30, 0, (ThreadLocalRandom.current().nextInt(60) - 30)); + } + + // Make sure no one gets stuck in the tree. + + if (loc.distanceSquared2D(cityLoc) < 250) { + loc = cityLoc; + loc = loc.add(30, 0, 0); + } + + return loc; + } + + // Handle promotion + private static void promote(PlayerCharacter pc, NPC npc) { + + if (npc == null || pc == null) + return; + + // test level 10 + if (pc.getLevel() < 10) { + // TODO send client promotion error + while (pc.getLevel() > 65) + pc.setLevel((short) 65); + return; + } + + // verify player not already promoted + if (pc.getPromotionClass() != null) { + // TODO send client promotion error + return; + } + + // Get promotion class for npc + Contract contract = npc.getContract(); + if (contract == null) + return; + int promoID = contract.getPromotionClass(); + if (promoID == 0) + return; + PromotionClass promo = DbManager.PromotionQueries.GET_PROMOTION_CLASS(promoID); + if (promo == null) { + // TODO log error here + return; + } + + // verify race valid for profession + Race race = pc.getRace(); + if (race == null || !promo.isAllowedRune(race.getToken())) { + // TODO send client promotion error + return; + } + + // verify baseclass valid for profession + BaseClass bc = pc.getBaseClass(); + if (bc == null || !promo.isAllowedRune(bc.getToken())) { + // TODO send client promotion error + return; + } + + // verify gender + if (promoID == 2511 && pc.isMale()) // Fury + return; + if (promoID == 2512 && pc.isMale()) // Huntress + return; + if (promoID == 2517 && !pc.isMale()) // Warlock + return; + + // Everything valid. Let's promote + pc.setPromotionClass(promo.getObjectUUID()); + + //pc.setLevel((short) 65); + + + promo = pc.getPromotionClass(); + if (promo == null) { + // TODO log error here + return; + } + + // recalculate all bonuses/formulas/skills/powers + pc.recalculate(); + + // send the rune application to the clients + ApplyRuneMsg arm = new ApplyRuneMsg(pc.getObjectType().ordinal(), pc.getObjectUUID(), promo.getObjectUUID(), promo.getObjectType().ordinal(), promo + .getObjectUUID(), true); + DispatchMessage.dispatchMsgToInterestArea(pc, arm, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + + + } + + /** + * Load and send vault to player. This is public only so a DevCmd can hook + * into it. + * + * @param playerCharacter - Player Character requesting vault + * @param target - NPC vaultkeeper + * @param cc - Client Connection + */ + public static void getVault(PlayerCharacter playerCharacter, NPC target, ClientConnection cc) { + if (playerCharacter == null || cc == null || target == null) + return; + + Account ac = playerCharacter.getAccount(); + if (ac == null) + return; + + CharacterItemManager itemManager = playerCharacter.getCharItemManager(); + if (itemManager == null) + return; + + // TODO uncomment this block after we determine when we + // setBankOpen(false) + /* + * // cannot have bank and vault open at the same time if + * (itemManager.isBankOpen()) return; + */ + + if (itemManager.getTradingWith() != null) { + return; + // TODO close trade window here - simple once this is moved to WS + } + + itemManager.setVaultOpen(true); + + // TODO for public test - remove this afterwards + // DevCmd.fillVault(pc, itemManager); + + // TODO When do we setVaultOpen(false)? I don't think the client sends a + // "CloseVault" message. + + OpenVaultMsg openVaultMsg = new OpenVaultMsg(playerCharacter, target); + Dispatch dispatch = Dispatch.borrow(playerCharacter, openVaultMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + ShowVaultInventoryMsg showVaultInventoryMsg = new ShowVaultInventoryMsg(playerCharacter, ac, target); // 37?? + dispatch = Dispatch.borrow(playerCharacter, showVaultInventoryMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + // All recordings have "open - show - open." + // Seems to work fine with just "show - open" as well. + + } + + /** + * Load and send Bank to player. This is public only so a DevCmd can hook + * into it. + * + * @param playerCharacter - Player Character requesting vault + * @param target - NPC vaultkeeper + * @param cc - Client Connection + */ + public static void getBank(PlayerCharacter playerCharacter, NPC target, ClientConnection cc) { + + if (playerCharacter == null) + return; + + if (cc == null) + return; + + CharacterItemManager itemManager = playerCharacter.getCharItemManager(); + + if (itemManager == null) + return; + + // TODO uncomment this block after we determine when we + // setVaultOpen(false) + /* + * // cannot have bank and vault open at the same time if + * (itemManager.isVaultOpen()) return; + */ + + if (itemManager.getTradingWith() != null) { + return; + // TODO close trade window here - simple once this is moved to WS + } + + itemManager.setBankOpen(true); + // TODO When do we setBankOpen(false)? I don't think the client sends a + // "CloseBank" message. + + AckBankWindowOpenedMsg ackBankWindowOpenedMsg = new AckBankWindowOpenedMsg(playerCharacter, 0L, 0L); + + Dispatch dispatch = Dispatch.borrow(playerCharacter, ackBankWindowOpenedMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + ReqBankInventoryMsg reqBankInventoryMsg = new ReqBankInventoryMsg(playerCharacter, 0L); + dispatch = Dispatch.borrow(playerCharacter, reqBankInventoryMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + ShowBankInventoryMsg showBankInventoryMsg = new ShowBankInventoryMsg(playerCharacter, 0L); + dispatch = Dispatch.borrow(playerCharacter, showBankInventoryMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + writer.putInt(this.messageType); + for (int i = 0; i < 3; i++) + writer.putInt(0); + if (messageType == 1) + writer.putString(this.language); + else + writer.putString(""); + writer.putInt(this.vendorObjectType); + writer.putInt(this.vendorObjectID); + + for (int i = 0; i < 3; i++) + writer.putInt(0); + writer.put((byte) 0); + writer.put((byte) 0); + if (this.messageType == 1) { + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + } + else if (this.messageType == 4) { + writer.putInt(0); + writer.putInt(0x364AF0D0); // 0x325695C0 + if (this.vd != null) + writer.putInt(vd.getObjectUUID()); + else + writer.putInt(this.unknown03); + writer.putInt(1); + writer.put((byte) 0); + writer.putInt(this.unknown03); + writer.put((byte) 0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(2); // 2 + if (menuType == 2) + writer.putInt(3); + else if (menuType == 3) + writer.putInt(4); + else + writer.putInt(5); + // writer.putInt(3); // 3=buy/sell/trade, 4=untrain, 5 closes + writer.putInt(10); // 10 + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + return; + + } + writer.putInt(15); + writer.putInt(5); + if (this.vd != null) + writer.putInt(vd.getObjectUUID()); + else + writer.putInt(this.unknown03); + + //filename datablock + writer.putInt(1); + writer.put((byte) 1); + writer.putInt(this.unknown03); + writer.put((byte) 1); + writer.putString(vd.getDialogType()); + writer.putInt(0); + writer.putInt(1); + writer.putInt(4); + writer.putInt(0); + writer.put((byte) 1); + + //vendor dialog datablock + writer.putString(vd.getDialogType()); + writer.putString(vd.getIntro()); + writer.put((byte) 0); + + //menu options datablock + writer.putInt(1); + writer.putString(" [ " + vd.getIntro() + " ] "); + ArrayList options = vd.getOptions(); + writer.putInt((options.size() + 1)); + for (MenuOption option : options) { + if (option.getMessage().equals(" [ Merchant options ] ")) { + writer.putInt(16); + } + else { + writer.putInt(14); + } + writer.put((byte) 0); + writer.putInt(0); + writer.put((byte) 0); + writer.putInt(1); + writer.putString(option.getMessage()); + writer.putInt(option.getOptionID()); + for (int i = 0; i < 3; i++) + writer.putInt(0); + } + writer.putInt(10); + writer.put((byte) 0); + writer.putInt(0); + writer.put((byte) 0); + writer.putInt(1); + writer.putString("Done"); + for (int i = 0; i < 4; i++) + writer.putInt(0); + // writer.putInt(1); + // writer.putInt(2); + for (int i = 0; i < 4; i++) + writer.putInt(0); + } + + /** + * Deserializes the subclass specific items from the supplied + * ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.messageType = reader.getInt(); + for (int i = 0; i < 3; i++) + reader.getInt(); + this.language = reader.getString(); + this.vendorObjectType = reader.getInt(); + this.vendorObjectID = reader.getInt(); + for (int i = 0; i < 3; i++) + reader.getInt(); + reader.get(); + reader.get(); + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + this.unknown03 = reader.getInt(); + reader.getInt(); + // if (this.messageType == 1) { + reader.get(); + reader.getInt(); + reader.get(); + reader.getInt(); + reader.getShort(); + // return; + // } + // reader.get(); + // this.unknown04 = reader.getInt(); + // reader.get(); + // TODO more message to go here + } + + public int getMessageType() { + return this.messageType; + } + + public void setMessageType(int value) { + this.messageType = value; + } + + public int getVendorObjectType() { + return this.vendorObjectType; + } + + public int getVendorObjectID() { + return this.vendorObjectID; + } + + public int getUnknown01() { + return this.unknown01; + } + + public int getUnknown02() { + return this.unknown02; + } + + public int getUnknown03() { + return this.unknown03; + } + + public void setUnknown03(int value) { + this.unknown03 = value; + } + + public void setLanguage(String value) { + this.language = value; + } + + public void updateMessage(int messageType, int menuType) { + this.messageType = messageType; + this.menuType = menuType; + } + + public void updateMessage(int messageType, String dialogType, String intro, int menuType) { + this.messageType = messageType; + this.dialogType = dialogType; + this.intro = intro; + this.introCode = " [ " + this.intro + " ] "; + this.menuType = menuType; + } + + public void updateMessage(int messageType, VendorDialog vd) { + this.messageType = messageType; + this.vd = vd; + } +} diff --git a/src/engine/net/client/msg/ViewResourcesMessage.java b/src/engine/net/client/msg/ViewResourcesMessage.java new file mode 100644 index 00000000..074d6077 --- /dev/null +++ b/src/engine/net/client/msg/ViewResourcesMessage.java @@ -0,0 +1,198 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum; +import engine.gameManager.DbManager; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.*; + + +public class ViewResourcesMessage extends ClientNetMsg { + + //resource hashes + //0001240F + //002339C7 (locked) + //00263669 + //00270DC3 + //002D6DEF + //047636B3 (locked) + //047B0CC1 + //04AB3761 + //1AF5DB3A + //47033237 + //4F8EFB0F + //5B57C3E4 + //86A0AC24 + //9705591E + //98378CB4 + //98D78D15 + //A0703E8C (locked) + //A0DA3807 + //A1723A93 + //A26E59CF + //D665C60F + //E3D05AE3 + //ED13904D + + private Guild guild; + private Building warehouseBuilding; + private Warehouse warehouseObject; + private PlayerCharacter player; + private City city; + + /** + * This is the general purpose constructor. + */ + + public ViewResourcesMessage(PlayerCharacter player) { + super(Protocol.VIEWRESOURCES); + this.guild = null; + this.player = player; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public ViewResourcesMessage(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.VIEWRESOURCES, origin, reader); + } + + public boolean configure() { + + if (this.warehouseBuilding.getParentZone() == null) + return false; + + this.city = (City) DbManager.getObject(Enum.GameObjectType.City, this.warehouseBuilding.getParentZone().getPlayerCityUUID()); + + if (this.city == null) + return false; + + this.warehouseObject = this.city.getWarehouse(); + + return this.warehouseObject != null; + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + writer.putInt(warehouseObject.getResources().size()); + + for (ItemBase ib : (warehouseObject.getResources().keySet())){ + + writer.putInt(ib.getHashID()); + writer.putInt((warehouseObject.getResources().get(ib))); + + + if (warehouseObject.isResourceLocked(ib) == true) + writer.put((byte)1); + else + writer.put((byte)0); + } + + writer.putInt(warehouseObject.getResources().size()); + + for (ItemBase ib : warehouseObject.getResources().keySet()){ + writer.putInt(ib.getHashID()); + writer.putInt(0); //available? + writer.putInt(Warehouse.getMaxResources().get(ib.getUUID())); //max? + } + GuildTag._serializeForDisplay(guild.getGuildTag(),writer); + + // Serialize what tags? Errant? + + writer.putInt(16); + writer.putInt(16); + writer.putInt(16); + writer.putInt(0); + writer.putInt(0); + + if (GuildStatusController.isTaxCollector(player.getGuildStatus())){ + writer.putInt(1); + writer.putString("Deposit"); + writer.putInt(-1760114543); + writer.putInt(1); + writer.put((byte)0); + + }else + + if (this.player.getGuild().equals(warehouseBuilding.getGuild()) && (GuildStatusController.isInnerCouncil(this.player.getGuildStatus()))){ + writer.putInt(4); + writer.putString("Lock"); + writer.putInt(2393548); + writer.putInt(1); //locked? on/off + writer.put((byte)0); + writer.putString("Deposit"); + writer.putInt(-1760114543); + + writer.putInt(1); + writer.put((byte)0); + writer.putString("Manage Mines"); + writer.putInt(-820683698); + writer.putInt(1); + writer.put((byte)0); + writer.putString("Withdraw"); + writer.putInt(-530228289); + writer.putInt(1); + writer.put((byte)0); + }else{ + writer.putInt(2); + writer.putString("Lock"); + writer.putInt(2393548); + writer.putInt(0); //locked? on/off + writer.put((byte)0); + writer.putString("Deposit"); + writer.putInt(-1760114543); + writer.putInt(1); + writer.put((byte)0); + } + + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + // this.locX = reader.getFloat(); + // this.locY = reader.getFloat(); + // this.locZ = reader.getFloat(); + // this.name = reader.getString(); + // this.unknown01 = reader.getInt(); + } + + public void setGuild(Guild guild) { + this.guild = guild; + } + + public void setWarehouseBuilding(Building warehouseBuilding) { + this.warehouseBuilding = warehouseBuilding; + } +} diff --git a/src/engine/net/client/msg/VisualUpdateMessage.java b/src/engine/net/client/msg/VisualUpdateMessage.java new file mode 100644 index 00000000..348cc338 --- /dev/null +++ b/src/engine/net/client/msg/VisualUpdateMessage.java @@ -0,0 +1,91 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.Enum.GameObjectType; +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractGameObject; +import engine.objects.Building; + +public class VisualUpdateMessage extends ClientNetMsg { + private int effectType; + private AbstractGameObject ago; + private Building building; + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public VisualUpdateMessage(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.VISUALUPDATE, origin, reader); + } + + public VisualUpdateMessage() { + super(Protocol.VISUALUPDATE); + } + + public VisualUpdateMessage(AbstractGameObject ago, int visualID) { + super(Protocol.VISUALUPDATE); + + if (ago == null) + return; + + this.effectType = visualID; + this.ago = ago; + + } + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + + } + + public void configure() { + + if (this.ago.getObjectType() == GameObjectType.Building) + this.building = (Building)this.ago; + else + this.building = null; + + } + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) throws SerializationException { + + if (this.building == null) { + writer.putInt(4); + writer.putInt(0); + writer.putInt(this.effectType); + writer.putInt(ago.getObjectType().ordinal()); + writer.putInt(ago.getObjectUUID()); + writer.putInt(0); + return; + } + + writer.putShort((short)100); + writer.putShort((short)120); + writer.putInt(1); + writer.putInt(this.building.getObjectType().ordinal()); + writer.putInt(this.building.getObjectUUID()); + writer.putInt(this.effectType); + + } +} diff --git a/src/engine/net/client/msg/WhoRequestMsg.java b/src/engine/net/client/msg/WhoRequestMsg.java new file mode 100644 index 00000000..c04dbd2b --- /dev/null +++ b/src/engine/net/client/msg/WhoRequestMsg.java @@ -0,0 +1,106 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; + +public class WhoRequestMsg extends ClientNetMsg { + + private int set; + private int filterType; + private String filter; + + /** + * This is the general purpose constructor. + */ + public WhoRequestMsg() { + super(Protocol.WHOREQUEST); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public WhoRequestMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.WHOREQUEST, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.set); + writer.putInt(this.filterType); + writer.putString(this.filter); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.set = reader.getInt(); + this.filterType = reader.getInt(); + this.filter = reader.getString(); + } + + /** + * @return the set + */ + public int getSet() { + return set; + } + + /** + * @return the filterType + */ + public int getFilterType() { + return filterType; + } + + /** + * @return the filter + */ + public String getFilter() { + return filter; + } + + /** + * @param set + * the set to set + */ + public void setSet(int set) { + this.set = set; + } + + /** + * @param filterType + * the filterType to set + */ + public void setFilterType(int filterType) { + this.filterType = filterType; + } + + /** + * @param filter + * the filter to set + */ + public void setFilter(String filter) { + this.filter = filter; + } + +} diff --git a/src/engine/net/client/msg/WhoResponseMsg.java b/src/engine/net/client/msg/WhoResponseMsg.java new file mode 100644 index 00000000..85184b77 --- /dev/null +++ b/src/engine/net/client/msg/WhoResponseMsg.java @@ -0,0 +1,313 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum; +import engine.gameManager.SessionManager; +import engine.net.*; +import engine.net.client.ClientConnection; +import engine.net.client.Protocol; +import engine.objects.Guild; +import engine.objects.GuildStatusController; +import engine.objects.PlayerCharacter; +import engine.objects.PromotionClass; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; + +public class WhoResponseMsg extends ClientNetMsg { + + private int unknown01; + private static int worldPop; + private ArrayList members = new ArrayList<>(); + + /** + * This is the general purpose constructor. + */ + public WhoResponseMsg() { + super(Protocol.WHORESPONSE); + this.unknown01 = 1; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public WhoResponseMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.WHORESPONSE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(unknown01); + writer.putInt(worldPop); + int size = this.members.size(); + writer.putInt(size); + //PlayerCharacter pc : this.members + for (PlayerCharacter pc: this.members) { + writer.putInt(pc.getObjectType().ordinal()); + writer.putInt(pc.getObjectUUID()); + writer.putString(pc.getFirstName()); + writer.putString(pc.getLastName()); + writer.putInt(pc.getRaceToken()); + writer.putInt(pc.getClassToken()); + writer.putInt(pc.getLevel()); + writer.putInt(0); // unknown 0 + writer.putInt(pc.isMale() ? 1 : 2); //gender? + writer.putInt(0); // unknown 0 + Guild guild = pc.getGuild(); + if (guild != null) { + writer.put((byte) 1); // Send Guild Info + writer.put((byte) 1); // SkipPartTwo + writer.putString(guild.getName()); + writer.putInt(guild.getCharter()); // Charter Type + writer.putInt(GuildStatusController.getTitle(pc.getGuildStatus())); + writer.putString("what"); // City?, Skip if SkipPartTwo = 0x00 + } else { + writer.put((byte) 0); // Don't Send guild info + writer.put((byte) 0); // Don't send last string + } + } + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.unknown01 = reader.getInt(); + WhoResponseMsg.worldPop = reader.getInt(); + // TODO implement Deserialization + } + + /** + * @return the number of PlayerCharacters + */ + public int getSize() { + return this.members.size(); + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the worldPop + */ + public static int getWorldPop() { + return WhoResponseMsg.worldPop; + } + + /** + * @param worldPop + * the worldPop to set + */ + public static void setWorldPop(int worldPop) { + WhoResponseMsg.worldPop = worldPop; + } + + public boolean addMember(PlayerCharacter pc) { + if (this.members.size() > 100) + return false; + this.members.add(pc); + return true; + } + + public static void HandleResponse(int set, int filterType, String filter, ClientConnection origin) { + + WhoResponseMsg msg = new WhoResponseMsg(); + WhoResponseMsg.setWorldPop(SessionManager.getAllActivePlayerCharacters().size()); + + PlayerCharacter playerCharacter = SessionManager.getPlayerCharacter(origin); + + if (playerCharacter != null) { + //check threshold + long currentTime = System.currentTimeMillis(); + long timestamp = playerCharacter.getTimeStamp("whoWindow"); + long dif = currentTime - timestamp; + if (dif < MBServerStatics.WHO_WINDOW_THRESHOLD) + return; + playerCharacter.setTimeStamp("whoWindow", currentTime); + } + + if (playerCharacter == null) { + } else if (filterType == 0) { // No Filter, send everyone + for (PlayerCharacter player : SessionManager.getAllActivePlayerCharacters()) + if (player != null) + if (player.isActive()) + if (!isAdmin(player)) + if (!HandleSet(set, player, playerCharacter, msg)) + break; + } + + else if (filterType == 1) { // Race Filter + for (PlayerCharacter player : SessionManager.getAllActivePlayerCharacters()) + if (player != null) + if (!isAdmin(player)) + if (player.isActive()) { + String[] race = player.getRace().getName().split(","); + if (filter.compareTo(race[0]) == 0) + if (!HandleSet(set, player, playerCharacter, msg)) + break; + } + } + + else if (filterType == 2) { // Class Filter + for (PlayerCharacter player : SessionManager.getAllActivePlayerCharacters()) + if (player != null) + if (!isAdmin(player)) + if (player.isActive()) { + if (filter.compareTo(player.getBaseClass().getName()) ==0 || (player.getPromotionClass() != null && filter.compareTo(player.getPromotionClass().getName()) == 0)) + if (!HandleSet(set, player, playerCharacter, msg)) + break; + + + // TODO Promotion Class needs added to + // PlayerCharacter + // else if + // (filter.compareTo(pc.getPromotionClass().getName()) + // == 0) + // if (!HandleSet(set, pc, ori, msg)) + // break; + } + } + + else if (filterType == 3) { // Level Filter + String range[] = filter.split(" "); + int low; + int high; + + try { + low = Integer.parseInt(range[0]); + } catch (NumberFormatException e) { + low = 1; + Logger.error("WhoResponseMsg: Low value in filter is not a proper integer. Defaulting to 1. Error = " + + e.getMessage()); + } + + try { + high = Integer.parseInt(range[1]); + } catch (NumberFormatException e) { + high = 1; + Logger.error( + "WhoResponseMsg: High value in filter is not a proper integer. Defaulting to 75. Error = " + e.getMessage()); + } + + for (PlayerCharacter player : SessionManager.getAllActivePlayerCharacters()) + if (player != null) + if (!isAdmin(player)) + if (player.isActive()) + if (player.getLevel() >= low && player.getLevel() <= high) + if (!HandleSet(set, player, playerCharacter, msg)) + break; + } + + else if (filterType == 4) { // Name Filter + filter = filter.toLowerCase(); + for (PlayerCharacter player : SessionManager.getAllActivePlayerCharacters()) + if (player != null) + if (!isAdmin(player)) + if (player.isActive()) + if (player.getName().toLowerCase().indexOf(filter) > -1) + if (!HandleSet(set, player, playerCharacter, msg)) + break; + } + + else if (filterType == 6) { // Status Filter + int type = Integer.parseInt(filter); + for (PlayerCharacter player : SessionManager.getAllActivePlayerCharacters()) + if (player != null) + if (!isAdmin(player)) + if (player.isActive()) { + if (type == 1) { + if (player.isLFGroup()) + if (!HandleSet(set, player, playerCharacter, msg)) + break; + } else if (type == 2) { + if (player.isLFGuild()) + if (!HandleSet(set, player, playerCharacter, msg)) + break; + } else if (type == 3) { + if (player.isRecruiting()) + if (!HandleSet(set, player, playerCharacter, msg)) + break; + } else + break; + } + } + + Dispatch dispatch = Dispatch.borrow(origin.getPlayerCharacter(), msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + } + + private static boolean HandleSet(int set, PlayerCharacter a, PlayerCharacter b, WhoResponseMsg msg) { + // This function handles the sets. 0 = search all, 1 = search nation, + // 2 = search guild. Returns true until 100 (max) players found + + if (set == 0) { // All + return msg.addMember(a); + } else if (set == 1) { // Nation + if (compareNation(a, b)) + return msg.addMember(a); + } else if (set == 2) { // Guild + if (compareGuild(a, b)) + return msg.addMember(a); + } + return true; + } + + private static boolean isAdmin(PlayerCharacter pc) { + PromotionClass promo = pc.getPromotionClass(); + if (promo == null) + return false; + return promo.getObjectUUID() <= 2503 || promo.getObjectUUID() >= 2527; + } + + private static boolean compareGuild(PlayerCharacter a, PlayerCharacter b) { + if (a == null || b == null) + return false; + return Guild.sameGuild(a.getGuild(), b.getGuild()); + } + + private static boolean compareNation(PlayerCharacter a, PlayerCharacter b) { + if (a == null || b == null) + return false; + Guild aG = a.getGuild(); + Guild bG = b.getGuild(); + if (aG == null || bG == null) + return false; + return (aG.getNation() == bG.getNation()) && aG.getNation() != null; + } + + + @Override + protected int getPowerOfTwoBufferSize() { + return 14; + } + +} diff --git a/src/engine/net/client/msg/WorldDataMsg.java b/src/engine/net/client/msg/WorldDataMsg.java new file mode 100644 index 00000000..119163c5 --- /dev/null +++ b/src/engine/net/client/msg/WorldDataMsg.java @@ -0,0 +1,134 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + + +import engine.exception.SerializationException; +import engine.gameManager.ConfigManager; +import engine.gameManager.ZoneManager; +import engine.net.AbstractConnection; +import engine.net.AbstractNetMsg; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Zone; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +public class WorldDataMsg extends ClientNetMsg { + + public static final long wdComp = 0xFF00FF0000000001L; + private static byte ver; + + /** + * This is the general purpose constructor. + */ + public WorldDataMsg() { + super(Protocol.NEWWORLD); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public WorldDataMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.NEWWORLD, origin, reader); + } + + /** + * @see AbstractNetMsg#getPowerOfTwoBufferSize() + */ + @Override + protected int getPowerOfTwoBufferSize() { + // Larger size for historically larger opcodes + return (18); // 2^17 == 131,072 + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) + throws SerializationException { + + + // TODO replace this return with SerializationException + + Zone root = ZoneManager.getSeaFloor(); + if (root == null){ + Logger.error("Failed to find Sea Floor!"); + return; + } + + writer.putString(ConfigManager.MB_WORLD_NAME.getValue()); + writer.putInt(512); + writer.putInt(384); + + writer.putInt(MBServerStatics.worldMapID); + writer.putInt(0x00000000); + + writer.putInt(getTotalMapSize(root) + 1); + Zone.serializeForClientMsg(root,writer); + + Zone hotzone = ZoneManager.getHotZone(); + + if (hotzone == null) + writer.putLong(0L); + else { + writer.putInt(hotzone.getObjectType().ordinal()); + writer.putInt(hotzone.getObjectUUID()); + } + + + + + writer.putFloat(0); + writer.putFloat(1); + writer.putFloat(0); + writer.putFloat(0.69999999f); + writer.putFloat(.5f); + writer.putFloat(1); + writer.putFloat(.5f); + writer.putFloat(0.69999999f); + writer.putFloat(1); + writer.putFloat(0); + writer.putFloat(0); + writer.putFloat(0.69999999f); + writer.putFloat(1); + writer.putFloat(.5f); + writer.putFloat(.5f); + writer.putFloat(0.69999999f); + writer.putFloat(1); + + + + } + + + @Override + protected void _deserialize(ByteBufferReader reader) { + + } + + private static int getTotalMapSize(Zone root) { + if (root.getNodes().isEmpty()) + return 0; + + int size = root.getNodes().size(); + for (Zone child : root.getNodes()) + size += getTotalMapSize(child); + return size; + } + + +} diff --git a/src/engine/net/client/msg/WorldObjectMsg.java b/src/engine/net/client/msg/WorldObjectMsg.java new file mode 100644 index 00000000..403563e5 --- /dev/null +++ b/src/engine/net/client/msg/WorldObjectMsg.java @@ -0,0 +1,282 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum; +import engine.Enum.RunegateType; +import engine.gameManager.DbManager; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.Network; +import engine.net.client.Protocol; +import engine.objects.AbstractGameObject; +import engine.objects.City; +import engine.objects.Mine; +import engine.objects.Runegate; +import engine.session.Session; +import org.pmw.tinylog.Logger; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; + +public class WorldObjectMsg extends ClientNetMsg { + + private Session s; + private boolean forEnterWorld; + private static ByteBuffer cachedEnterWorld; + private static long cachedExpireTime; + + public static final long wdComp = 0xFF00FF0000000003L; + private static byte ver = 1; + + private boolean updateCities = false; + private boolean updateRunegates = false; + private boolean updateMines = false; + + /** + * This is the general purpose constructor. + * + * @param s + * Session + * @param forEnterWorld + * boolean flag + */ + public WorldObjectMsg(Session s, boolean forEnterWorld) { + super(Protocol.CITYDATA); + this.s = s; + this.forEnterWorld = forEnterWorld; + } + + public WorldObjectMsg(boolean updateCities, boolean updateRunegates, boolean updateMines) { + super(Protocol.CITYDATA); + this.s = null; + this.forEnterWorld = false; + this.updateCities = updateCities; + this.updateRunegates = updateRunegates; + this.updateMines = updateMines; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public WorldObjectMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.CITYDATA, origin, reader); + this.forEnterWorld = false; + } + + @Override + protected int getPowerOfTwoBufferSize() { + return (18); // 2^14 == 16384 + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + if (this.forEnterWorld) + serializeForEnterWorld(writer); + else + serializeForMapUpdate(writer); + } + + /** + * Specific use serializer + * + * @param writer + */ + private void serializeForMapUpdate(ByteBufferWriter writer) { + + //Handle City updates + + if (this.updateCities) { + writer.put((byte) 0); + ArrayList cityList = new ArrayList<>(); + ConcurrentHashMap map = DbManager.getMap(Enum.GameObjectType.City); + if (map != null) { + for (AbstractGameObject ago : map.values()) + if (ago.getObjectType().equals(Enum.GameObjectType.City)) + cityList.add((City)ago); + + writer.putInt(cityList.size()); + for (City city: cityList){ + City.serializeForClientMsg(city, writer); + } + + } else { + Logger.error("missing city map"); + writer.putInt(0); + } + } else + writer.put((byte) 1); + + + //Handle Runegate updates + if (this.updateRunegates) { + + writer.put((byte) 0); + writer.putInt(RunegateType.values().length); + + for(RunegateType gateType : engine.Enum.RunegateType.values()) { + + Runegate.getRunegates()[gateType.ordinal()]._serializeForEnterWorld(writer); + } + } else + writer.put((byte) 1); + + + //Handle Mine updates + try{ + if (this.updateMines) { + ArrayList mineList = new ArrayList<>(); + for (Mine toAdd: Mine.mineMap.keySet()){ + mineList.add(toAdd); + } + + writer.putInt(mineList.size()); + for (Mine mine: mineList) + Mine.serializeForClientMsg(mine, writer); + } else + writer.putInt(0); + }catch(Exception e){ + Logger.error(e); + } + + + + writer.put((byte) 0); // PAD + } + + /** + * Specific use serializer + * + * @param writer + */ + private void serializeForEnterWorld(ByteBufferWriter writer) { + if (s == null || s.getPlayerCharacter() == null) + return; + + long startT = System.currentTimeMillis(); + + if (cachedEnterWorld == null) { + // Never before been cached, so init stuff + cachedEnterWorld = Network.byteBufferPool.getBuffer(19); + cachedExpireTime = 0L; + } + + //Check to see if its time to renew cache. + if (cachedExpireTime < System.currentTimeMillis()) { + synchronized (cachedEnterWorld) { + WorldObjectMsg.attemptSerializeForEnterWorld(cachedEnterWorld); + } + cachedExpireTime = startT + 60000; + } + + writer.putBB(cachedEnterWorld); + + } + + private static void attemptSerializeForEnterWorld(ByteBuffer bb) { + bb.clear(); + ByteBufferWriter temp = new ByteBufferWriter(bb); + temp.put((byte) 0); // PAD + + + ArrayList cityList = new ArrayList<>(); + ConcurrentHashMap map = DbManager.getMap(Enum.GameObjectType.City); + for (AbstractGameObject ago : map.values()) + + if (ago.getObjectType().equals(Enum.GameObjectType.City)) + cityList.add((City)ago); + + temp.putInt(cityList.size()); + + for (City city: cityList) + City.serializeForClientMsg(city, temp); + temp.put((byte) 0); // PAD + + // Serialize runegates + + temp.putInt(RunegateType.values().length); + + for(RunegateType gateType : engine.Enum.RunegateType.values()) { + + Runegate.getRunegates()[gateType.ordinal()]._serializeForEnterWorld(temp); + } + + ArrayList mineList = new ArrayList<>(); + for (Mine toAdd : Mine.mineMap.keySet()){ + mineList.add(toAdd); + } + + temp.putInt(mineList.size()); + for (Mine mine: mineList) + Mine.serializeForClientMsg(mine, temp); + temp.put((byte) 0); // PAD + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) + { + // Client only sends 11 bytes. + + byte type = reader.get(); + + if (type == 1){ + reader.get(); + reader.get(); + reader.getInt(); + + }else{ + reader.get(); + reader.getInt(); + reader.get(); + reader.getInt(); + } + + } + + /** + * @return the s + */ + public Session getS() { + return s; + } + + /** + * @return the forEnterWorld + */ + public boolean isForEnterWorld() { + return forEnterWorld; + } + + public void updateCities(boolean value) { + this.updateCities = value; + } + + public void updateRunegates(boolean value) { + this.updateRunegates = value; + } + + public void updateMines(boolean value) { + this.updateMines = value; + } + + +} diff --git a/src/engine/net/client/msg/WorldRealmMsg.java b/src/engine/net/client/msg/WorldRealmMsg.java new file mode 100644 index 00000000..f2f05b0a --- /dev/null +++ b/src/engine/net/client/msg/WorldRealmMsg.java @@ -0,0 +1,100 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg; + +import engine.Enum.RealmType; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.Realm; + + +public class WorldRealmMsg extends ClientNetMsg { + + + + /** + * This is the general purpose constructor. + */ + public WorldRealmMsg() { + super(Protocol.REALMDATA); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public WorldRealmMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.REALMDATA, origin, reader); + } + + @Override + protected int getPowerOfTwoBufferSize() { + return (14); // 2^14 == 16384 + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + + @Override + protected void _serialize(ByteBufferWriter writer) { + + int realmCount; + int realmID; + Realm serverRealm; + + + realmCount = RealmType.values().length - 1; + // Realm count without seafloor + + writer.putInt(realmCount); + + for (RealmType realmType : RealmType.values()) { + + realmID = realmType.getRealmID(); + // Don't serialize seafloor + + if (realmID == 0) + continue; + + serverRealm = Realm.getRealm(realmID); + serverRealm.serializeForClientMsg(writer); + + } + + writer.putInt(0x0); + writer.putInt(3000000); + + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + // TODO Implement Deserialization + } +} diff --git a/src/engine/net/client/msg/chat/AbstractChatMsg.java b/src/engine/net/client/msg/chat/AbstractChatMsg.java new file mode 100644 index 00000000..714f71b1 --- /dev/null +++ b/src/engine/net/client/msg/chat/AbstractChatMsg.java @@ -0,0 +1,190 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.chat; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; +import engine.objects.AbstractWorldObject; + +public abstract class AbstractChatMsg extends ClientNetMsg { + + protected int sourceType; + protected int sourceID; + protected String sourceName; + protected AbstractWorldObject source; + + protected int unknown01; + protected String message; + protected int unknown02; + + /** + * This is the general purpose constructor. + */ + protected AbstractChatMsg(Protocol protocolMsg, AbstractWorldObject source, String message) { + super(protocolMsg); + + this.unknown01 = 0; + this.message = message; + this.source = source; + this.unknown02 = 0; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + protected AbstractChatMsg(Protocol protocolMsg, AbstractConnection origin, ByteBufferReader reader) { + super(protocolMsg, origin, reader); + + //THIS may cause initialization error, but messing up guild chat + //this.unknown01 = 0; + //this.unknown02 = 0; + } + + /** + * Copy constructor + */ + protected AbstractChatMsg(AbstractChatMsg msg) { + super(msg.getProtocolMsg()); + this.sourceType = msg.sourceType; + this.sourceID = msg.sourceID; + this.sourceName = msg.sourceName; + this.source = msg.source; + this.unknown01 = msg.unknown01; + this.message = msg.getMessage(); + this.unknown02 = msg.getUnknown02(); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected abstract void _deserialize(ByteBufferReader reader) ; + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected abstract void _serialize(ByteBufferWriter writer) throws SerializationException; + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @return the message + */ + public String getMessage() { + return message; + } + + /** + * @return the unknown02 + */ + public int getUnknown02() { + return unknown02; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @param message + * the message to set + */ + public void setMessage(String message) { + this.message = message; + } + + /** + * @param unknown02 + * the unknown02 to set + */ + public void setUnknown02(int unknown02) { + this.unknown02 = unknown02; + } + + /** + * @return the sourceObjName + */ + public AbstractWorldObject getSource() { + return source; + } + + /** + * @param sourceObjName + * the sourceObjName to set + */ + public void setSource(AbstractWorldObject source) { + this.source = source; + } + + /** + * @return the sourceType + */ + public int getSourceType() { + return sourceType; + } + + /** + * @return the sourceID + */ + public int getSourceID() { + return sourceID; + } + + + /** + * @return the sourceName + */ + public String getSourceName() { + return sourceName; + } + + /** + * @param sourceType + * the sourceType to set + */ + public void setSourceType(int sourceType) { + this.sourceType = sourceType; + } + + /** + * @param sourceID + * the sourceID to set + */ + public void setSourceID(int sourceID) { + this.sourceID = sourceID; + } + + /** + * @param sourceName + * the sourceName to set + */ + public void setSourceName(String sourceName) { + this.sourceName = sourceName; + } + +} diff --git a/src/engine/net/client/msg/chat/ChatCSRMsg.java b/src/engine/net/client/msg/chat/ChatCSRMsg.java new file mode 100644 index 00000000..f70450a7 --- /dev/null +++ b/src/engine/net/client/msg/chat/ChatCSRMsg.java @@ -0,0 +1,78 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.chat; + + +import engine.gameManager.SessionManager; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.ClientConnection; +import engine.net.client.Protocol; +import engine.objects.AbstractWorldObject; +import engine.server.MBServerStatics; +import engine.session.Session; + +public class ChatCSRMsg extends AbstractChatMsg { + + /** + * This is the general purpose constructor. + */ + public ChatCSRMsg(AbstractWorldObject source, String message) { + super(Protocol.CHATCSR, source, message); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ChatCSRMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CHATCSR, origin, reader); + Session s = SessionManager.getSession((ClientConnection) origin); + if (s == null) + return; + this.source = s.getPlayerCharacter(); + } + + /** + * Copy constructor + */ + public ChatCSRMsg(ChatCSRMsg msg) { + super(msg); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); + reader.getLong(); + this.message = reader.getString(); + reader.getInt(); // pad + this.unknown02 = reader.getInt(); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.source.getObjectType().ordinal()); + writer.putInt(this.source.getObjectUUID()); + writer.putInt(this.unknown01); + writer.putString(this.message); + writer.putString(this.source.getName()); + writer.putInt(MBServerStatics.worldMapID); + } + +} diff --git a/src/engine/net/client/msg/chat/ChatCityMsg.java b/src/engine/net/client/msg/chat/ChatCityMsg.java new file mode 100644 index 00000000..67b64309 --- /dev/null +++ b/src/engine/net/client/msg/chat/ChatCityMsg.java @@ -0,0 +1,88 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.chat; + +import engine.Enum.GameObjectType; +import engine.gameManager.SessionManager; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractGameObject; +import engine.objects.AbstractWorldObject; +import engine.server.MBServerStatics; + +public class ChatCityMsg extends AbstractChatMsg { + + /** + * This is the general purpose constructor. + */ + public ChatCityMsg(AbstractWorldObject source, String message) { + super(Protocol.CHATCITY, source, message); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ChatCityMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CHATCITY, origin, reader); + } + + /** + * Copy constructor + */ + public ChatCityMsg(ChatCityMsg msg) { + super(msg); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + long sourceID = reader.getLong(); + int objectUUID = AbstractGameObject.extractUUID(GameObjectType.PlayerCharacter, sourceID); + this.source = SessionManager.getPlayerCharacterByID(objectUUID); + this.unknown01 = reader.getInt(); + this.message = reader.getString(); + this.sourceName = reader.getString(); + this.unknown02 = reader.getInt(); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + // TODO Implement Serialize + if (this.source != null){ + writer.putInt(source.getObjectType().ordinal()); + writer.putInt(source.getObjectUUID()); + } + + else + writer.putLong(0L); + writer.putInt(0); + writer.putString(this.message); + if (this.source == null) { + // TODO log error here + writer.putString(""); + writer.putInt(0); + } else { + writer.putString(((AbstractCharacter) this.source).getFirstName()); + writer.putInt(MBServerStatics.worldMapID); + } + } + +} diff --git a/src/engine/net/client/msg/chat/ChatGlobalMsg.java b/src/engine/net/client/msg/chat/ChatGlobalMsg.java new file mode 100644 index 00000000..c3561417 --- /dev/null +++ b/src/engine/net/client/msg/chat/ChatGlobalMsg.java @@ -0,0 +1,89 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.chat; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.server.MBServerStatics; + +public class ChatGlobalMsg extends AbstractChatMsg { + + /** + * This is the general purpose constructor. + */ + + //chat global is using leader channel protocolMsg now. + public ChatGlobalMsg(AbstractWorldObject source, String message) { + super(Protocol.LEADERCHANNELMESSAGE, source, message); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ChatGlobalMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.LEADERCHANNELMESSAGE, origin, reader); + } + + /** + * Copy constructor + */ + public ChatGlobalMsg(ChatGlobalMsg msg) { + super(msg); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.unknown01 = reader.getInt(); + + this.message = reader.getString(); + this.sourceName = reader.getString(); + + this.unknown02 = reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.putInt(this.unknown01); + writer.putString(this.message); + if (this.source == null) { + // TODO log error here + writer.putString(""); + writer.putInt(0); + } else { + writer.putString(((AbstractCharacter) this.source).getFirstName()); + writer.putInt(MBServerStatics.worldMapID); + } + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + } +} diff --git a/src/engine/net/client/msg/chat/ChatGroupMsg.java b/src/engine/net/client/msg/chat/ChatGroupMsg.java new file mode 100644 index 00000000..72fd375f --- /dev/null +++ b/src/engine/net/client/msg/chat/ChatGroupMsg.java @@ -0,0 +1,75 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.chat; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractWorldObject; + +public class ChatGroupMsg extends AbstractChatMsg { + + /** + * This is the general purpose constructor. + */ + public ChatGroupMsg(AbstractWorldObject source, String message) { + super(Protocol.CHATGROUP, source, message); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ChatGroupMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CHATGROUP, origin, reader); + } + + /** + * Copy constructor + */ + public ChatGroupMsg(ChatGroupMsg msg) { + super(msg); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.unknown01 = reader.getInt(); + + this.message = reader.getString(); + this.sourceName = reader.getString(); + + this.unknown02 = reader.getInt(); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.putInt(this.unknown01); + + writer.putString(this.message); + writer.putString(this.sourceName); + + writer.putInt(this.unknown02); + } + +} diff --git a/src/engine/net/client/msg/chat/ChatGuildMsg.java b/src/engine/net/client/msg/chat/ChatGuildMsg.java new file mode 100644 index 00000000..94b77de6 --- /dev/null +++ b/src/engine/net/client/msg/chat/ChatGuildMsg.java @@ -0,0 +1,153 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.chat; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractWorldObject; + +public class ChatGuildMsg extends AbstractChatMsg { + + protected int unknown03; + protected int unknown04; + protected int unknown05; + protected int unknown06; + + /** + * This is the general purpose constructor. + */ + public ChatGuildMsg(AbstractWorldObject source, String message) { + super(Protocol.CHATGUILD, source, message); + this.unknown03 = 0; + this.unknown04 = 0; + this.unknown05 = 0; + this.unknown06 = 0; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ChatGuildMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CHATGUILD, origin, reader); + } + + /** + * Copy constructor + */ + public ChatGuildMsg(ChatGuildMsg msg) { + super(msg); + this.unknown03 = msg.unknown03; + this.unknown04 = msg.unknown04; + this.unknown05 = msg.unknown05; + this.unknown06 = msg.unknown06; + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.unknown01 = reader.getInt(); + this.message = reader.getString(); + this.unknown02 = reader.getInt(); + this.sourceName = reader.getString(); + this.unknown03 = reader.getInt(); + this.unknown04 = reader.getInt(); + this.unknown05 = reader.getInt(); + this.unknown06 = reader.getInt(); + } + + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.putInt(this.unknown01); + writer.putString(this.message); + writer.putInt(this.unknown02); + writer.putString(this.sourceName); + writer.putInt(this.unknown03); + writer.putInt(this.unknown04); + writer.putInt(this.unknown05); + writer.putInt(this.unknown06); + } + + /** + * @return the unknown03 + */ + public int getUnknown03() { + return unknown03; + } + + /** + * @param unknown03 + * the unknown03 to set + */ + public void setUnknown03(int unknown03) { + this.unknown03 = unknown03; + } + + /** + * @return the unknown04 + */ + public int getUnknown04() { + return unknown04; + } + + /** + * @param unknown04 + * the unknown04 to set + */ + public void setUnknown04(int unknown04) { + this.unknown04 = unknown04; + } + + /** + * @return the unknown05 + */ + public int getUnknown05() { + return unknown05; + } + + /** + * @param unknown05 + * the unknown05 to set + */ + public void setUnknown05(int unknown05) { + this.unknown05 = unknown05; + } + + /** + * @return the unknown06 + */ + public int getUnknown06() { + return unknown06; + } + + /** + * @param unknown06 + * the unknown06 to set + */ + public void setUnknown06(int unknown06) { + this.unknown06 = unknown06; + } + +} diff --git a/src/engine/net/client/msg/chat/ChatICMsg.java b/src/engine/net/client/msg/chat/ChatICMsg.java new file mode 100644 index 00000000..cc50d26a --- /dev/null +++ b/src/engine/net/client/msg/chat/ChatICMsg.java @@ -0,0 +1,132 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.chat; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractWorldObject; + +public class ChatICMsg extends AbstractChatMsg { + + protected int unknown03; + protected int unknown04; + protected int unknown05; + + /** + * This is the general purpose constructor. + */ + public ChatICMsg(AbstractWorldObject source, String message) { + super(Protocol.CHATIC, source, message); + this.unknown03 = 0; + this.unknown04 = 0; + this.unknown05 = 0; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ChatICMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CHATIC, origin, reader); + } + + /** + * Copy constructor + */ + public ChatICMsg(ChatICMsg msg) { + super(msg); + this.unknown03 = msg.unknown03; + this.unknown04 = msg.unknown04; + this.unknown05 = msg.unknown05; + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.unknown01 = reader.getInt(); + this.message = reader.getString(); + this.sourceName = reader.getString(); + this.unknown02 = reader.getInt(); + this.unknown03 = reader.getInt(); + this.unknown04 = reader.getInt(); + this.unknown05 = reader.getInt(); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.putInt(this.unknown01); + writer.putString(this.message); + writer.putString(this.sourceName); + writer.putInt(this.unknown02); + writer.putInt(this.unknown03); + writer.putInt(this.unknown04); + writer.putInt(this.unknown05); + } + + /** + * @return the unknown03 + */ + public int getUnknown03() { + return unknown03; + } + + /** + * @param unknown03 + * the unknown03 to set + */ + public void setUnknown03(int unknown03) { + this.unknown03 = unknown03; + } + + /** + * @return the unknown04 + */ + public int getUnknown04() { + return unknown04; + } + + /** + * @param unknown04 + * the unknown04 to set + */ + public void setUnknown04(int unknown04) { + this.unknown04 = unknown04; + } + + /** + * @return the unknown05 + */ + public int getUnknown05() { + return unknown05; + } + + /** + * @param unknown05 + * the unknown05 to set + */ + public void setUnknown05(int unknown05) { + this.unknown05 = unknown05; + } + +} \ No newline at end of file diff --git a/src/engine/net/client/msg/chat/ChatInfoMsg.java b/src/engine/net/client/msg/chat/ChatInfoMsg.java new file mode 100644 index 00000000..d39b4c85 --- /dev/null +++ b/src/engine/net/client/msg/chat/ChatInfoMsg.java @@ -0,0 +1,60 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.chat; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractWorldObject; + +public class ChatInfoMsg extends AbstractChatMsg { + + /** + * This is the general purpose constructor. + */ + public ChatInfoMsg(AbstractWorldObject source, String message) { + super(Protocol.CHATINFO, source, message); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ChatInfoMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CHATINFO, origin, reader); + } + + /** + * Copy constructor + */ + public ChatInfoMsg(ChatInfoMsg msg) { + super(msg); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + // TODO Implement Serialization + } + +} diff --git a/src/engine/net/client/msg/chat/ChatLeaderMsg.java b/src/engine/net/client/msg/chat/ChatLeaderMsg.java new file mode 100644 index 00000000..82f57281 --- /dev/null +++ b/src/engine/net/client/msg/chat/ChatLeaderMsg.java @@ -0,0 +1,151 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.chat; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractWorldObject; + +public class ChatLeaderMsg extends AbstractChatMsg { + + protected int unknown03; + protected int unknown04; + protected int unknown05; + protected int unknown06; + + /** + * This is the general purpose constructor. + */ + public ChatLeaderMsg(AbstractWorldObject source, String message) { + super(Protocol.LEADERCHANNELMESSAGE, source, message); + this.unknown03 = 0; + this.unknown04 = 0; + this.unknown05 = 0; + this.unknown06 = 0; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ChatLeaderMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.LEADERCHANNELMESSAGE, origin, reader); + } + + /** + * Copy constructor + */ + public ChatLeaderMsg(ChatLeaderMsg msg) { + super(msg); + this.unknown03 = msg.unknown03; + this.unknown04 = msg.unknown04; + this.unknown05 = msg.unknown05; + this.unknown06 = msg.unknown06; + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.unknown01 = reader.getInt(); + this.message = reader.getString(); + this.sourceName = reader.getString(); + this.unknown03 = reader.getInt(); + this.unknown04 = reader.getInt(); + this.unknown05 = reader.getInt(); + this.unknown06 = reader.getInt(); + } + + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.putInt(this.unknown01); + writer.putString(this.message); + writer.putString(this.sourceName); + writer.putInt(this.unknown03); + writer.putInt(this.unknown04); + writer.putInt(this.unknown05); + writer.putInt(this.unknown06); + } + + /** + * @return the unknown03 + */ + public int getUnknown03() { + return unknown03; + } + + /** + * @param unknown03 + * the unknown03 to set + */ + public void setUnknown03(int unknown03) { + this.unknown03 = unknown03; + } + + /** + * @return the unknown04 + */ + public int getUnknown04() { + return unknown04; + } + + /** + * @param unknown04 + * the unknown04 to set + */ + public void setUnknown04(int unknown04) { + this.unknown04 = unknown04; + } + + /** + * @return the unknown05 + */ + public int getUnknown05() { + return unknown05; + } + + /** + * @param unknown05 + * the unknown05 to set + */ + public void setUnknown05(int unknown05) { + this.unknown05 = unknown05; + } + + /** + * @return the unknown06 + */ + public int getUnknown06() { + return unknown06; + } + + /** + * @param unknown06 + * the unknown06 to set + */ + public void setUnknown06(int unknown06) { + this.unknown06 = unknown06; + } + +} diff --git a/src/engine/net/client/msg/chat/ChatPvPMsg.java b/src/engine/net/client/msg/chat/ChatPvPMsg.java new file mode 100644 index 00000000..dd93f139 --- /dev/null +++ b/src/engine/net/client/msg/chat/ChatPvPMsg.java @@ -0,0 +1,87 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.chat; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractWorldObject; + +public class ChatPvPMsg extends AbstractChatMsg { + + protected int unknown03; + + /** + * This is the general purpose constructor. + */ + public ChatPvPMsg(AbstractWorldObject source, String message) { + super(Protocol.CHATPVP, source, message); + this.unknown03 = 0; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ChatPvPMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CHATPVP, origin, reader); + } + + /** + * Copy constructor + */ + public ChatPvPMsg(ChatPvPMsg msg) { + super(msg); + this.unknown03 = msg.unknown03; + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + this.unknown03 = reader.getInt(); + this.message = reader.getString(); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(0); // Probably Type + writer.putInt(0); // Probably objectUUID + writer.putInt(0); + + writer.putString(this.message); + } + + /** + * @return the unknown03 + */ + public int getUnknown03() { + return unknown03; + } + + /** + * @param unknown03 + * the unknown03 to set + */ + public void setUnknown03(int unknown03) { + this.unknown03 = unknown03; + } + +} diff --git a/src/engine/net/client/msg/chat/ChatSayMsg.java b/src/engine/net/client/msg/chat/ChatSayMsg.java new file mode 100644 index 00000000..c8137cb8 --- /dev/null +++ b/src/engine/net/client/msg/chat/ChatSayMsg.java @@ -0,0 +1,91 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.chat; + +import engine.Enum.GameObjectType; +import engine.gameManager.SessionManager; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractGameObject; +import engine.objects.AbstractWorldObject; +import engine.server.MBServerStatics; + +public class ChatSayMsg extends AbstractChatMsg { + + /** + * This is the general purpose constructor. + */ + public ChatSayMsg(AbstractWorldObject source, String message) { + super(Protocol.CHATSAY, source, message); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ChatSayMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CHATSAY, origin, reader); + } + + /** + * Copy constructor + */ + public ChatSayMsg(ChatSayMsg msg) { + super(msg); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + + long sourceID = reader.getLong(); + this.unknown01 = reader.getInt(); + + int objectUUID = AbstractGameObject.extractUUID(GameObjectType.PlayerCharacter, sourceID); + this.source = SessionManager.getPlayerCharacterByID(objectUUID); + + // this.unknown01 = reader.getInt(); //not needed? + this.message = reader.getString(); + + this.sourceName = reader.getString(); + this.unknown02 = reader.getInt(); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + if (this.source != null){ + writer.putInt(this.source.getObjectType().ordinal()); + writer.putInt(source.getObjectUUID()); + } + else + writer.putLong(0L); + writer.putInt(0); + writer.putString(this.message); + if (this.source == null) { + // TODO log error here + writer.putString(""); + writer.putInt(0); + } else { + writer.putString(((AbstractCharacter) this.source).getFirstName()); + writer.putInt(MBServerStatics.worldMapID); + } + } +} diff --git a/src/engine/net/client/msg/chat/ChatShoutMsg.java b/src/engine/net/client/msg/chat/ChatShoutMsg.java new file mode 100644 index 00000000..91442752 --- /dev/null +++ b/src/engine/net/client/msg/chat/ChatShoutMsg.java @@ -0,0 +1,80 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.chat; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.server.MBServerStatics; + +public class ChatShoutMsg extends AbstractChatMsg { + + /** + * This is the general purpose constructor. + */ + public ChatShoutMsg(AbstractWorldObject source, String message) { + super(Protocol.CHATSHOUT, source, message); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ChatShoutMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CHATSHOUT, origin, reader); + } + + /** + * Copy constructor + */ + public ChatShoutMsg(ChatShoutMsg msg) { + super(msg); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.unknown01 = reader.getInt(); + + this.message = reader.getString(); + this.sourceName = reader.getString(); + + this.unknown02 = reader.getInt(); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.putInt(this.unknown01); + writer.putString(this.message); + if (this.source == null) { + // TODO log error here + writer.putString(""); + writer.putInt(0); + } else { + writer.putString(((AbstractCharacter) this.source).getFirstName()); + writer.putInt(MBServerStatics.worldMapID); + } + } +} diff --git a/src/engine/net/client/msg/chat/ChatSystemChannelMsg.java b/src/engine/net/client/msg/chat/ChatSystemChannelMsg.java new file mode 100644 index 00000000..33c96e7d --- /dev/null +++ b/src/engine/net/client/msg/chat/ChatSystemChannelMsg.java @@ -0,0 +1,162 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.chat; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractWorldObject; + +public class ChatSystemChannelMsg extends AbstractChatMsg { + + // TODO enum this? + + // messageType + // 1 = Error + // 2 = Info + // 3 = Message of the Day + protected int messageType; + + protected int unknown03; + protected int unknown04; + + protected int channel; + + /** + * This is the general purpose constructor. + */ + public ChatSystemChannelMsg(AbstractWorldObject source, String message, int messageType) { + super(Protocol.SYSTEMCHANNEL, source, message); + this.channel = 0; + this.messageType = messageType; + this.unknown03 = 0; + this.unknown04 = 0; + } + + /** + * Copy constructor + */ + public ChatSystemChannelMsg(ChatSystemChannelMsg msg) { + super(msg); + this.messageType = msg.messageType; + this.unknown03 = msg.unknown03; + this.unknown04 = msg.unknown04; + this.channel = msg.channel; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ChatSystemChannelMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.SYSTEMCHANNEL, origin, reader); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.channel = reader.getInt(); + this.messageType = reader.getInt(); + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.message = reader.getString(); + this.unknown03 = reader.getInt(); + this.unknown04 = reader.getInt(); // seems to alternate beween 0x0 and + // 0x40C30000 + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.channel); + writer.putInt(this.messageType); + if (this.source != null) + ;// writer.putLong(this.source.getCompositeID()); + else + // writer.putLong(0L); + writer.putString(this.message); + writer.putInt(this.unknown03); + writer.putInt(this.unknown04); + + /* + * for (String key : this.vars.keySet()) { writer.putString(key); + * writer.putString(this.vars.get(key)); } + */ + } + + /** + * @return the channel + */ + public int getChannel() { + return channel; + } + + /** + * @param channel + * the channel to set + */ + public void setChannel(int channel) { + this.channel = channel; + } + + /** + * @return the messageType + */ + public int getMessageType() { + return messageType; + } + + /** + * @param messageType + * the messageType to set + */ + public void setMessageType(int messageType) { + this.messageType = messageType; + } + + /** + * @return the unknown03 + */ + public int getUnknown03() { + return unknown03; + } + + /** + * @param unknown03 + * the unknown03 to set + */ + public void setUnknown03(int unknown03) { + this.unknown03 = unknown03; + } + + /** + * @return the unknown04 + */ + public int getUnknown04() { + return unknown04; + } + + /** + * @param unknown04 + * the unknown04 to set + */ + public void setUnknown04(int unknown04) { + this.unknown04 = unknown04; + } + +} diff --git a/src/engine/net/client/msg/chat/ChatSystemMsg.java b/src/engine/net/client/msg/chat/ChatSystemMsg.java new file mode 100644 index 00000000..e6e61968 --- /dev/null +++ b/src/engine/net/client/msg/chat/ChatSystemMsg.java @@ -0,0 +1,203 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.chat; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractWorldObject; +import engine.server.MBServerStatics; + +import java.util.concurrent.ConcurrentHashMap; + + +public class ChatSystemMsg extends AbstractChatMsg { + + // TODO enum this? + + // channel + // 1 = System + // 2 = General Announcement (Flashing at top of screen!) + // 3 = Commander + // 5 = Nation + // 6 = Leader + // 7 = Shout + // 8 = Siege + // 9 = Territory + // 10 = Info + // 12 = Guild + // 13 = Inner Council + // 14 = Group + // 15 = City + // 16 = say + // 17 = Emote + // 19 = tell + protected int channel; + + // messageType + // 1 = Error + // 2 = Info + // 3 = Message of the Day + protected int messageType; + + protected int unknown03; + protected int numVariables; + // TODO this doesn't need to be a global value, + // its inherit to the HashMap + protected ConcurrentHashMap vars; + + // TODO make a list of variables + /** + * This is the general purpose constructor. + */ + public ChatSystemMsg(AbstractWorldObject source, String message) { + super(Protocol.SYSTEMBROADCASTCHANNEL, source, message); + this.messageType = 2; + this.unknown03 = 0; + this.numVariables = 0; + vars = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ChatSystemMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.SYSTEMBROADCASTCHANNEL, origin, reader); + } + + /** + * Copy constructor + */ + public ChatSystemMsg(ChatSystemMsg msg) { + super(msg); + this.channel = msg.channel; + this.messageType = msg.messageType; + this.unknown03 = msg.unknown03; + this.numVariables = msg.numVariables; + this.vars = msg.vars; + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.vars = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + + this.channel = reader.getInt(); + this.messageType = reader.getInt(); + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + + this.message = reader.getString(); + this.unknown03 = reader.getInt(); + this.numVariables = reader.getInt(); + + for (int i = 0; i < this.numVariables; ++i) { + String key = reader.getString(); + String value = reader.getString(); + reader.get(); + this.vars.put(key, value); + } + + } + + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.channel); + writer.putInt(this.messageType); + if (this.source != null){ + writer.putInt(source.getObjectType().ordinal()); + writer.putInt(source.getObjectUUID()); + } + else + writer.putLong(0L); + writer.putString(this.message); + writer.putInt(this.unknown03); + writer.putInt(this.numVariables); + + for (String key : this.vars.keySet()) { + writer.putString(key); + writer.putString(this.vars.get(key)); + } + } + + /** + * @return the channel + */ + public int getChannel() { + return channel; + } + + /** + * @param channel + * the channel to set + */ + public void setChannel(int channel) { + this.channel = channel; + } + + /** + * @return the messageType + */ + public int getMessageType() { + return messageType; + } + + /** + * @param messageType + * the messageType to set + */ + public void setMessageType(int messageType) { + this.messageType = messageType; + } + + /** + * @return the unknown03 + */ + public int getUnknown03() { + return unknown03; + } + + /** + * @param unknown03 + * the unknown03 to set + */ + public void setUnknown03(int unknown03) { + this.unknown03 = unknown03; + } + + /** + * @return the numVariables + */ + public int getNumVariables() { + return numVariables; + } + + + + /** + * @return the vars + */ + public ConcurrentHashMap getVars() { + return vars; + } + + +} diff --git a/src/engine/net/client/msg/chat/ChatTellMsg.java b/src/engine/net/client/msg/chat/ChatTellMsg.java new file mode 100644 index 00000000..43d98098 --- /dev/null +++ b/src/engine/net/client/msg/chat/ChatTellMsg.java @@ -0,0 +1,171 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.chat; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.server.MBServerStatics; + +public class ChatTellMsg extends AbstractChatMsg { + + protected AbstractWorldObject target; + protected int targetType; + protected int targetID; + protected String targetName; + protected int unknown03; + + /** + * This is the general purpose constructor. + */ + public ChatTellMsg(AbstractWorldObject source, AbstractWorldObject target, String message) { + super(Protocol.CHATTELL, source, message); + this.target = target; + + // TODO could this be simplified? Check serializer; + if (this.target != null) { + this.targetType = target.getObjectType().ordinal(); + this.targetID = target.getObjectUUID(); + this.targetName = target.getName(); + } + this.unknown03 = 0; + } + + /** + * Copy constructor + */ + public ChatTellMsg(ChatTellMsg msg) { + super(msg); + this.target = msg.target; + this.targetType = msg.targetType; + this.targetID = msg.targetID; + this.targetName = msg.targetName; + this.unknown03 = msg.unknown03; + } + + public int getTargetType() { + return targetType; + } + + public int getTargetID() { + return targetID; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ChatTellMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CHATTELL, origin, reader); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.unknown01 = reader.getInt(); + this.message = reader.getString(); + this.sourceName = reader.getString(); // sourceName + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + this.targetName = reader.getString(); + this.unknown02 = reader.getInt(); + this.unknown03 = reader.getInt(); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.source.getObjectType().ordinal()); + writer.putInt(source.getObjectUUID()); + writer.putInt(this.unknown01); + writer.putString(this.message); + if (AbstractWorldObject.IsAbstractCharacter(source)) { + writer.putString(((AbstractCharacter) this.source).getFirstName()); + } else { + writer.putString(this.source.getName()); + } + if (this.target != null) { + writer.putInt(this.target.getObjectType().ordinal()); + writer.putInt(this.target.getObjectUUID()); + if (AbstractWorldObject.IsAbstractCharacter(target)) { + writer.putString(((AbstractCharacter) this.target).getFirstName()); + } else { + writer.putString(this.target.getName()); + } + } else { + writer.putInt(this.targetType); + writer.putInt(this.targetID); + writer.putString(this.targetName); + } + writer.putInt(MBServerStatics.worldMapID); + writer.putInt(MBServerStatics.worldMapID); + } + + /** + * @return the target + */ + public AbstractWorldObject getTarget() { + return target; + } + + /** + * @param target + * the target to set + */ + public void setTarget(AbstractWorldObject target) { + this.target = target; + } + + + + + /** + * @return the targetName + */ + public String getTargetName() { + return targetName; + } + + /** + * @param targetName + * the targetName to set + */ + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + /** + * @return the unknown03 + */ + public int getUnknown03() { + return unknown03; + } + + /** + * @param unknown03 + * the unknown03 to set + */ + public void setUnknown03(int unknown03) { + this.unknown03 = unknown03; + } + +} diff --git a/src/engine/net/client/msg/chat/GuildEnterWorldMsg.java b/src/engine/net/client/msg/chat/GuildEnterWorldMsg.java new file mode 100644 index 00000000..ef3e3ed7 --- /dev/null +++ b/src/engine/net/client/msg/chat/GuildEnterWorldMsg.java @@ -0,0 +1,246 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.chat; + +import engine.Enum.GameObjectType; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.objects.PlayerCharacter; + +public class GuildEnterWorldMsg extends AbstractChatMsg { + + protected String name; + protected int guildTitle; + protected int charterType; + protected int unknown04; + protected int guildUUID; + protected int unknown05; + protected int unknown06; + + /** + * This is the general purpose constructor. + */ + public GuildEnterWorldMsg(PlayerCharacter source) { + super(Protocol.GUILDMEMBERONLINE, source, "[!PLAYERRANK!] !PLAYERNAME! is online"); + + this.name = ""; + this.guildTitle = 0; + this.charterType = 9; + this.unknown02 = 12; + this.unknown04 = 2; + this.guildUUID = 0; + this.unknown05 = 0x2E46D00C; + this.unknown06 = 0; + } + + /** + * Copy constructor + */ + public GuildEnterWorldMsg(GuildEnterWorldMsg msg) { + super(msg); + this.name = msg.name; + this.guildTitle = msg.guildTitle; + this.charterType = msg.charterType; + this.unknown04 = msg.unknown04; + this.guildUUID = msg.guildUUID; + this.unknown05 = msg.unknown05; + this.unknown06 = msg.unknown06; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public GuildEnterWorldMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.GUILDMEMBERONLINE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putString(this.name); + + writer.putInt(guildTitle); + writer.put((byte) 1); // forgot this + writer.putInt(this.charterType); + writer.putInt(this.unknown02); + writer.putInt(this.unknown04); + writer.putInt(GameObjectType.Guild.ordinal()); + writer.putInt(this.guildUUID); + + writer.putString(this.message); + writer.putInt(this.unknown05); + writer.putInt(this.unknown06); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.name = reader.getString(); + this.guildTitle = reader.getInt(); + reader.get(); // forgot this + this.unknown02 = reader.getInt(); + this.charterType = reader.getInt(); + this.unknown04 = reader.getInt(); + reader.getInt(); // Object Type Padding + this.guildUUID = reader.getInt(); + this.message = reader.getString(); + this.unknown05 = reader.getInt(); + this.unknown06 = reader.getInt(); + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name + * the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the guildTitle + */ + public int getGuildTitle() { + return guildTitle; + } + + /** + * @param guildTitle + * the guildTitle to set + */ + public void setGuildTitle(int guildTitle) { + this.guildTitle = guildTitle; + } + + /** + * @return the unknown02 + */ + @Override + public int getUnknown02() { + return unknown02; + } + + /** + * @param unknown02 + * the unknown02 to set + */ + @Override + public void setUnknown02(int unknown02) { + this.unknown02 = unknown02; + } + + /** + * @return the unknown03 + */ + public int getCharter() { + return charterType; + } + + /** + * @param unknown03 + * the unknown03 to set + */ + public void setCharter(int charter) { + this.charterType = charter; + } + + /** + * @return the unknown04 + */ + public int getUnknown04() { + return unknown04; + } + + /** + * @param unknown04 + * the unknown04 to set + */ + public void setUnknown04(int unknown04) { + this.unknown04 = unknown04; + } + + /** + * @return the guildUUID + */ + public int getGuildUUID() { + return guildUUID; + } + + /** + * @param guildUUID + * the guildUUID to set + */ + public void setGuildUUID(int guildUUID) { + this.guildUUID = guildUUID; + } + + /** + * @return the message + */ + @Override + public String getMessage() { + return message; + } + + /** + * @param message + * the message to set + */ + @Override + public void setMessage(String message) { + this.message = message; + } + + /** + * @return the unknown05 + */ + public int getUnknown05() { + return unknown05; + } + + /** + * @param unknown05 + * the unknown05 to set + */ + public void setUnknown05(int unknown05) { + this.unknown05 = unknown05; + } + + /** + * @return the unknown06 + */ + public int getUnknown06() { + return unknown06; + } + + /** + * @param unknown06 + * the unknown06 to set + */ + public void setUnknown06(int unknown06) { + this.unknown06 = unknown06; + } + +} diff --git a/src/engine/net/client/msg/commands/ClientAdminCommandMsg.java b/src/engine/net/client/msg/commands/ClientAdminCommandMsg.java new file mode 100644 index 00000000..5335f660 --- /dev/null +++ b/src/engine/net/client/msg/commands/ClientAdminCommandMsg.java @@ -0,0 +1,101 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.commands; + +import engine.Enum.GameObjectType; +import engine.gameManager.DbManager; +import engine.math.Vector3f; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; +import engine.objects.AbstractGameObject; + +public class ClientAdminCommandMsg extends ClientNetMsg { + + private int msgType; + private String msgCommand; + private long senderCompID; + private int targetType; + private int targetUUID; + private Vector3f location; + private AbstractGameObject target; + + /** + * This is the general purpose constructor. + */ + public ClientAdminCommandMsg() { + super(Protocol.CLIENTADMINCOMMAND); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ClientAdminCommandMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CLIENTADMINCOMMAND, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + // TODO implement Serialize() + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.msgType = reader.getInt(); + this.msgCommand = reader.getString(); + this.targetType = reader.getInt(); + this.targetUUID = reader.getInt(); + this.senderCompID = reader.getLong(); // always null?? + this.location = new Vector3f(reader.getFloat(), reader.getFloat(), reader.getFloat()); + + if (targetUUID != 0) + target = DbManager.getObject(GameObjectType.values()[this.targetType], this.targetUUID); + } + + /** + * @return the msgCommand + */ + public String getMsgCommand() { + return msgCommand; + } + + /** + * @return the targetUUID + */ + public int getTargetUUID() { + return targetUUID; + } + + /** + * @return the location + */ + public Vector3f getLocation() { + return location; + } + + /** + * @return the target + */ + public AbstractGameObject getTarget() { + return target; + } + +} diff --git a/src/engine/net/client/msg/group/AppointGroupLeaderMsg.java b/src/engine/net/client/msg/group/AppointGroupLeaderMsg.java new file mode 100644 index 00000000..636c34d4 --- /dev/null +++ b/src/engine/net/client/msg/group/AppointGroupLeaderMsg.java @@ -0,0 +1,129 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.group; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class AppointGroupLeaderMsg extends ClientNetMsg { + + private int targetType; + private int targetID; + private int response; + private String message; + + /** + * This is the general purpose constructor. + */ + public AppointGroupLeaderMsg() { + super(Protocol.GROUPLEADERAPPOINT); + this.message = "You are already the group leader"; + + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public AppointGroupLeaderMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.GROUPLEADERAPPOINT, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.targetType); + writer.putInt(this.targetID); + writer.putInt(this.response); + if (this.response == 1) + writer.putString(this.message); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + this.response = reader.getInt(); + if (this.response == 1) + this.message = reader.getString(); + } + + /** + * @return the targetType + */ + public int getTargetType() { + return targetType; + } + + /** + * @param targetType + * the targetType to set + */ + public void setTargetType(int targetType) { + this.targetType = targetType; + } + + /** + * @return the targetID + */ + public int getTargetID() { + return targetID; + } + + /** + * @param targetID + * the targetID to set + */ + public void setTargetID(int targetID) { + this.targetID = targetID; + } + + /** + * @return the response + */ + public int getResponse() { + return response; + } + + /** + * @param response + * the response to set + */ + public void setResponse(int response) { + this.response = response; + } + + /** + * @return the message + */ + public String getMessage() { + return message; + } + + /** + * @param message + * the message to set + */ + public void setMessage(String message) { + this.message = message; + } + +} diff --git a/src/engine/net/client/msg/group/DisbandGroupMsg.java b/src/engine/net/client/msg/group/DisbandGroupMsg.java new file mode 100644 index 00000000..d542420b --- /dev/null +++ b/src/engine/net/client/msg/group/DisbandGroupMsg.java @@ -0,0 +1,69 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.group; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class DisbandGroupMsg extends ClientNetMsg { + + private int unknown01; + + /** + * This is the general purpose constructor. + */ + public DisbandGroupMsg() { + super(Protocol.GROUPDISBAND); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public DisbandGroupMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.GROUPDISBAND, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.unknown01); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.unknown01 = reader.getInt(); + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + +} diff --git a/src/engine/net/client/msg/group/FormationFollowMsg.java b/src/engine/net/client/msg/group/FormationFollowMsg.java new file mode 100644 index 00000000..c57401ca --- /dev/null +++ b/src/engine/net/client/msg/group/FormationFollowMsg.java @@ -0,0 +1,112 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.group; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class FormationFollowMsg extends ClientNetMsg { + + private boolean follow; + private int formation; + private int unknown01; + + /** + * This is the general purpose constructor. + */ + public FormationFollowMsg() { + super(Protocol.GROUPFOLLOW); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public FormationFollowMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.GROUPFOLLOW, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + if (this.follow) { + writer.putInt(1); + writer.putInt(0); + } else { + writer.putInt(0); + writer.putInt(1); + } + writer.putInt(this.formation); + writer.putInt(this.unknown01); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.follow = reader.getInt() == 1; + reader.getInt(); + this.formation = reader.getInt(); + this.unknown01 = reader.getInt(); + } + + /** + * @return the follow + */ + public boolean isFollow() { + return follow; + } + + /** + * @param follow + * the follow to set + */ + public void setFollow(boolean follow) { + this.follow = follow; + } + + /** + * @return the formation + */ + public int getFormation() { + return formation; + } + + /** + * @param formation + * the formation to set + */ + public void setFormation(int formation) { + this.formation = formation; + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + +} diff --git a/src/engine/net/client/msg/group/GroupInviteMsg.java b/src/engine/net/client/msg/group/GroupInviteMsg.java new file mode 100644 index 00000000..c7e98ed5 --- /dev/null +++ b/src/engine/net/client/msg/group/GroupInviteMsg.java @@ -0,0 +1,220 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.group; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class GroupInviteMsg extends ClientNetMsg { + + private int sourceType; + private int sourceID; + private int targetType; + private int targetID; + private int groupType; + private int groupID; + private int unknown01; + private int invited; + private String name; + + /** + * This is the general purpose constructor. + */ + public GroupInviteMsg() { + super(Protocol.INVITEGROUP); + this.name = ""; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public GroupInviteMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.INVITEGROUP, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.sourceType); + writer.putInt(this.sourceID); + writer.putInt(this.targetType); + writer.putInt(this.targetID); + writer.putInt(this.groupType); + writer.putInt(this.groupID); + writer.putInt(this.unknown01); + writer.putInt(this.invited); + if (this.invited == 1) + writer.putString(this.name); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + + this.sourceType = reader.getInt(); + this.sourceID = reader.getInt(); + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + this.groupType = reader.getInt(); + this.groupID = reader.getInt(); + this.unknown01 = reader.getInt(); + this.invited = reader.getInt(); + if (this.invited == 1) + this.name = reader.getString(); + + } + + /** + * @return the sourceType + */ + public int getSourceType() { + return sourceType; + } + + /** + * @param sourceType + * the sourceType to set + */ + public void setSourceType(int sourceType) { + this.sourceType = sourceType; + } + + /** + * @return the sourceID + */ + public int getSourceID() { + return sourceID; + } + + /** + * @param sourceID + * the sourceID to set + */ + public void setSourceID(int sourceID) { + this.sourceID = sourceID; + } + + /** + * @return the targetType + */ + public int getTargetType() { + return targetType; + } + + /** + * @param targetType + * the targetType to set + */ + public void setTargetType(int targetType) { + this.targetType = targetType; + } + + /** + * @return the targetID + */ + public int getTargetID() { + return targetID; + } + + /** + * @param targetID + * the targetID to set + */ + public void setTargetID(int targetID) { + this.targetID = targetID; + } + + /** + * @return the groupType + */ + public int getGroupType() { + return groupType; + } + + /** + * @param groupType + * the groupType to set + */ + public void setGroupType(int groupType) { + this.groupType = groupType; + } + + /** + * @return the groupID + */ + public int getGroupID() { + return groupID; + } + + /** + * @param groupID + * the groupID to set + */ + public void setGroupID(int groupID) { + this.groupID = groupID; + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the invited + */ + public int getInvited() { + return invited; + } + + /** + * @param invited + * the invited to set + */ + public void setInvited(int invited) { + this.invited = invited; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name + * the name to set + */ + public void setName(String name) { + this.name = name; + } + +} diff --git a/src/engine/net/client/msg/group/GroupInviteResponseMsg.java b/src/engine/net/client/msg/group/GroupInviteResponseMsg.java new file mode 100644 index 00000000..c988f43e --- /dev/null +++ b/src/engine/net/client/msg/group/GroupInviteResponseMsg.java @@ -0,0 +1,123 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.group; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class GroupInviteResponseMsg extends ClientNetMsg { + + private int groupType; + private int groupID; + private int unknown01; + private int unknown02; + + /** + * This is the general purpose constructor. + */ + public GroupInviteResponseMsg() { + super(Protocol.JOINGROUP); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public GroupInviteResponseMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.JOINGROUP, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.groupType); + writer.putInt(this.groupID); + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.groupType = reader.getInt(); + this.groupID = reader.getInt(); + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + } + + /** + * @return the groupType + */ + public int getGroupType() { + return groupType; + } + + /** + * @param groupType + * the groupType to set + */ + public void setGroupType(int groupType) { + this.groupType = groupType; + } + + /** + * @return the groupID + */ + public int getGroupID() { + return groupID; + } + + /** + * @param groupID + * the groupID to set + */ + public void setGroupID(int groupID) { + this.groupID = groupID; + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the unknown02 + */ + public int getUnknown02() { + return unknown02; + } + + /** + * @param unknown02 + * the unknown02 to set + */ + public void setUnknown02(int unknown02) { + this.unknown02 = unknown02; + } + +} diff --git a/src/engine/net/client/msg/group/GroupUpdateMsg.java b/src/engine/net/client/msg/group/GroupUpdateMsg.java new file mode 100644 index 00000000..e4b554bc --- /dev/null +++ b/src/engine/net/client/msg/group/GroupUpdateMsg.java @@ -0,0 +1,352 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.group; + +import engine.Enum.GameObjectType; +import engine.gameManager.GroupManager; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; +import engine.objects.Group; +import engine.objects.PlayerCharacter; + +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +//See GroupUpdateMsgBreakdown.txt is SBData directory. +public class GroupUpdateMsg extends ClientNetMsg { + + private int messageType; + private int unknown02; + private int playerUUID; + private Set players; + private Group group; + + /** + * This is the general purpose constructor. + */ + public GroupUpdateMsg() { + super(Protocol.UPDATEGROUP); + this.messageType = 1; + this.unknown02 = 1; + this.playerUUID = 0; + this.players = Collections.newSetFromMap(new ConcurrentHashMap<>()); + } + + public GroupUpdateMsg(int messageType, int unknown02, Set players, Group group) { + super(Protocol.UPDATEGROUP); + this.messageType = messageType; + this.unknown02 = unknown02; + this.playerUUID = 0; + this.players = players; + this.group = group; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public GroupUpdateMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.UPDATEGROUP, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(GameObjectType.Group.ordinal()); + writer.putInt(this.group.getObjectUUID()); + writer.putInt(this.messageType); + + // 5 breaks everything including movement etc + // 4 sends a party dissolved message + // 3 closes the group window and leaves the group + // 2 seems to update the location but not the stats correctly upon coming back into range + // 1 seems to add you to the group but if called by a job tops up your stats on the client and desyncs it + + switch (messageType) { + case 4: + GroupUpdateMsg._serializeFour(writer); + break; + case 5: + this._serializeFive(writer); + break; + case 6: + this._serializeSix(writer); + break; + case 7: + this._serializeSeven(writer); + break; + case 8: + this._serializeEight(writer); + break; + default: + writer.putInt(this.unknown02); + // Send player data + writer.putInt(this.players.size()); + int i = 0; + for (PlayerCharacter pc : this.players) { + this.serializePlayer(writer, pc, this.messageType, i++); + } + writer.putInt(0); // end data + break; + } + } + + // *** Refactor: This method is an abortion. Needs to be re-written from scratch. + + private void serializePlayer(ByteBufferWriter writer, PlayerCharacter player, int messageType, int count) { + + if (messageType == 1) { + writer.putString((player != null) ? player.getFirstName() : "nullError"); + writer.putString((player != null) ? player.getLastName() : ""); + } else if (messageType == 2) { + if (count == 0) { + writer.putString((player != null) ? player.getFirstName() : "nullError"); + writer.putString((player != null) ? player.getLastName() : ""); + } else { + writer.putInt(0); + } + } else if (messageType == 3) { + writer.putString(" "); + writer.putString(" "); + } else { + writer.putInt(0); + writer.putInt(0); + } + + if (messageType == 3) { + for (int i = 0; i < 6; i++) { + writer.putInt(0); + } + } else { + // mana health stam % + writer.putInt((int) (player.getHealth() / player.getHealthMax() * 100)); // should be health but does nothing + writer.putInt((int) (player.getStamina() / player.getStaminaMax() * 100)); // stam % + writer.putInt((int) (player.getMana() / player.getManaMax() * 100)); // mana % + writer.putInt((player != null) ? Float.floatToIntBits(player.getLoc().getX()) : 0); + writer.putInt((player != null) ? Float.floatToIntBits(player.getLoc().getY()) : 0); + writer.putInt((player != null) ? Float.floatToIntBits(player.getLoc().getZ()) : 0); + } + + if (player == null) + writer.putLong(0); + else { + writer.putInt(GameObjectType.PlayerCharacter.ordinal()); + writer.putInt(player.getObjectUUID()); + } + + if (messageType == 3) { + writer.putInt(0); + writer.putInt(-1); + writer.putInt(0); + return; + } else if (messageType == 5) { + writer.putInt(0); + writer.putInt(0); + return; + } + if (group != null && player != null) { + writer.putInt((this.group.getGroupLead() == player) ? 2 : 1); + } else { + writer.putInt(1); + } + if (messageType == 2) { + writer.putInt(-1); + writer.putInt(0); + return; + } + writer.putInt(1); + writer.putInt(1); + writer.put((byte) 1); + + // if sending message type 1 this seems to make the group window flicker the button + // i think getfollow and split gold might be the wrong way around + if (group != null) { + writer.put(this.group.getSplitGold() ? (byte) 1 : (byte) 0); + } else { + writer.put((byte) 0); + } + + // always gets reset on a message type 1 + if (player != null) { + writer.put(player.getFollow() ? (byte) 1 : (byte) 0); + } else { + writer.put((byte) 0); + } + } + + private static void _serializeFour(ByteBufferWriter writer) { + + // 4 sends a party dissolved window + for (int i = 0; i < 3; i++) { + writer.putInt(0); + } + } + + //sync player's stats and position + private void _serializeFive(ByteBufferWriter writer) { + writer.putInt(1); + writer.putInt(players.size() - 1); + for (PlayerCharacter player : players) { + if (player.getObjectUUID() == this.playerUUID) { + continue; //skip self + } + writer.putInt((int) (player.getHealth() / player.getHealthMax() * 100)); + writer.putInt((int) (player.getStamina() / player.getStaminaMax() * 100)); + writer.putInt((int) (player.getMana() / player.getManaMax() * 100)); + writer.putFloat(player.getLoc().x); + writer.putFloat(player.getLoc().y); + writer.putFloat(player.getLoc().z); + writer.putInt(GameObjectType.PlayerCharacter.ordinal()); + writer.putInt(player.getObjectUUID()); + + } + writer.putInt(0); + writer.putInt(0); + } + + private void _serializeSix(ByteBufferWriter writer) { + writer.putInt(0); + if (this.group != null) { + writer.put(this.group.getSplitGold() ? (byte) 1 : (byte) 0); + } else { + writer.put((byte) 0); + } + writer.putInt(0); + writer.putInt(0); + } + + private void _serializeSeven(ByteBufferWriter writer) { + PlayerCharacter player = this.players.iterator().next(); + writer.putInt(0); + if (player != null) { + writer.put(player.getFollow() ? (byte) 1 : (byte) 0); + } else { + writer.put((byte) 0); + } + writer.putInt(0); + writer.putInt(0); + } + + private void _serializeEight(ByteBufferWriter writer) { + PlayerCharacter player = this.players.iterator().next();; + writer.putInt(0); + if (player != null) { + writer.put(player.getFollow() ? (byte) 1 : (byte) 0); + writer.putInt(GameObjectType.PlayerCharacter.ordinal()); + writer.putInt(player.getObjectUUID()); + } else { + writer.put((byte) 0); + writer.putLong(0L); + } + writer.putInt(0); + writer.putInt(0); + } + + /** + * Deserializes the subclass specific items from the supplied + * ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.players = Collections.newSetFromMap(new ConcurrentHashMap<>()); + + reader.getInt(); + this.group = GroupManager.getGroup(reader.getInt()); + // TODO figure this mess out + } + + public void addPlayer(PlayerCharacter value) { + this.players.add(value); + } + + public void setPlayer(PlayerCharacter value) { + this.players.clear(); + this.players.add(value); + } + + /** + * @return the messageType + */ + public int getMessageType() { + return messageType; + } + + /** + * @param messageType the messageType to set + */ + public void setMessageType(int messageType) { + this.messageType = messageType; + } + + /** + * @return the unknown02 + */ + public int getUnknown02() { + return unknown02; + } + + /** + * @param unknown02 the unknown02 to set + */ + public void setUnknown02(int unknown02) { + this.unknown02 = unknown02; + } + + /** + * @return the playerUUID + */ + public long getPlayerID() { + return playerUUID; + } + + /** + * @param playerUUID the playerUUID to set + */ + public void setPlayerUUID(int playerUUID) { + this.playerUUID = playerUUID; + } + + /** + * @return the players + */ + public Set getPlayers() { + return players; + } + + /** + * @param players the players to set + */ + public void setPlayers(Set players) { + this.players = players; + } + + /** + * @return the group + */ + public Group getGroup() { + return group; + } + + /** + * @param group the group to set + */ + public void setGroup(Group group) { + this.group = group; + } + +} diff --git a/src/engine/net/client/msg/group/LeaveGroupMsg.java b/src/engine/net/client/msg/group/LeaveGroupMsg.java new file mode 100644 index 00000000..8c3f1678 --- /dev/null +++ b/src/engine/net/client/msg/group/LeaveGroupMsg.java @@ -0,0 +1,122 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.group; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class LeaveGroupMsg extends ClientNetMsg { + + private int unknown01; + private int unknown02; + private int unknown03; + private int unknown04; + + /** + * This is the general purpose constructor. + */ + public LeaveGroupMsg() { + super(Protocol.LEAVEGROUP); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public LeaveGroupMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.LEAVEGROUP, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + writer.putInt(this.unknown03); + writer.putInt(this.unknown04); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + this.unknown03 = reader.getInt(); + this.unknown04 = reader.getInt(); + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the unknown02 + */ + public int getUnknown02() { + return unknown02; + } + + /** + * @param unknown02 + * the unknown02 to set + */ + public void setUnknown02(int unknown02) { + this.unknown02 = unknown02; + } + + /** + * @return the unknown03 + */ + public int getUnknown03() { + return unknown03; + } + + /** + * @param unknown03 + * the unknown03 to set + */ + public void setUnknown03(int unknown03) { + this.unknown03 = unknown03; + } + + /** + * @return the unknown04 + */ + public int getUnknown04() { + return unknown04; + } + + /** + * @param unknown04 + * the unknown04 to set + */ + public void setUnknown04(int unknown04) { + this.unknown04 = unknown04; + } +} diff --git a/src/engine/net/client/msg/group/RemoveFromGroupMsg.java b/src/engine/net/client/msg/group/RemoveFromGroupMsg.java new file mode 100644 index 00000000..4ee5fb56 --- /dev/null +++ b/src/engine/net/client/msg/group/RemoveFromGroupMsg.java @@ -0,0 +1,129 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.group; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class RemoveFromGroupMsg extends ClientNetMsg { + + private int targetType; + private int targetID; + private int response; + private String message; + + /** + * This is the general purpose constructor. + */ + public RemoveFromGroupMsg() { + super(Protocol.GROUPREMOVE); + + this.message = "Quit if you want to remove yourself"; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public RemoveFromGroupMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.GROUPREMOVE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.targetType); + writer.putInt(this.targetID); + writer.putInt(this.response); + if (this.response == 1) + writer.putString(this.message); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + this.response = reader.getInt(); + if (this.response == 1) + this.message = reader.getString(); + } + + /** + * @return the targetType + */ + public int getTargetType() { + return targetType; + } + + /** + * @param targetType + * the targetType to set + */ + public void setTargetType(int targetType) { + this.targetType = targetType; + } + + /** + * @return the targetID + */ + public int getTargetID() { + return targetID; + } + + /** + * @param targetID + * the targetID to set + */ + public void setTargetID(int targetID) { + this.targetID = targetID; + } + + /** + * @return the response + */ + public int getResponse() { + return response; + } + + /** + * @param response + * the response to set + */ + public void setResponse(int response) { + this.response = response; + } + + /** + * @return the message + */ + public String getMessage() { + return message; + } + + /** + * @param message + * the message to set + */ + public void setMessage(String message) { + this.message = message; + } + +} diff --git a/src/engine/net/client/msg/group/ToggleGroupSplitMsg.java b/src/engine/net/client/msg/group/ToggleGroupSplitMsg.java new file mode 100644 index 00000000..c58aaa77 --- /dev/null +++ b/src/engine/net/client/msg/group/ToggleGroupSplitMsg.java @@ -0,0 +1,72 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.group; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class ToggleGroupSplitMsg extends ClientNetMsg { + + private int unknown01; + + /** + * This is the general purpose constructor. + */ + public ToggleGroupSplitMsg() { + super(Protocol.GROUPTREASURE); + this.unknown01 = 1; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ToggleGroupSplitMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.GROUPTREASURE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.unknown01); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.unknown01 = reader.getInt(); + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + +} diff --git a/src/engine/net/client/msg/guild/AcceptInviteToGuildMsg.java b/src/engine/net/client/msg/guild/AcceptInviteToGuildMsg.java new file mode 100644 index 00000000..f9899e73 --- /dev/null +++ b/src/engine/net/client/msg/guild/AcceptInviteToGuildMsg.java @@ -0,0 +1,110 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.guild; + +import engine.Enum.GameObjectType; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class AcceptInviteToGuildMsg extends ClientNetMsg { + + private int guildUUID; + private int unknown01; + private int unknown02; + + /** + * This is the general purpose constructor. + */ + public AcceptInviteToGuildMsg() { + super(Protocol.JOINGUILD); + } + + public AcceptInviteToGuildMsg(int guildUUID, int unknown01, int unknown02) { + super(Protocol.JOINGUILD); + this.guildUUID = guildUUID; + this.unknown01 = unknown01; + this.unknown02 = unknown02; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public AcceptInviteToGuildMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.JOINGUILD, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(GameObjectType.Guild.ordinal()); + writer.putInt(this.guildUUID); + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + } + + /** + * Deserializes the subclass specific items from the supplied + * ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); // Object Type padding + this.guildUUID = reader.getInt(); + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + } + + /** + * @return the guildUUID + */ + public int getGuildUUID() { + return guildUUID; + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the unknown02 + */ + public int getUnknown02() { + return unknown02; + } + + /** + * @param unknown02 + * the unknown02 to set + */ + public void setUnknown02(int unknown02) { + this.unknown02 = unknown02; + } + +} diff --git a/src/engine/net/client/msg/guild/AcceptSubInviteMsg.java b/src/engine/net/client/msg/guild/AcceptSubInviteMsg.java new file mode 100644 index 00000000..8e35ca6d --- /dev/null +++ b/src/engine/net/client/msg/guild/AcceptSubInviteMsg.java @@ -0,0 +1,154 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.guild; + +import engine.Enum.GameObjectType; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class AcceptSubInviteMsg extends ClientNetMsg { + + private int guildUUID; + private int unknown01; + private int unknown02; + private String response; + + /** + * This is the general purpose constructor. + */ + public AcceptSubInviteMsg() { + super(Protocol.JOINFORPROVINCE); + } + + public AcceptSubInviteMsg(int guildUUID, int unknown01, int unknown02, String response) { + super(Protocol.JOINFORPROVINCE); + this.guildUUID = guildUUID; + this.unknown01 = unknown01; + this.unknown02 = unknown02; + this.response = response; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public AcceptSubInviteMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.JOINFORPROVINCE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(GameObjectType.Guild.ordinal()); + writer.putInt(this.guildUUID); + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + writer.putString(this.response); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); // Object Type Padding + this.guildUUID = reader.getInt(); + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + reader.getString(); + } + + /** + * @return the guildUUID + */ + public int guildUUID() { + return guildUUID; + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the unknown02 + */ + public int getUnknown02() { + return unknown02; + } + + /** + * @param unknown02 + * the unknown02 to set + */ + public void setUnknown02(int unknown02) { + this.unknown02 = unknown02; + } + + + public String getResponse() { + return this.response; + } + + public void setResponse(String value) { + this.response = value; + } +} diff --git a/src/engine/net/client/msg/guild/BanishUnbanishMsg.java b/src/engine/net/client/msg/guild/BanishUnbanishMsg.java new file mode 100644 index 00000000..af8722d3 --- /dev/null +++ b/src/engine/net/client/msg/guild/BanishUnbanishMsg.java @@ -0,0 +1,72 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.guild; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class BanishUnbanishMsg extends ClientNetMsg { + + private int target; + private int msgType; + + /** + * This is the general purpose constructor. + */ + public BanishUnbanishMsg() { + super(Protocol.BANISHMEMBER); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public BanishUnbanishMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.BANISHMEMBER, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); + target = reader.getInt(); + + //Unknows? + reader.getInt(); + this.msgType = reader.getInt(); + reader.getInt(); + } + + public int getTarget() { + return target; + } + + public int getMsgType() { + return msgType; + } + + public void setMsgType(int msgType) { + this.msgType = msgType; + } +} diff --git a/src/engine/net/client/msg/guild/BreakFealtyMsg.java b/src/engine/net/client/msg/guild/BreakFealtyMsg.java new file mode 100644 index 00000000..ae4bac3e --- /dev/null +++ b/src/engine/net/client/msg/guild/BreakFealtyMsg.java @@ -0,0 +1,61 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.net.client.msg.guild; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class BreakFealtyMsg extends ClientNetMsg { + + private int guildUUID; + + /** + * This is the general purpose constructor. + */ + public BreakFealtyMsg() { + super(Protocol.BREAKFEALTY); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public BreakFealtyMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.BREAKFEALTY, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); // Object Type Padding + this.guildUUID = reader.getInt(); + reader.getInt(); + } + + public int getGuildUUID() { + return guildUUID; + } + +} diff --git a/src/engine/net/client/msg/guild/ChangeRankMsg.java b/src/engine/net/client/msg/guild/ChangeRankMsg.java new file mode 100644 index 00000000..05e14a4c --- /dev/null +++ b/src/engine/net/client/msg/guild/ChangeRankMsg.java @@ -0,0 +1,163 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + +package engine.net.client.msg.guild; + +import engine.Enum.GameObjectType; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class ChangeRankMsg extends ClientNetMsg { + + private int playerUUID; + private int previousRank, newRank; + private byte ic, rec, tax; + private String errorMsg; + + /** + * This is the general purpose constructor. + */ + public ChangeRankMsg() { + super(Protocol.GUILDRANKCHANGE); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ChangeRankMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.GUILDRANKCHANGE, origin, reader); + } + + public int getPlayerUUID() { + return playerUUID; + } + + public int getPreviousRank() { + return previousRank; + } + + public void setPreviousRank(int previousRank) { + this.previousRank = previousRank; + } + + public int getNewRank() { + return newRank; + } + + public void setNewRank(int newRank) { + this.newRank = newRank; + } + + public byte getIc() { + return ic; + } + + public void setIc(byte ic) { + this.ic = ic; + } + + public byte getRec() { + return rec; + } + + public void setRec(byte rec) { + this.rec = rec; + } + + public byte getTax() { + return tax; + } + + public void setTax(byte tax) { + this.tax = tax; + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(GameObjectType.PlayerCharacter.ordinal()); + writer.putInt(this.playerUUID); + writer.putInt(this.newRank); + writer.putInt(this.previousRank); + + writer.put(ic); + writer.put(tax); + writer.put(rec); + + writer.putInt(0); + + writer.putString(this.errorMsg); + } + + /** + * Deserializes the subclass specific items from the supplied + * ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); // Object Type Padding + this.playerUUID = reader.getInt(); + this.newRank = reader.getInt(); + this.previousRank = reader.getInt(); + this.ic = reader.get(); + this.tax = reader.get(); + this.rec = reader.get(); + + reader.getInt(); //Pad + + this.errorMsg = reader.getString(); + } +} diff --git a/src/engine/net/client/msg/guild/DisbandGuildMsg.java b/src/engine/net/client/msg/guild/DisbandGuildMsg.java new file mode 100644 index 00000000..0ed97a3b --- /dev/null +++ b/src/engine/net/client/msg/guild/DisbandGuildMsg.java @@ -0,0 +1,58 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.guild; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class DisbandGuildMsg extends ClientNetMsg { + + private int response; + private String message; + + /** + * This is the general purpose constructor. + */ + public DisbandGuildMsg() { + super(Protocol.DISBANDGUILD); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public DisbandGuildMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.DISBANDGUILD, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.response); + writer.putString(this.message); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.response = reader.getInt(); + this.message = reader.getString(); + //this.sourceID = reader.getInt(); + } + +} diff --git a/src/engine/net/client/msg/guild/DismissGuildMsg.java b/src/engine/net/client/msg/guild/DismissGuildMsg.java new file mode 100644 index 00000000..1e0559ea --- /dev/null +++ b/src/engine/net/client/msg/guild/DismissGuildMsg.java @@ -0,0 +1,78 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.guild; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class DismissGuildMsg extends ClientNetMsg { + + private int unknown01; + private int guildType; + private int guildID; + + /** + * This is the general purpose constructor. + */ + public DismissGuildMsg() { + super(Protocol.DISMISSGUILD); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public DismissGuildMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.DISMISSGUILD, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.unknown01 = reader.getInt(); + this.guildType = reader.getInt(); + this.guildID = reader.getInt(); + reader.getInt(); + + } + + public int getUnknown01() { + return this.unknown01; + } + + + + public void setUnknown01(int value) { + this.unknown01 = value; + } + + public int getGuildID() { + return guildID; + } + + public void setGuildID(int guildID) { + this.guildID = guildID; + } + +} diff --git a/src/engine/net/client/msg/guild/GuildControlMsg.java b/src/engine/net/client/msg/guild/GuildControlMsg.java new file mode 100644 index 00000000..39e286da --- /dev/null +++ b/src/engine/net/client/msg/guild/GuildControlMsg.java @@ -0,0 +1,241 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.guild; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class GuildControlMsg extends ClientNetMsg { + + private int unknown01; + private int unknown02; + private int unknown03; + private int unknown04; + private String message; + private byte unknown05; + private int unknown06; + private int unknown07; + private byte unknown08; + + private byte isGM; + + /** + * This is the general purpose constructor. + */ + public GuildControlMsg() { + super(Protocol.REQUESTMEMBERLIST); + this.unknown01 = 2; + this.unknown02 = 0; + this.unknown03 = 0; + this.unknown04 = 0; + this.message = "No error message"; + this.unknown05 = 0; + this.unknown06 = 0; + this.unknown07 = 257; + this.unknown08 = (byte) 1; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public GuildControlMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.REQUESTMEMBERLIST, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + writer.putInt(this.unknown03); + writer.putInt(this.unknown04); + writer.putString(this.message); + + writer.put((byte) 1); //Always 1 + + writer.put(isGM); //Can be Tax Collector + writer.put(isGM); //Can be Recruiter + writer.put(isGM); //Can be IC + writer.put(isGM); + + writer.put(isGM); //Can be GM + writer.put((byte) 1); + writer.put((byte) 1); + writer.put((byte) 1); + writer.put((byte) 1); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + this.unknown03 = reader.getInt(); + this.unknown04 = reader.getInt(); + this.message = reader.getString(); + this.unknown05 = reader.get(); + if (this.unknown05 == (byte) 1) { + this.unknown06 = reader.getInt(); + this.unknown07 = reader.getInt(); + this.unknown08 = reader.get(); + } + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the unknown02 + */ + public int getUnknown02() { + return unknown02; + } + + /** + * @param unknown02 + * the unknown02 to set + */ + public void setUnknown02(int unknown02) { + this.unknown02 = unknown02; + } + + /** + * @return the unknown03 + */ + public int getUnknown03() { + return unknown03; + } + + /** + * @param unknown03 + * the unknown03 to set + */ + public void setUnknown03(int unknown03) { + this.unknown03 = unknown03; + } + + /** + * @return the unknown04 + */ + public int getUnknown04() { + return unknown04; + } + + /** + * @param unknown04 + * the unknown04 to set + */ + public void setUnknown04(int unknown04) { + this.unknown04 = unknown04; + } + + /** + * @return the message + */ + public String getMessage() { + return message; + } + + /** + * @param message + * the message to set + */ + public void setMessage(String message) { + this.message = message; + } + + /** + * @return the unknown05 + */ + public byte getUnknown05() { + return unknown05; + } + + /** + * @param unknown05 + * the unknown05 to set + */ + public void setUnknown05(byte unknown05) { + this.unknown05 = unknown05; + } + + /** + * @return the unknown06 + */ + public int getUnknown06() { + return unknown06; + } + + /** + * @param unknown06 + * the unknown06 to set + */ + public void setUnknown06(int unknown06) { + this.unknown06 = unknown06; + } + + /** + * @return the unknown07 + */ + public int getUnknown07() { + return unknown07; + } + + /** + * @param unknown07 + * the unknown07 to set + */ + public void setUnknown07(int unknown07) { + this.unknown07 = unknown07; + } + + /** + * @return the unknown08 + */ + public byte getUnknown08() { + return unknown08; + } + + public void setGM(byte b) { + this.isGM = b; + } + + /** + * @param unknown08 + * the unknown08 to set + */ + public void setUnknown08(byte unknown08) { + this.unknown08 = unknown08; + } + +} diff --git a/src/engine/net/client/msg/guild/GuildCreationCloseMsg.java b/src/engine/net/client/msg/guild/GuildCreationCloseMsg.java new file mode 100644 index 00000000..82b4e552 --- /dev/null +++ b/src/engine/net/client/msg/guild/GuildCreationCloseMsg.java @@ -0,0 +1,67 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.guild; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; +import org.pmw.tinylog.Logger; + +public class GuildCreationCloseMsg extends ClientNetMsg { + + + /** + * This is the general purpose constructor. + */ + public GuildCreationCloseMsg() { + super(Protocol.CANCELGUILDCREATION); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public GuildCreationCloseMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CANCELGUILDCREATION, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + + + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + for(int j = 0; j < 4; j++) { + int i = reader.getInt(); + Logger.info( j+ "->" + i); + } + } + +} + + + diff --git a/src/engine/net/client/msg/guild/GuildCreationFinalizeMsg.java b/src/engine/net/client/msg/guild/GuildCreationFinalizeMsg.java new file mode 100644 index 00000000..a28c4c58 --- /dev/null +++ b/src/engine/net/client/msg/guild/GuildCreationFinalizeMsg.java @@ -0,0 +1,160 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.guild; + +import engine.Enum.GameObjectType; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; +import engine.objects.GuildTag; +import engine.objects.Item; +import org.pmw.tinylog.Logger; + +public class GuildCreationFinalizeMsg extends ClientNetMsg { + + private GuildTag guildTag; + + private String guildName; + private String guildMotto; + + private int unknown01; //Appears to be a pad + private int unknown02; //Appears to be a pad + private int unknown03; //Appears to be a pad + + private int icVote; + private int membershipVote; + + private int unknown04; //Always -1 + private int unknown05; //Appears to be a pad + private int unknown06; //Appears to be a pad + private int subType; //Appears to be a pad + private int unknown08; //3 = close window, 4 = Failure to create Guild. + private boolean close = false; + private int charterUUID; + + /** + * This is the general purpose constructor. + */ + public GuildCreationFinalizeMsg() { + super(Protocol.CREATEPETITION); + } + + public GuildCreationFinalizeMsg(boolean close) { + super(Protocol.CREATEPETITION); + this.close = close; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public GuildCreationFinalizeMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CREATEPETITION, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + GuildTag._serializeForDisplay(guildTag,writer); + + writer.putString(this.guildName); + writer.putString(this.guildMotto); + + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + writer.putInt(this.unknown03); + + writer.putInt(this.membershipVote); + writer.putInt(this.icVote); + + writer.putInt(this.unknown04); + writer.putInt(this.unknown05); + + writer.putInt(this.unknown06); + writer.putInt(3); + writer.putInt(this.unknown08); + writer.putInt(GameObjectType.Item.ordinal()); + writer.putInt(this.charterUUID); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + guildTag = new GuildTag(reader, true); + + this.guildName = reader.getString(); + this.guildMotto = reader.getString(); + + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + this.unknown03 = reader.getInt(); + + this.membershipVote = reader.getInt(); + this.icVote = reader.getInt(); + + this.unknown04 = reader.getInt(); + this.unknown05 = reader.getInt(); + + this.unknown06 = reader.getInt(); + this.subType = reader.getInt(); + this.unknown08 = reader.getInt(); + reader.getInt(); // Object Type padding + this.charterUUID = reader.getInt(); + } + + public String getName() { + return this.guildName; + } + + public String getMotto() { + return this.guildMotto; + } + + public Item getCharter() { + + Item charterObject; + + charterObject = Item.getFromCache(this.charterUUID); + + if (charterObject == null) + Logger.error( "Invalid charter object UUID: " + this.charterUUID); + + return charterObject; + } + + public GuildTag getGuildTag() { + return this.guildTag; + } + + public int getMemberVoteFlag() { + + if (this.membershipVote != 0) + return 1; + + return 0; + } + + public int getICVoteFlag() { + + if (this.icVote != 0) + return 1; + + return 0; + } +} diff --git a/src/engine/net/client/msg/guild/GuildCreationOptionsMsg.java b/src/engine/net/client/msg/guild/GuildCreationOptionsMsg.java new file mode 100644 index 00000000..83574d94 --- /dev/null +++ b/src/engine/net/client/msg/guild/GuildCreationOptionsMsg.java @@ -0,0 +1,151 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.guild; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; +import engine.objects.GuildTag; + +public class GuildCreationOptionsMsg extends ClientNetMsg { + + private int screenType; + private ScreenType messageScreen; + private boolean close = false; + + /** + * This is the general purpose constructor. + */ + public GuildCreationOptionsMsg() { + super(Protocol.CHECKUNIQUEGUILD); + } + public GuildCreationOptionsMsg(boolean close) { + super(Protocol.CHECKUNIQUEGUILD); + this.close = close; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public GuildCreationOptionsMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CHECKUNIQUEGUILD, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.screenType); + if (this.close){ + writer.putInt(2); + writer.putInt(0); +// writer.putInt(0); + return; + } + if(this.messageScreen != null) { + this.messageScreen._serialize(writer); + } else { + writer.putInt(0); + writer.putInt(5); + } + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.screenType = reader.getInt(); + if(this.screenType == 1) { + this.messageScreen = new Screen1(); + } else if(this.screenType == 2){ + this.messageScreen = new Screen2(); + } else { + System.out.println("Attempting to Deserilaize: Message Type" + screenType); + int counter = 0; + do { + int i = reader.getInt(); + System.out.println(counter++ + "->" + i); + } while (true); + } + this.messageScreen._deserialize(reader); + } + + /** + * @return the rulership + */ + public int getScreenType() { + return this.screenType; + } + + /** + * @param rulership + * the rulership to set + */ + public void setScreenType(int type) { + this.screenType = type; + } +} + +abstract class ScreenType { + public abstract void _serialize(ByteBufferWriter writer); + public abstract void _deserialize(ByteBufferReader reader) ; +} + +class Screen1 extends ScreenType{ + private int unknown01; + private int unknown02; + private String name; + private String motto; + + @Override + public void _serialize(ByteBufferWriter writer) { + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + writer.putString(this.name); + } + @Override + public void _deserialize(ByteBufferReader reader) + { + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + this.name = reader.getString(); + this.motto = reader.getString(); + } +} + +class Screen2 extends ScreenType{ + private int unknown01; + private int unknown02; + private GuildTag gt; + + @Override + public void _serialize(ByteBufferWriter writer) { + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + GuildTag._serializeForGuildCreation(this.gt,writer); + } + @Override + public void _deserialize(ByteBufferReader reader) + { + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + this.gt = new GuildTag(reader, true); + } +} + + diff --git a/src/engine/net/client/msg/guild/GuildInfoMsg.java b/src/engine/net/client/msg/guild/GuildInfoMsg.java new file mode 100644 index 00000000..f81f4f06 --- /dev/null +++ b/src/engine/net/client/msg/guild/GuildInfoMsg.java @@ -0,0 +1,453 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.guild; + + +import engine.Enum.GameObjectType; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; +import engine.objects.*; +import org.joda.time.DateTime; + + +public class GuildInfoMsg extends ClientNetMsg { + + private int msgType; + private int objectUUID; + private int objectType; + private Guild guild; + private AbstractGameObject ago; + + /** + * This is the general purpose constructor. + */ + public GuildInfoMsg() { + super(Protocol.UPDATEGUILD); + this.msgType = 4; + this.objectType = 0; + this.objectUUID = 0; + + } + + public GuildInfoMsg(AbstractGameObject ago, Guild guild, int unknown01) { + super(Protocol.UPDATEGUILD); + this.msgType = unknown01; + this.objectType = ago.getObjectType().ordinal(); + this.objectUUID = ago.getObjectUUID(); + this.ago = ago; + + this.guild = guild; + } + + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public GuildInfoMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.UPDATEGUILD, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.msgType); + + if(this.msgType == 2) { + new GuildInfoMessageType2(this.ago, guild)._serialize(writer); + } else if(this.msgType == 5) { + new GuildInfoMessageType5(this.ago, guild)._serialize(writer); + }else if(this.msgType == 4){ + new GuildInfoMessageType4(this.ago, guild)._serialize(writer); + } else { + writer.putLong(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.put((byte) 0); + } + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.msgType = reader.getInt(); + this.objectType = reader.getInt(); + this.objectUUID = reader.getInt(); + + if (this.msgType == 1) + reader.getInt(); + reader.getInt(); // PAdding + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.get(); + + //default guild tag deserializations. + if (this.msgType == 5){ + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + + reader.getInt(); + } + + + } + + /** + * @return the unknown01 + */ + public int getMsgType() { + return msgType; + } + + public void setMsgType(int msgType) { + this.msgType = msgType; + } + + /** + * @return the objectUUID + */ + public int getObjectID() { + return objectUUID; + } + + public int getObjectType() { + return objectType; + } + + public void setObjectType(int objectType) { + this.objectType = objectType; + } +} + +abstract class GuildInfoMessageType { + protected int objectType; + protected int objectID; + protected Guild g; + protected AbstractGameObject ago; + + public GuildInfoMessageType(AbstractGameObject ago, Guild g) { + this.objectType = ago.getObjectType().ordinal(); + this.objectID = ago.getObjectUUID(); + this.ago = ago; + this.g = g; + } + + abstract void _serialize(ByteBufferWriter writer); +} + +class GuildInfoMessageType2 extends GuildInfoMessageType { + + public GuildInfoMessageType2(AbstractGameObject ago, Guild g) { + super(ago, g); + } + + @Override + void _serialize(ByteBufferWriter writer) { + writer.putInt(this.objectType); + writer.putInt(this.objectID); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(1); + + Guild nation = null; + if (this.g != null) { + writer.putInt(GameObjectType.Guild.ordinal()); + writer.putInt(g.getObjectUUID()); + writer.putString(g.getName()); + + if(this.objectType == GameObjectType.PlayerCharacter.ordinal()) { + PlayerCharacter pc = PlayerCharacter.getFromCache(this.objectID); + + if(pc != null) { + writer.putInt(GuildStatusController.getRank(pc.getGuildStatus())); + } + } else { + writer.putInt(5); + } + GuildTag._serializeForDisplay(g.getGuildTag(),writer); + nation = this.g.getNation(); + } else { + writer.putLong(0L); + writer.putString(""); + GuildTag._serializeForDisplay(GuildTag.ERRANT,writer); + } + + writer.putInt(1); + if (nation != null) { + writer.putInt(GameObjectType.Guild.ordinal()); + writer.putInt(nation.getObjectUUID()); + + City city = g.getOwnedCity(); + if (city != null) { + writer.putString(city.getCityName()); + writer.putInt(city.getObjectType().ordinal()); + writer.putInt(city.getObjectUUID()); + } else { + writer.putString(""); //city name + writer.putLong(0L); //should be city ID + } + + GuildTag._serializeForDisplay(nation.getGuildTag(),writer); + + } else { + writer.putLong(0L); + writer.putString(""); + writer.putLong(0L); + GuildTag._serializeForDisplay(GuildTag.ERRANT,writer); + } + writer.putInt(0); + + writer.putInt(0); + writer.put((byte)0); + } +} + +class GuildInfoMessageType4 extends GuildInfoMessageType { + + public GuildInfoMessageType4(AbstractGameObject ago, Guild g) { + super(ago, g); + } + + @Override + void _serialize(ByteBufferWriter writer) { + String cityName = ""; + writer.putInt(this.objectType); + writer.putInt(this.objectID); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + PlayerCharacter pc = PlayerCharacter.getFromCache(this.objectID); + if (this.g == null || pc == null){ + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.put((byte)0); + return; + } + + writer.putInt(1); + Guild nation = this.g.getNation(); + writer.putInt(0); + writer.putInt(0); + writer.putString(g.getName()); + writer.putInt(0); + GuildTag._serializeForDisplay(g.getGuildTag(),writer); + writer.putInt(1); + writer.putInt(0); + writer.putInt(0); + City city = g.getOwnedCity(); + if (city != null) + cityName = city.getCityName(); + writer.putString(nation.getName()); + writer.putInt(0); + writer.putInt(0); + + GuildTag._serializeForDisplay(nation.getGuildTag(),writer); + writer.putInt(1); + + writer.putString(g.getName()); + writer.putString(g.getMotto()); + writer.putString(nation.getName()); + writer.putInt(GuildStatusController.getRank(pc.getGuildStatus())); + writer.putInt(GuildStatusController.getTitle(pc.getGuildStatus())); + writer.putInt(g.getCharter()); + writer.putString(cityName); //Shows City Name FUCK + AbstractCharacter guildLeader; + String guildLeaderName = ""; + if (g.isNPCGuild()){ + guildLeader = NPC.getFromCache(g.getGuildLeaderUUID()); + if (guildLeader != null) + guildLeaderName = guildLeader.getName(); + } + + else{ + guildLeader = PlayerCharacter.getFromCache(g.getGuildLeaderUUID()); + if (guildLeader != null) + guildLeaderName = ((PlayerCharacter)guildLeader).getCombinedName(); + } + + writer.putString(guildLeaderName); + writer.putString(pc.getName()); + + writer.putDateTime(DateTime.now()); + writer.put((byte)1); + writer.put((byte)1); + writer.put((byte)1); + writer.put((byte)0); + writer.putInt(0); + + + + + + + // writer.put((byte)0); + + + // writer.putString(cityName); + // writer.putInt(10); + // writer.putInt(6); + // writer.putInt(10); + // writer.putString(cityName); + // writer.putString(pc.getFirstName()); + // writer.putString("Nation Leader"); + // writer.putDateTime(DateTime.now()); + // writer.put((byte)1); + // writer.put((byte)1); + // writer.putInt(1); + // writer.putInt(this.objectType); + // writer.putInt(this.objectID); + // writer.putInt(0); + // writer.putInt(0); + + + } +} + +class GuildInfoMessageType5 extends GuildInfoMessageType { + + public GuildInfoMessageType5(AbstractGameObject ago, Guild g) { + super(ago, g); + } + + @Override + void _serialize(ByteBufferWriter writer) { + + PlayerCharacter pc = null; + + if(ago.getObjectType().equals(GameObjectType.PlayerCharacter)) { + pc = (PlayerCharacter) ago; + } + + if(pc != null && g != null && g.getObjectUUID() != 0) { + Guild n = g.getNation(); + if(n == null) { + n = Guild.getErrantNation(); + } + + writer.putInt(ago.getObjectType().ordinal()); + writer.putInt(ago.getObjectUUID()); + + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + + writer.putInt(1); + writer.putInt(0); + writer.putInt(0); + + writer.putString(g.getName()); //No Effect? + writer.putInt(0); //Pad + + GuildTag._serializeForDisplay(g.getGuildTag(),writer); //Also a waste of space... + + writer.putInt(1); + writer.putInt(0); + writer.putInt(0); + writer.putString(n.getName()); //No Effect? + writer.putInt(0); + writer.putInt(0); + + GuildTag._serializeForDisplay(n.getGuildTag(),writer); + + writer.putInt(1); + + writer.putString(g.getName()); //Guild Name + writer.putString(g.getMotto()); //TODO Motto + writer.putString(n.getName()); //Nation Name + + writer.putInt(GuildStatusController.getRank(pc.getGuildStatus())); //Rank + writer.putInt(GuildStatusController.getTitle(pc.getGuildStatus())); //Title + writer.putInt(g.getCharter()); + + if(g.getNation().equals(Guild.getErrantNation())) + writer.putString("Errant"); + else + writer.putString("City"); + writer.putString("Guild Leader"); + writer.putString(""); //Nation Leader - I believe + + DateTime now = DateTime.now(); + writer.putDateTime(now); + + writer.put((byte) 1); + writer.put((byte) 1); + writer.put((byte) 0); + writer.put((byte) 0); + + GuildTag._serializeForDisplay(g.getGuildTag(),writer); + GuildTag._serializeForDisplay(g.getNation().getGuildTag(),writer); + writer.putInt(0); + } else { + writer.putLong(0); + + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + + writer.put((byte) 0); + + GuildTag._serializeForDisplay(GuildTag.ERRANT,writer); + GuildTag._serializeForDisplay(GuildTag.ERRANT,writer); + + writer.putInt(0); + } + } +} diff --git a/src/engine/net/client/msg/guild/GuildListMsg.java b/src/engine/net/client/msg/guild/GuildListMsg.java new file mode 100644 index 00000000..17404166 --- /dev/null +++ b/src/engine/net/client/msg/guild/GuildListMsg.java @@ -0,0 +1,228 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.guild; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.gameManager.SessionManager; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; +import engine.objects.Guild; +import engine.objects.GuildHistory; +import engine.objects.GuildStatusController; +import engine.objects.PlayerCharacter; + +import java.util.ArrayList; + + +public class GuildListMsg extends ClientNetMsg { + + private GuildListMessageType glm; + + /** + * Type 1 Constructor - Guild Roster + */ + public GuildListMsg(Guild g) { + super(Protocol.SENDMEMBERENTRY); + this.glm = new GuildListMessageType1(g); + } + + /** + * Type 4 Constructor - Guild History + */ + public GuildListMsg(PlayerCharacter pc) { + super(Protocol.SENDMEMBERENTRY); + this.glm = new GuildListMessageType2(pc); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public GuildListMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.SENDMEMBERENTRY, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + //TODO Find Default and null check this + this.glm._serialize(writer); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + /* + * + * The Server should never receive this message directly. + * Instances in recordings will need to be decoded by hand, + * converting from our format to the standard format will + * cause more problems than its worth to fix. + * + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + this.unknown03 = reader.get(); + this.unknown04 = reader.getInt(); + this.unknown05 = reader.get(); + this.unknown06 = reader.getInt(); + int size = reader.getInt(); + for (int i = 0; i < size; i++) { + GuildTableList gt = new GuildTableList(); + reader.getInt(); // Player Character ID Type + gt.setUUID(reader.getInt()); + gt.setName(reader.getString()); + gt.setActionType(reader.get()); + gt.setGuildTitle(reader.getInt()); + gt.setGuildRank(reader.getInt()); + gt.setUnknown02(reader.getInt()); + gt.setUnknown03(reader.getInt()); + gt.setUnknown04(reader.getInt()); + gt.setUnknown05(reader.getInt()); + gt.setUnknown06(reader.getInt()); + gt.setUnknown07(reader.getInt()); + } + */ + } + + @Override + protected int getPowerOfTwoBufferSize() { + // Larger size for historically larger opcodes + return 15; + } +} + +abstract class GuildListMessageType { + public ArrayList history = new ArrayList<>(); + + abstract void _serialize(ByteBufferWriter writer); +} + +class GuildListMessageType1 extends GuildListMessageType { + + private Guild g; + + public GuildListMessageType1(Guild g) { + this.g = g; + } + + @Override + void _serialize(ByteBufferWriter writer) { + Enum.GuildType gt = Enum.GuildType.getGuildTypeFromInt(g.getCharter()); + + writer.putInt(1); + writer.putInt(gt.ordinal()); //Charter Type + writer.putInt(1); //Always 1? + writer.put((byte) 1); + writer.put((byte) 0); + writer.putInt(gt.getNumberOfRanks()); //Number of Ranks + + + ArrayList guildRoster = Guild.GuildRoster(g); + writer.putInt(guildRoster.size() + g.getBanishList().size()); + + // Send guild list of each player + for (PlayerCharacter player : guildRoster) { + + byte isActive = SessionManager.getPlayerCharacterByID(player.getObjectUUID()) != null ? (byte)1 : (byte)0; + writer.putInt(Enum.GameObjectType.PlayerCharacter.ordinal()); + writer.putInt(player.getObjectUUID()); + writer.putString(player.getCombinedName()); + writer.put(isActive); //Active? + writer.putInt(GuildStatusController.getTitle(player.getGuildStatus())); + writer.putInt(GuildStatusController.getRank(player.getGuildStatus())); + writer.putInt(0); // 1/2 has no effect + writer.putInt(0); // 1 has no effect + writer.putInt(0); // 1 has no effect + writer.putInt(0); // 1 has no effect + writer.putInt(GuildStatusController.getRank(player.getGuildStatus())); + writer.putInt(0); // window failed to open when set to 1 + } + + for (PlayerCharacter banished : g.getBanishList()){ + byte isActive = SessionManager.getPlayerCharacterByID(banished.getObjectUUID()) != null ? (byte)1 : (byte)0; + writer.putInt(Enum.GameObjectType.PlayerCharacter.ordinal()); + writer.putInt(banished.getObjectUUID()); + writer.putString(banished.getCombinedName()); + writer.put(isActive); //Active? + writer.putInt(GuildStatusController.getTitle(banished.getGuildStatus())); + writer.putInt(3); + writer.putInt(0); // 1/2 has no effect + writer.putInt(0); // 1 has no effect + writer.putInt(0); // 1 has no effect + writer.putInt(0); // 1 has no effect + writer.putInt(3); + writer.putInt(0); // window failed to open when set to 1 + } + } +} + +class GuildListMessageType2 extends GuildListMessageType { + + private PlayerCharacter pc; + + public GuildListMessageType2(PlayerCharacter pc) { + this.pc = pc; + } + + @Override + void _serialize(ByteBufferWriter writer) { + + Guild g = pc.getGuild(); + + writer.putInt(4); + + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + + writer.put((byte) 0); + writer.put((byte) 0); + writer.putInt(1); + writer.putInt(pc.getObjectType().ordinal()); + writer.putInt(pc.getObjectUUID()); + writer.putString(pc.getCombinedName()); + + writer.put((byte) 1); + writer.putInt(GuildStatusController.getTitle(pc.getGuildStatus())); //Title Maybe? + writer.putInt(GuildStatusController.getRank(pc.getGuildStatus())); //Rank? + + writer.putInt(pc.getRaceToken()); //race token + writer.putInt(pc.getBaseClassToken()); //class token + + writer.putInt(2); //PAD + writer.putInt(pc.getLevel()); + writer.putInt(g.getCharter()); + + //TODO Get Guild History from the DB + //ArrayList history = DbManager.GuildQueries.GET_GUILD_HISTORY_OF_PLAYER((int)pc.getPlayerUUID()); + writer.putInt(pc.getGuildHistory().size()); //Number of Entries + + for(GuildHistory guildHistory : pc.getGuildHistory()) { + writer.putInt(guildHistory.getHistoryType().getType()); + writer.putInt(GameObjectType.Guild.ordinal()); + writer.putInt((int) guildHistory.getGuildID()); + writer.putString(guildHistory.getGuildName()); + writer.putInt(0); + writer.putDateTime(guildHistory.getTime()); + } + } + +} diff --git a/src/engine/net/client/msg/guild/GuildUnknownMsg.java b/src/engine/net/client/msg/guild/GuildUnknownMsg.java new file mode 100644 index 00000000..6eb32778 --- /dev/null +++ b/src/engine/net/client/msg/guild/GuildUnknownMsg.java @@ -0,0 +1,67 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.guild; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class GuildUnknownMsg extends ClientNetMsg { + + private int unknown01; + private int unknown02; + private int unknown03; + private byte unknownByte; + + + /** + * This is the general purpose constructor. + */ + public GuildUnknownMsg() { + super(Protocol.UPDATECLIENTALLIANCES); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public GuildUnknownMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.UPDATECLIENTALLIANCES, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.put((byte)1); +// writer.putInt(this.unknown01); +// writer.putInt(this.unknown02); +// writer.putInt(this.unknown02); +// writer.putInt(this.unknownByte); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.unknown01 = reader.getInt(); + this.unknown02 = reader.getInt(); + this.unknown02 = reader.getInt(); + this.unknownByte = reader.get(); + } +} diff --git a/src/engine/net/client/msg/guild/InviteToGuildMsg.java b/src/engine/net/client/msg/guild/InviteToGuildMsg.java new file mode 100644 index 00000000..a5692ac4 --- /dev/null +++ b/src/engine/net/client/msg/guild/InviteToGuildMsg.java @@ -0,0 +1,196 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.guild; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; +import engine.objects.GuildTag; + +public class InviteToGuildMsg extends ClientNetMsg { + + private int response; + private int sourceType; + private int sourceUUID; + private int targetType; + private int targetUUID; + private int unknown01; + private String message; + private GuildTag gt; + private String guildName; + private int guildType; + private int guildID; + private String targetName; + + /** + * This is the general purpose constructor. + */ + public InviteToGuildMsg() { + super(Protocol.INVITETOGUILD); + this.response = 0; + this.message = "No error message"; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public InviteToGuildMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.INVITETOGUILD, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.response); + writer.putLong(this.sourceUUID); + writer.putLong(this.targetUUID); + writer.putInt(this.unknown01); + writer.putString(this.message); + writer.putInt(this.gt.backgroundColor01); + writer.putInt(this.gt.backgroundColor02); + writer.putInt(this.gt.symbolColor); + writer.putInt(this.gt.symbol); + writer.putInt(this.gt.backgroundDesign); + writer.putString(this.guildName); + writer.putInt(this.guildType); + writer.putInt(this.guildID); + writer.putString(this.targetName); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.response = reader.getInt(); + this.sourceType = reader.getInt(); + this.sourceUUID = reader.getInt(); + this.targetType = reader.getInt(); + this.targetUUID = reader.getInt(); + this.unknown01 = reader.getInt(); + this.message = reader.getString(); + this.gt = new GuildTag(reader); + this.guildName = reader.getString(); + this.guildType = reader.getInt(); + this.guildID = reader.getInt(); + this.targetName = reader.getString(); + } + + public GuildTag getGuildTag() { + return this.gt; + } + + public void setGuildTag(GuildTag gt) { + this.gt = gt; + } + + /** + * @return the response + */ + public int getResponse() { + return response; + } + + /** + * @param response + * the response to set + */ + public void setResponse(int response) { + this.response = response; + } + + /** + * @return the sourceUUID + */ + public int getSourceUUID() { + return sourceUUID; + } + + /** + * @param sourceUUID + * the sourceUUID to set + */ + public void setSourceUUID(int sourceUUID) { + this.sourceUUID = sourceUUID; + } + + /** + * @return the targetUUID + */ + public int getTargetUUID() { + return targetUUID; + } + + /** + * @param targetUUID + * the targetUUID to set + */ + public void setTargetUUID(int targetUUID) { + this.targetUUID = targetUUID; + } + + /** + * @param guildName + * the guildName to set + */ + public void setGuildName(String guildName) { + this.guildName = guildName; + } + + /** + * @param guildID + * the guildID to set + */ + public void setGuildUUID(int guildID) { + this.guildID = guildID; + } + + /** + * @return the targetName + */ + public String getTargetName() { + return targetName; + } + + /** + * @param targetName + * the targetName to set + */ + public void setTargetName(String targetName) { + this.targetName = targetName; + } + + public void setSourceType(int sourceType) { + this.sourceType = sourceType; + } + + public void setTargetType(int targetType) { + this.targetType = targetType; + } + + public void setGuildType(int guildType) { + this.guildType = guildType; + } + + /** + * @return the targetType + */ + public int getTargetType() { + return targetType; + } +} diff --git a/src/engine/net/client/msg/guild/InviteToSubMsg.java b/src/engine/net/client/msg/guild/InviteToSubMsg.java new file mode 100644 index 00000000..3784d696 --- /dev/null +++ b/src/engine/net/client/msg/guild/InviteToSubMsg.java @@ -0,0 +1,201 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.guild; + +import engine.Enum.GameObjectType; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; +import engine.objects.GuildTag; + +public class InviteToSubMsg extends ClientNetMsg { + + //12d67402 0000f062 56253600 0010b062 d7821800 00000000 + //10000000 4e006f0020006500720072006f00720020006d00650073007300610067006500 + //0a000000 03000000 07000000 a0000000 04000000 + //17000000 4800650072006f006500730020006f0066002000530065006100200044006f006700730020005200650073007400 + //0000200a 33000000 00000000 01000000 + + //12d67402 0000d062 11fe1700 00009063 3ee91300 00000000 + //10000000 4e006f0020006500720072006f00720020006d00650073007300610067006500 + //08000000 08000000 07000000 96000000 0d000000 + //11000000 42006c006f006f00640020004d006f006f006e00200052006900730069006e006700 + //0000c006 64540000 01000000 00000000 + + //12d67402 0000e062 db2d0000 00005063 116e1100 00000000 + //10000000 4e006f0020006500720072006f00720020006d00650073007300610067006500 + //05000000 11000000 0e000000 95000000 05000000 + //09000000 530074006f0072006d0068006f006c006400 + //0000c006 56300000 01000000 00000000 + + private int sourceUUID; + private int targetUUID; + private int unknown01; + private String message; + private GuildTag gt; + private String guildName; + private int guildUUID; + private int unknown02; + private int unknown03; + + /** + * This is the general purpose constructor. + */ + public InviteToSubMsg() { + super(Protocol.INVITEGUILDFEALTY); + this.message = "No error message"; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public InviteToSubMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.INVITEGUILDFEALTY, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(GameObjectType.PlayerCharacter.ordinal()); + writer.putInt(this.sourceUUID); + writer.putInt(GameObjectType.PlayerCharacter.ordinal()); + writer.putInt(this.targetUUID); + writer.putInt(this.unknown01); + writer.putString(this.message); + writer.putInt(this.gt.backgroundColor01); + writer.putInt(this.gt.backgroundColor02); + writer.putInt(this.gt.symbolColor); + writer.putInt(this.gt.symbol); + writer.putInt(this.gt.backgroundDesign); + writer.putString(this.guildName); + writer.putInt(GameObjectType.Guild.ordinal()); + writer.putInt(this.guildUUID); + writer.putInt(this.unknown02); + writer.putInt(this.unknown03); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); + this.sourceUUID = reader.getInt(); + reader.getInt(); + this.targetUUID = reader.getInt(); + this.unknown01 = reader.getInt(); + this.message = reader.getString(); + this.gt = new GuildTag(reader); + this.guildName = reader.getString(); + reader.getInt(); // Padding for Object Type + this.guildUUID = reader.getInt(); + this.unknown02 = reader.getInt(); + this.unknown03 = reader.getInt(); + } + + public GuildTag getGuildTag() { + return this.gt; + } + + public void setGuildTag(GuildTag gt) { + this.gt = gt; + } + + /** + * @return the sourceUUID + */ + public int getSourceUUID() { + return sourceUUID; + } + + /** + * @return the targetUUID + */ + public int getTargetUUID() { + return targetUUID; + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the message + */ + public String getMessage() { + return message; + } + + /** + * @param message + * the message to set + */ + public void setMessage(String message) { + this.message = message; + } + + /** + * @return the guildName + */ + public String getGuildName() { + return guildName; + } + + /** + * @param guildName + * the guildName to set + */ + public void setGuildName(String guildName) { + this.guildName = guildName; + } + + + public int getUnknown02() { + return unknown02; + } + + public void setUnknown02(int unknown02) { + this.unknown02 = unknown02; + } + + public int getUnknown03() { + return unknown03; + } + + public void setUnknown03(int unknown03) { + this.unknown03 = unknown03; + } + + public int getGuildUUID() { + return guildUUID; + } + + public void setGuildUUID(int guildUUID) { + this.guildUUID = guildUUID; + } +} diff --git a/src/engine/net/client/msg/guild/LeaveGuildMsg.java b/src/engine/net/client/msg/guild/LeaveGuildMsg.java new file mode 100644 index 00000000..8ee24ce3 --- /dev/null +++ b/src/engine/net/client/msg/guild/LeaveGuildMsg.java @@ -0,0 +1,105 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.guild; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class LeaveGuildMsg extends ClientNetMsg { + + private int response; + private String message; + private long sourceID; + + /** + * This is the general purpose constructor. + */ + public LeaveGuildMsg() { + super(Protocol.LEAVEGUILD); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public LeaveGuildMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.LEAVEGUILD, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.response); + writer.putString(this.message); + writer.putLong(this.sourceID); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.response = reader.getInt(); + this.message = reader.getString(); + this.sourceID = reader.getLong(); + } + + /** + * @return the response + */ + public int getResponse() { + return response; + } + + /** + * @param response + * the response to set + */ + public void setResponse(int response) { + this.response = response; + } + + /** + * @return the message + */ + public String getMessage() { + return message; + } + + /** + * @param message + * the message to set + */ + public void setMessage(String message) { + this.message = message; + } + + /** + * @return the sourceID + */ + public long getSourceID() { + return sourceID; + } + + /** + * @param sourceID + * the sourceID to set + */ + public void setSourceID(long sourceID) { + this.sourceID = sourceID; + } + +} diff --git a/src/engine/net/client/msg/guild/MOTDCommitMsg.java b/src/engine/net/client/msg/guild/MOTDCommitMsg.java new file mode 100644 index 00000000..58b70016 --- /dev/null +++ b/src/engine/net/client/msg/guild/MOTDCommitMsg.java @@ -0,0 +1,110 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.guild; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class MOTDCommitMsg extends ClientNetMsg { + + private String message; + private int unknown01; + private int type; + + /** + * This is the general purpose constructor. + */ + public MOTDCommitMsg() { + super(Protocol.SETMOTD); + this.message = ""; + this.unknown01 = 0; + this.type = 0; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public MOTDCommitMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.SETMOTD, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putString(this.message); + writer.putInt(this.unknown01); + writer.putInt(this.type); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.message = reader.getString(); + this.unknown01 = reader.getInt(); + this.type = reader.getInt(); + } + + /** + * @return the message + */ + public String getMessage() { + return message; + } + + /** + * @param message + * the message to set + */ + public void setMessage(String message) { + this.message = message; + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the type + */ + public int getType() { + return type; + } + + /** + * @param type + * the type to set + */ + public void setType(int type) { + this.type = type; + } + +} diff --git a/src/engine/net/client/msg/guild/MOTDMsg.java b/src/engine/net/client/msg/guild/MOTDMsg.java new file mode 100644 index 00000000..ec33b534 --- /dev/null +++ b/src/engine/net/client/msg/guild/MOTDMsg.java @@ -0,0 +1,133 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.guild; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class MOTDMsg extends ClientNetMsg { + + private int type; + private byte response; + private int unknown01; + private String message; + + /** + * This is the general purpose constructor. + */ + public MOTDMsg() { + super(Protocol.MOTD); + this.type = 0; + this.response = (byte) 0; + this.unknown01 = 0; + this.message = ""; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public MOTDMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.MOTD, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.type); + writer.put(this.response); + if (this.response == (byte) 1) { + writer.putInt(this.unknown01); + writer.putString(this.message); + } + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.type = reader.getInt(); + this.response = reader.get(); + if (this.response == (byte) 1) { + this.unknown01 = reader.getInt(); + this.message = reader.getString(); + } + } + + /** + * @return the type + */ + public int getType() { + return type; + } + + /** + * @param type + * the type to set + */ + public void setType(int type) { + this.type = type; + } + + /** + * @return the response + */ + public byte getResponse() { + return response; + } + + /** + * @param response + * the response to set + */ + public void setResponse(byte response) { + this.response = response; + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the message + */ + public String getMessage() { + return message; + } + + /** + * @param message + * the message to set + */ + public void setMessage(String message) { + this.message = message; + } + +} diff --git a/src/engine/net/client/msg/guild/ReqGuildListMsg.java b/src/engine/net/client/msg/guild/ReqGuildListMsg.java new file mode 100644 index 00000000..69d8491f --- /dev/null +++ b/src/engine/net/client/msg/guild/ReqGuildListMsg.java @@ -0,0 +1,56 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.guild; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class ReqGuildListMsg extends ClientNetMsg { + + private long nationCompID; + private long guildToSubCompID; + + /** + * This is the general purpose constructor. + */ + public ReqGuildListMsg() { + super(Protocol.REQUESTGUILDLIST); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public ReqGuildListMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.REQUESTGUILDLIST, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); + reader.getString(); + } + +} diff --git a/src/engine/net/client/msg/guild/SendGuildEntryMsg.java b/src/engine/net/client/msg/guild/SendGuildEntryMsg.java new file mode 100644 index 00000000..73105b86 --- /dev/null +++ b/src/engine/net/client/msg/guild/SendGuildEntryMsg.java @@ -0,0 +1,106 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.guild; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; +import engine.objects.Guild; +import engine.objects.GuildTag; +import engine.objects.PlayerCharacter; + +import java.util.ArrayList; + +public class SendGuildEntryMsg extends ClientNetMsg { + + private PlayerCharacter pc; + + /** + * This is the general purpose constructor. + */ + public SendGuildEntryMsg(PlayerCharacter pc) { + super(Protocol.SENDGUILDENTRY); + this.pc = pc; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public SendGuildEntryMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.SENDGUILDENTRY, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + ArrayListsubsAndSovs = new ArrayList<>(); + + Guild nation = pc.getGuild().getNation(); + if (pc.getGuild() != nation) + subsAndSovs.add(nation); + subsAndSovs.addAll(pc.getGuild().getSubGuildList()); + + + writer.putInt(1); + //GuildState + //Petitioner = 2, Sworn = 4 , Nation = 5, protectorate = 6, city-State = 7(nation), province = 8, + + writer.putInt(subsAndSovs.size()); + writer.putInt(1); + if (subsAndSovs.size() > 0){ + + for (Guild guild : subsAndSovs){ + int state = guild.getGuildState().getStateID(); + + writer.putInt(guild.getObjectType().ordinal()); + writer.putInt(guild.getObjectUUID()); + + writer.putString(guild.getName()); + + //TODO set Alliance date + writer.putShort((short)1); + writer.putInt(0); + writer.putShort((short)0); + writer.put((byte)0); + + writer.putInt(state); + GuildTag._serializeForDisplay(guild.getGuildTag(),writer); + if (guild == nation) + writer.putInt(2); // Break Fealty + else + writer.putInt(1); // Dismiss, Swear In. + } + } + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + + } + + public PlayerCharacter getPc() { + return pc; + } + + public void setPc(PlayerCharacter pc) { + this.pc = pc; + } + +} diff --git a/src/engine/net/client/msg/guild/SwearInGuildMsg.java b/src/engine/net/client/msg/guild/SwearInGuildMsg.java new file mode 100644 index 00000000..480f97b6 --- /dev/null +++ b/src/engine/net/client/msg/guild/SwearInGuildMsg.java @@ -0,0 +1,78 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.guild; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class SwearInGuildMsg extends ClientNetMsg { + + private int guildType; + private int guildUUID; + + private String message; + + /** + * This is the general purpose constructor. + */ + public SwearInGuildMsg() { + super(Protocol.SWEARINGUILD); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public SwearInGuildMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.SWEARINGUILD, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + + this.guildType = reader.getInt(); + this.guildUUID = reader.getInt(); + reader.getInt(); + this.message = reader.getString(); + } + + /** + * @return the targetType + */ + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + + public int getGuildUUID() { + return guildUUID; + } + +} diff --git a/src/engine/net/client/msg/guild/SwearInMsg.java b/src/engine/net/client/msg/guild/SwearInMsg.java new file mode 100644 index 00000000..8a7f5b75 --- /dev/null +++ b/src/engine/net/client/msg/guild/SwearInMsg.java @@ -0,0 +1,123 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.guild; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class SwearInMsg extends ClientNetMsg { + + private int targetType; + private int targetID; + private int unknown01; + private String message; + + /** + * This is the general purpose constructor. + */ + public SwearInMsg() { + super(Protocol.ACTIVATEPLEDGE); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public SwearInMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.ACTIVATEPLEDGE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.targetType); + writer.putInt(this.targetID); + writer.putInt(this.unknown01); + writer.putString(this.message); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.targetType = reader.getInt(); + this.targetID = reader.getInt(); + this.unknown01 = reader.getInt(); + this.message = reader.getString(); + } + + /** + * @return the targetType + */ + public int getTargetType() { + return targetType; + } + + /** + * @param targetType + * the targetType to set + */ + public void setTargetType(int targetType) { + this.targetType = targetType; + } + + /** + * @return the targetID + */ + public int getTargetID() { + return targetID; + } + + /** + * @param targetID + * the targetID to set + */ + public void setTargetID(int targetID) { + this.targetID = targetID; + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the message + */ + public String getMessage() { + return message; + } + + /** + * @param message + * the message to set + */ + public void setMessage(String message) { + this.message = message; + } + +} diff --git a/src/engine/net/client/msg/login/CharSelectScreenMsg.java b/src/engine/net/client/msg/login/CharSelectScreenMsg.java new file mode 100644 index 00000000..47585c29 --- /dev/null +++ b/src/engine/net/client/msg/login/CharSelectScreenMsg.java @@ -0,0 +1,294 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.login; + + +import engine.exception.SerializationException; +import engine.net.AbstractConnection; +import engine.net.AbstractNetMsg; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; +import engine.objects.Account; +import engine.objects.PlayerCharacter; +import engine.session.Session; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; + + +public class CharSelectScreenMsg extends ClientNetMsg { + + private int numChars; + private int selectedIndex; + private int static01; + private byte static02; + private ArrayList chars; + private boolean fromCommit; + private Account account; + + /** + * Special Constructor + * + * @param s + */ + public CharSelectScreenMsg(Session s) { + this(s, false); + } + + /** + * Special Constructor + * + * @param s + * @param fromCommit + */ + public CharSelectScreenMsg(Session s, boolean fromCommit) { + super(Protocol.CHARSELECTSCREEN); + this.fromCommit = fromCommit; + this.chars = new ArrayList<>(); + // get this account + this.account = s.getAccount(); + + // Get all the character records for this account + chars = new ArrayList<>(this.account.characterMap.values()); + + if (chars == null) { + this.chars = new ArrayList<>(); + } + + // idiot check the quantity of the ArrayList/numChars + this.numChars = chars.size(); + if (this.numChars > 7) { + Logger.error("Account '" + this.account.getUname() + "' has more than 7 characters."); + + this.numChars = 7; + } + + // Get the last character used (As a composite ID). + int lastChar = s.getAccount().getLastCharIDUsed(); + + // Look it up for the index # + this.selectedIndex = 0; + + for (PlayerCharacter pc : chars) + if (pc.getObjectUUID() == lastChar) + break; + else + selectedIndex++; + + // idiot check the index # + if (this.selectedIndex < 0) { + this.selectedIndex = 0; + } + if (this.selectedIndex > 6) { + this.selectedIndex = 6; + } + + this.static01 = 7; + this.static02 = (byte) 1; + } + + /** + * This is the general purpose constructor. + */ + public CharSelectScreenMsg() { + super(Protocol.CHARSELECTSCREEN); + this.chars = new ArrayList<>(); + this.selectedIndex = 0; + this.static01 = 7; + this.static02 = (byte) 1; + this.fromCommit = false; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public CharSelectScreenMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CHARSELECTSCREEN, origin, reader); + + this.chars = new ArrayList<>(); + } + + /** + * @see AbstractNetMsg#getPowerOfTwoBufferSize() + */ + @Override + protected int getPowerOfTwoBufferSize() { + //Larger size for historically larger opcodes + return (17); // 2^17 == 131,072 + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + if (this.account == null) + Logger.error( "failed to find account for message"); + + // Double check char belongs to this account + for (int i = 0; i < this.numChars; ++i) { + if (this.chars.get(i) == null) + Logger.error("failed to find character"); + if (this.chars.get(i).getAccount() == null) + Logger.error("failed to find account for character " + + this.chars.get(i).getObjectUUID()); + if (this.chars.get(i).getAccount().getObjectUUID() != this.account.getObjectUUID()) { + this.chars.remove(i); + this.numChars--; + + Logger.error( "This character doesn't belong to this account."); + + } + } + + writer.putInt(this.numChars); // 4bytes + writer.putInt(this.selectedIndex); // 4bytes + writer.putInt(this.static01); // 4bytes + writer.put(this.static02); // 1 byte + + for (int i = 0; i < this.numChars; ++i) { + try { + if (!fromCommit) + PlayerCharacter.serializeForClientMsgLogin(this.chars.get(i),writer); + else + PlayerCharacter.serializeForClientMsgCommit(this.chars.get(i),writer); + } catch (SerializationException e) { + Logger.error( "failed to serialize character " + this.chars.get(i).getObjectUUID()); + // Handled already. + } + } + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.numChars = reader.getInt(); + this.selectedIndex = reader.getInt(); + + this.static01 = reader.monitorInt(0, "CharSelectScreenMsg-01"); + this.static02 = reader.monitorByte((byte) 0, "CharSelectScreenMsg-02"); + + // TODO is this correct?!?!? + } + + /** + * @return the numChars + */ + public int getNumChars() { + return numChars; + } + + /** + * @param numChars + * the numChars to set + */ + public void setNumChars(int numChars) { + this.numChars = numChars; + } + + /** + * @return the selectedIndex + */ + public int getSelectedIndex() { + return selectedIndex; + } + + /** + * @param selectedIndex + * the selectedIndex to set + */ + public void setSelectedIndex(int selectedIndex) { + this.selectedIndex = selectedIndex; + } + + /** + * @return the static01 + */ + public int getStatic01() { + return static01; + } + + /** + * @param static01 + * the static01 to set + */ + public void setStatic01(int static01) { + this.static01 = static01; + } + + /** + * @return the static02 + */ + public byte getStatic02() { + return static02; + } + + /** + * @param static02 + * the static02 to set + */ + public void setStatic02(byte static02) { + this.static02 = static02; + } + + /** + * @return the chars + */ + public ArrayList getChars() { + return chars; + } + + /** + * @param chars + * the chars to set + */ + public void setChars(ArrayList chars) { + this.chars = chars; + } + + /** + * @return the fromCommit + */ + public boolean isFromCommit() { + return fromCommit; + } + + /** + * @param fromCommit + * the fromCommit to set + */ + public void setFromCommit(boolean fromCommit) { + this.fromCommit = fromCommit; + } + + /** + * @return the account + */ + public Account getAccount() { + return account; + } + + /** + * @param account + * the account to set + */ + public void setAccount(Account account) { + this.account = account; + } + +} diff --git a/src/engine/net/client/msg/login/ClientLoginInfoMsg.java b/src/engine/net/client/msg/login/ClientLoginInfoMsg.java new file mode 100644 index 00000000..5d38b673 --- /dev/null +++ b/src/engine/net/client/msg/login/ClientLoginInfoMsg.java @@ -0,0 +1,267 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.login; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class ClientLoginInfoMsg extends ClientNetMsg { + + private String uname; + private String pword; + + private int unknown01; + private int unknown02; + + private String os; + + private int unknown03; + private int unknown04; + private int unknown05; + private int unknown06; + private int unknown07; + private int unknown08; + private short unknown09; + + /** + * This is the general purpose constructor. + */ + public ClientLoginInfoMsg(String uName, String pWord, String os) { + super(Protocol.LOGIN); + this.uname = uName; + this.pword = pWord; + this.os = os; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ClientLoginInfoMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.LOGIN, origin, reader); + } + + /** + * This is the Copy constructor. + */ + public ClientLoginInfoMsg(ClientLoginInfoMsg msg) { + super(Protocol.LOGIN, msg); + this.uname = msg.uname; + this.pword = msg.pword; + this.os = msg.os; + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putString(this.uname); + writer.putString(this.pword); + writer.putInt(this.unknown01); + writer.putInt(this.unknown02); + writer.putString(this.os); + writer.putInt(this.unknown03); + writer.putInt(this.unknown04); + writer.putInt(this.unknown05); + writer.putInt(this.unknown06); + writer.putInt(this.unknown07); + writer.putInt(this.unknown08); + writer.putShort(this.unknown09); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.uname = reader.getString(); + this.pword = reader.getString(); + + this.unknown01 = reader.monitorInt(0, "ClientLoginInfoMsg 01"); + this.unknown02 = reader.monitorInt(0, "ClientLoginInfoMsg 02"); + + this.os = reader.getString(); + + this.unknown03 = reader.monitorInt(0, "ClientLoginInfoMsg 03"); + this.unknown04 = reader.monitorInt(0, "ClientLoginInfoMsg 04"); + this.unknown05 = reader.monitorInt(0, "ClientLoginInfoMsg 05"); + this.unknown06 = reader.monitorInt(0, "ClientLoginInfoMsg 06"); + this.unknown07 = reader.monitorInt(0, "ClientLoginInfoMsg 07"); + this.unknown08 = reader.monitorInt(0, "ClientLoginInfoMsg 08"); + this.unknown09 = reader + .monitorShort((short) 0, "ClientLoginInfoMsg 09"); + + } + + /** + * @return the uname + */ + public String getUname() { + return uname; + } + + /** + * @return the pword + */ + public String getPword() { + return pword; + } + + /** + * @return the os + */ + public String getOs() { + return os; + } + + /** + * @return the unknown01 + */ + public int getUnknown01() { + return unknown01; + } + + /** + * @param unknown01 + * the unknown01 to set + */ + public void setUnknown01(int unknown01) { + this.unknown01 = unknown01; + } + + /** + * @return the unknown02 + */ + public int getUnknown02() { + return unknown02; + } + + /** + * @param unknown02 + * the unknown02 to set + */ + public void setUnknown02(int unknown02) { + this.unknown02 = unknown02; + } + + /** + * @return the unknown03 + */ + public int getUnknown03() { + return unknown03; + } + + /** + * @param unknown03 + * the unknown03 to set + */ + public void setUnknown03(int unknown03) { + this.unknown03 = unknown03; + } + + /** + * @return the unknown04 + */ + public int getUnknown04() { + return unknown04; + } + + /** + * @param unknown04 + * the unknown04 to set + */ + public void setUnknown04(int unknown04) { + this.unknown04 = unknown04; + } + + /** + * @return the unknown05 + */ + public int getUnknown05() { + return unknown05; + } + + /** + * @param unknown05 + * the unknown05 to set + */ + public void setUnknown05(int unknown05) { + this.unknown05 = unknown05; + } + + /** + * @return the unknown06 + */ + public int getUnknown06() { + return unknown06; + } + + /** + * @param unknown06 + * the unknown06 to set + */ + public void setUnknown06(int unknown06) { + this.unknown06 = unknown06; + } + + /** + * @return the unknown07 + */ + public int getUnknown07() { + return unknown07; + } + + /** + * @param unknown07 + * the unknown07 to set + */ + public void setUnknown07(int unknown07) { + this.unknown07 = unknown07; + } + + /** + * @return the unknown08 + */ + public int getUnknown08() { + return unknown08; + } + + /** + * @param unknown08 + * the unknown08 to set + */ + public void setUnknown08(int unknown08) { + this.unknown08 = unknown08; + } + + /** + * @return the unknown09 + */ + public short getUnknown09() { + return unknown09; + } + + /** + * @param unknown09 + * the unknown09 to set + */ + public void setUnknown09(short unknown09) { + this.unknown09 = unknown09; + } + +} diff --git a/src/engine/net/client/msg/login/CommitNewCharacterMsg.java b/src/engine/net/client/msg/login/CommitNewCharacterMsg.java new file mode 100644 index 00000000..5078e7df --- /dev/null +++ b/src/engine/net/client/msg/login/CommitNewCharacterMsg.java @@ -0,0 +1,457 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.login; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class CommitNewCharacterMsg extends ClientNetMsg { + + private String firstName; + private String lastName; + private int serverID; + private int hairStyle; + private int beardStyle; + private int skinColor; + private int hairColor; + private int beardColor; + private int kit; + private int numRunes; + private int[] runes; + private int numStats; + private int strengthMod; + private int dexterityMod; + private int constitutionMod; + private int intelligenceMod; + private int spiritMod; + + /** + * This is the general purpose constructor. + */ + public CommitNewCharacterMsg() { + super(Protocol.CREATECHAR); + runes = new int[23]; + strengthMod = 0; + dexterityMod = 0; + constitutionMod = 0; + intelligenceMod = 0; + spiritMod = 0; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public CommitNewCharacterMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.CREATECHAR, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + + writer.putString(this.firstName); + writer.putString(this.lastName); + + writer.putInt(this.serverID); + writer.putInt(0); + writer.putInt(this.hairStyle); + writer.putInt(0); + writer.putInt(this.beardStyle); + writer.putInt(this.skinColor); + writer.putInt(this.hairColor); + writer.putInt(this.beardColor); + writer.putInt(this.kit); + for (int i = 0; i < 23; i++) { + writer.putInt(0); + writer.putInt(this.runes[i]); + writer.putInt(0); + writer.putInt(0); + } + writer.putInt(this.numStats); + if (this.strengthMod != 0) { + writer.putInt(0x8AC3C0E6); + writer.putInt(this.strengthMod); + } + if (this.dexterityMod != 0) { + writer.putInt(0xE07B3336); + writer.putInt(this.dexterityMod); + } + if (this.constitutionMod != 0) { + writer.putInt(0xB15DC77E); + writer.putInt(this.constitutionMod); + } + if (this.intelligenceMod != 0) { + writer.putInt(0xFF665EC3); + writer.putInt(this.intelligenceMod); + } + if (this.spiritMod != 0) { + writer.putInt(0xACB82E33); + writer.putInt(this.spiritMod); + } + + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + runes = new int[23]; + runes = new int[23]; + strengthMod = 0; + dexterityMod = 0; + constitutionMod = 0; + intelligenceMod = 0; + spiritMod = 0; + + this.firstName = reader.getString(); + this.lastName = reader.getString(); + this.serverID = reader.getInt(); + + reader.monitorInt(0, "CommitNewCharacter 01"); + + this.hairStyle = reader.getInt(); + + reader.monitorInt(0, "CommitNewCharacter 02"); + + this.beardStyle = reader.getInt(); + this.skinColor = reader.getInt(); + this.hairColor = reader.getInt(); + this.beardColor = reader.getInt(); + this.kit = reader.getInt(); + this.clearRunes(); + int runeCount = 0; + for (int i = 0; i < 23; i++) { + + reader.monitorInt(0, "CommitNewCharacter 03-" + i); + + this.runes[i] = reader.getInt(); + + reader.monitorInt(0, "CommitNewCharacter 04-" + i); + reader.monitorInt(0, "CommitNewCharacter 05-" + i); + + if (this.runes[i] != 0) + runeCount++; + } + this.numRunes = runeCount; + this.numStats = reader.getInt(); + int stattype; + for (int i = 0; i < this.numStats; i++) { + stattype = reader.getInt(); + if (stattype == 0x8AC3C0E6) + this.strengthMod = reader.getInt(); + else if (stattype == 0xE07B3336) + this.dexterityMod = reader.getInt(); + else if (stattype == 0xB15DC77E) + this.constitutionMod = reader.getInt(); + else if (stattype == 0xFF665EC3) + this.intelligenceMod = reader.getInt(); + else if (stattype == 0xACB82E33) + this.spiritMod = reader.getInt(); + } + } + + + public void clearRunes() { + for (int i = 0; i < 23; i++) + this.runes[i] = 0; + } + + public int getRace() { + for (int i = 0; i < 23; i++) + if (this.runes[i] > 1999 && this.runes[i] < 2030) + return this.runes[i]; + return 0; + } + + public int getBaseClass() { + for (int i = 0; i < 23; i++) + if (this.runes[i] > 2499 && this.runes[i] < 2504) + return this.runes[i]; + return 0; + } + + /** + * @return the firstName + */ + public String getFirstName() { + return firstName; + } + + /** + * @param firstName + * the firstName to set + */ + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + /** + * @return the lastName + */ + public String getLastName() { + return lastName; + } + + /** + * @param lastName + * the lastName to set + */ + public void setLastName(String lastName) { + this.lastName = lastName; + } + + /** + * @return the serverID + */ + public int getServerID() { + return serverID; + } + + /** + * @param serverID + * the serverID to set + */ + public void setServerID(int serverID) { + this.serverID = serverID; + } + + /** + * @return the hairStyle + */ + public int getHairStyle() { + return hairStyle; + } + + /** + * @param hairStyle + * the hairStyle to set + */ + public void setHairStyle(int hairStyle) { + this.hairStyle = hairStyle; + } + + /** + * @return the beardStyle + */ + public int getBeardStyle() { + return beardStyle; + } + + /** + * @param beardStyle + * the beardStyle to set + */ + public void setBeardStyle(int beardStyle) { + this.beardStyle = beardStyle; + } + + /** + * @return the skinColor + */ + public int getSkinColor() { + return skinColor; + } + + /** + * @param skinColor + * the skinColor to set + */ + public void setSkinColor(int skinColor) { + this.skinColor = skinColor; + } + + /** + * @return the hairColor + */ + public int getHairColor() { + return hairColor; + } + + /** + * @param hairColor + * the hairColor to set + */ + public void setHairColor(int hairColor) { + this.hairColor = hairColor; + } + + /** + * @return the beardColor + */ + public int getBeardColor() { + return beardColor; + } + + /** + * @param beardColor + * the beardColor to set + */ + public void setBeardColor(int beardColor) { + this.beardColor = beardColor; + } + + /** + * @return the kit + */ + public int getKit() { + return kit; + } + + /** + * @param kit + * the kit to set + */ + public void setKit(int kit) { + this.kit = kit; + } + + /** + * @return the runeCount + */ + public int getNumRunes() { + return numRunes; + } + + /** + * @param numRunes + * the runeCount to set + */ + public void setNumRunes(int numRunes) { + this.numRunes = numRunes; + } + + /** + * @return the runes + */ + public int[] getRunes() { + return runes; + } + + /** + * @param runes + * the runes to set + */ + public void setRunes(int[] runes) { + this.runes = runes; + } + + /** + * @return the numStats + */ + public int getNumStats() { + return numStats; + } + + /** + * @param numStats + * the numStats to set + */ + public void setNumStats(int numStats) { + this.numStats = numStats; + } + + /** + * @return the strengthMod + */ + public int getStrengthMod() { + return strengthMod; + } + + /** + * @param strengthMod + * the strengthMod to set + */ + public void setStrengthMod(int strengthMod) { + this.strengthMod = strengthMod; + } + + /** + * @return the dexterityMod + */ + public int getDexterityMod() { + return dexterityMod; + } + + /** + * @param dexterityMod + * the dexterityMod to set + */ + public void setDexterityMod(int dexterityMod) { + this.dexterityMod = dexterityMod; + } + + /** + * @return the constitutionMod + */ + public int getConstitutionMod() { + return constitutionMod; + } + + /** + * @param constitutionMod + * the constitutionMod to set + */ + public void setConstitutionMod(int constitutionMod) { + this.constitutionMod = constitutionMod; + } + + /** + * @return the intelligenceMod + */ + public int getIntelligenceMod() { + return intelligenceMod; + } + + /** + * @param intelligenceMod + * the intelligenceMod to set + */ + public void setIntelligenceMod(int intelligenceMod) { + this.intelligenceMod = intelligenceMod; + } + + /** + * @return the spiritMod + */ + public int getSpiritMod() { + return spiritMod; + } + + /** + * @param spiritMod + * the spiritMod to set + */ + public void setSpiritMod(int spiritMod) { + this.spiritMod = spiritMod; + } + +} diff --git a/src/engine/net/client/msg/login/DeleteCharacterMsg.java b/src/engine/net/client/msg/login/DeleteCharacterMsg.java new file mode 100644 index 00000000..be4efab2 --- /dev/null +++ b/src/engine/net/client/msg/login/DeleteCharacterMsg.java @@ -0,0 +1,75 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.net.client.msg.login; + +import engine.Enum.GameObjectType; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class DeleteCharacterMsg extends ClientNetMsg { + + private int characterUUID; + private String firstName; + private String serverName; + + /** + * This is the general purpose constructor. + */ + public DeleteCharacterMsg() { + super(Protocol.REMOVECHAR); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public DeleteCharacterMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.REMOVECHAR, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(GameObjectType.PlayerCharacter.ordinal()); + writer.putInt(this.characterUUID); + writer.putString(this.firstName); + writer.putString(this.serverName); + } + + /** + * Deserializes the subclass specific items from the supplied + * ByteBufferReader. + */ + + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); // Object Type Padding + this.characterUUID = reader.getInt(); + this.firstName = reader.getString(); + this.serverName = reader.getString(); + } + + /** + * @return the characterUUID + */ + public int getCharacterUUID() { + return characterUUID; + } + +} diff --git a/src/engine/net/client/msg/login/GameServerIPRequestMsg.java b/src/engine/net/client/msg/login/GameServerIPRequestMsg.java new file mode 100644 index 00000000..5e548e36 --- /dev/null +++ b/src/engine/net/client/msg/login/GameServerIPRequestMsg.java @@ -0,0 +1,63 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.login; + +import engine.Enum.GameObjectType; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class GameServerIPRequestMsg extends ClientNetMsg { + + private int characterUUID; + + /** + * This is the general purpose constructor. + */ + public GameServerIPRequestMsg() { + super(Protocol.SELECTCHAR); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public GameServerIPRequestMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.SELECTCHAR, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(GameObjectType.PlayerCharacter.ordinal()); + writer.putInt(this.characterUUID); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + reader.getInt(); // Object Type Padding + this.characterUUID = reader.getInt(); + } + + /** + * @return the characterUUID + */ + public int getCharacterUUID() { + return characterUUID; + } + +} diff --git a/src/engine/net/client/msg/login/GameServerIPResponseMsg.java b/src/engine/net/client/msg/login/GameServerIPResponseMsg.java new file mode 100644 index 00000000..35061578 --- /dev/null +++ b/src/engine/net/client/msg/login/GameServerIPResponseMsg.java @@ -0,0 +1,85 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.login; + + +import engine.gameManager.ConfigManager; +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class GameServerIPResponseMsg extends ClientNetMsg { + + private String ip; + private int port; + + /** + * This is the general purpose constructor. + */ + public GameServerIPResponseMsg(String ip, int port) { + super(Protocol.GAMESERVERIPRESPONSE); + this.ip = ip; + this.port = port; + } + + /** + * This is the general purpose constructor. + */ + public GameServerIPResponseMsg( ) { + super(Protocol.GAMESERVERIPRESPONSE); + this.ip = ConfigManager.MB_PUBLIC_ADDR.getValue(); + this.port = Integer.parseInt(ConfigManager.MB_WORLD_PORT.getValue()); + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public GameServerIPResponseMsg(AbstractConnection origin, + ByteBufferReader reader) { + super(Protocol.GAMESERVERIPRESPONSE, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putString(this.ip); + writer.putInt(this.port); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.ip = reader.getString(); + this.port = reader.getInt(); + } + + /** + * @return the ip + */ + public String getIp() { + return ip; + } + + /** + * @return the port + */ + public int getPort() { + return port; + } +} diff --git a/src/engine/net/client/msg/login/InvalidNameMsg.java b/src/engine/net/client/msg/login/InvalidNameMsg.java new file mode 100644 index 00000000..10dc4b77 --- /dev/null +++ b/src/engine/net/client/msg/login/InvalidNameMsg.java @@ -0,0 +1,89 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.login; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; +import engine.server.MBServerStatics; + +public class InvalidNameMsg extends ClientNetMsg { + + private String FirstName; + private String LastName; + private int errorCode; + private int serverID; + /** + * This is the general purpose constructor. + */ + public InvalidNameMsg(String FirstName, String LastName, + int errorCode) { + super(Protocol.NAMEVERIFY); + this.FirstName = FirstName; + this.LastName = LastName; + this.errorCode = errorCode; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public InvalidNameMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.NAMEVERIFY, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putString(this.FirstName); + writer.putString(this.LastName); + writer.putInt(MBServerStatics.worldMapID); + writer.putInt(errorCode); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.FirstName = reader.getString(); + this.LastName = reader.getString(); + this.serverID = reader.getInt(); + reader.monitorInt(0, "InvalidNameMsg 01"); + } + + + /** + * @return the firstName + */ + public String getFirstName() { + return FirstName; + } + + /** + * @return the lastName + */ + public String getLastName() { + return LastName; + } + + /** + * @return the errorCode + */ + public int getErrorCode() { + return errorCode; + } + +} diff --git a/src/engine/net/client/msg/login/LoginErrorMsg.java b/src/engine/net/client/msg/login/LoginErrorMsg.java new file mode 100644 index 00000000..9decaaa5 --- /dev/null +++ b/src/engine/net/client/msg/login/LoginErrorMsg.java @@ -0,0 +1,72 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.login; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class LoginErrorMsg extends ClientNetMsg { + + private int reason; + private String message; + + /** + * This is the general purpose constructor. + */ + public LoginErrorMsg(int Reason, String message) { + super(Protocol.LOGINFAILED); + this.reason = Reason; + this.message = message; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public LoginErrorMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.LOGINFAILED, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.reason); + writer.putString(this.message); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.reason = reader.getInt(); + this.message = reader.getString(); + } + + /** + * @return the reason + */ + public int getReason() { + return reason; + } + + /** + * @return the message + */ + public String getMessage() { + return message; + } +} diff --git a/src/engine/net/client/msg/login/ServerStatusMsg.java b/src/engine/net/client/msg/login/ServerStatusMsg.java new file mode 100644 index 00000000..241bf8b2 --- /dev/null +++ b/src/engine/net/client/msg/login/ServerStatusMsg.java @@ -0,0 +1,80 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.login; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class ServerStatusMsg extends ClientNetMsg { + + private int serverID; + private byte isUp; + + /** + * This is the general purpose constructor. + */ + public ServerStatusMsg(int serverID, byte isUp) { + super(Protocol.ARCSERVERSTATUS); + this.serverID = serverID; + this.isUp = isUp; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the + * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading + * past the limit) then this constructor Throws that Exception to the + * caller. + */ + public ServerStatusMsg(AbstractConnection origin, ByteBufferReader reader) { + super(Protocol.ARCSERVERSTATUS, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied NetMsgWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putInt(this.serverID); + writer.put(this.isUp); + } + + /** + * Deserializes the subclass specific items from the supplied NetMsgReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.serverID = reader.getInt(); + this.isUp = reader.get(); + } + + public int getServerID() { + return this.serverID; + } + + public byte getIsUp() { + return this.isUp; + } + + public boolean isUp() { + return (this.isUp == 0x01); + } + + public void setServerID(int value) { + this.serverID = value; + } + + public void setIsUp(byte value) { + this.isUp = value; + } +} diff --git a/src/engine/net/client/msg/login/VersionInfoMsg.java b/src/engine/net/client/msg/login/VersionInfoMsg.java new file mode 100644 index 00000000..bd6eb78a --- /dev/null +++ b/src/engine/net/client/msg/login/VersionInfoMsg.java @@ -0,0 +1,72 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.net.client.msg.login; + + +import engine.net.AbstractConnection; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; + +public class VersionInfoMsg extends ClientNetMsg { + + private String majorVersion; + private String minorVersion; + + /** + * This is the general purpose constructor. + */ + public VersionInfoMsg(String majorVersion, String minorVersion) { + super(Protocol.VERSIONINFO); + this.majorVersion = majorVersion; + this.minorVersion = minorVersion; + } + + /** + * This constructor is used by NetMsgFactory. It attempts to deserialize the ByteBuffer into a message. If a BufferUnderflow occurs (based on reading past the limit) then this constructor Throws that Exception to the caller. + */ + public VersionInfoMsg(AbstractConnection origin, ByteBufferReader reader) + { + super(Protocol.VERSIONINFO, origin, reader); + } + + /** + * Serializes the subclass specific items to the supplied ByteBufferWriter. + */ + @Override + protected void _serialize(ByteBufferWriter writer) { + writer.putString(this.majorVersion); + writer.putString(this.minorVersion); + } + + /** + * Deserializes the subclass specific items from the supplied ByteBufferReader. + */ + @Override + protected void _deserialize(ByteBufferReader reader) { + this.majorVersion = reader.getString(); + this.minorVersion = reader.getString(); + } + + /** + * @return the majorVersion + */ + public String getMajorVersion() { + return majorVersion; + } + + /** + * @return the minorVersion + */ + public String getMinorVersion() { + return minorVersion; + } +} diff --git a/src/engine/objects/AbstractCharacter.java b/src/engine/objects/AbstractCharacter.java new file mode 100644 index 00000000..244f91c8 --- /dev/null +++ b/src/engine/objects/AbstractCharacter.java @@ -0,0 +1,1974 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum; +import engine.Enum.*; +import engine.InterestManagement.InterestManager; +import engine.InterestManagement.WorldGrid; +import engine.exception.SerializationException; +import engine.gameManager.*; +import engine.job.AbstractJob; +import engine.job.JobContainer; +import engine.job.JobScheduler; +import engine.jobs.ChantJob; +import engine.jobs.PersistentAoeJob; +import engine.jobs.TrackJob; +import engine.math.AtomicFloat; +import engine.math.Bounds; +import engine.math.Vector3fImmutable; +import engine.net.ByteBufferWriter; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.msg.MoveToPointMsg; +import engine.powers.EffectsBase; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +public abstract class AbstractCharacter extends AbstractWorldObject { + + protected String firstName; + protected String lastName; + protected short statStrCurrent; + protected short statDexCurrent; + protected short statConCurrent; + protected short statIntCurrent; + protected short statSpiCurrent; + protected short unusedStatPoints; + protected short level; + protected int exp; + protected Vector3fImmutable bindLoc; + protected Vector3fImmutable faceDir; + protected Guild guild; + protected byte runningTrains; + protected ConcurrentHashMap powers; + protected ConcurrentHashMap skills; + protected final CharacterItemManager charItemManager; + + // Variables NOT to be stored in db + protected boolean sit = false; + protected boolean walkMode; + protected boolean combat = false; + + protected Vector3fImmutable startLoc = Vector3fImmutable.ZERO; + protected Vector3fImmutable endLoc = Vector3fImmutable.ZERO; + private float desiredAltitude = 0; + private long takeOffTime = 0; + protected boolean itemCasting = false; + + // nextEndLoc is used to store the next end location when someone is clicking + // around the ground while other timers like changeAltitude are still + // ticking down so that mobs/players following dont just move away to your projected location + protected Vector3fImmutable nextEndLoc = Vector3fImmutable.ZERO; + + protected float speed; + protected AtomicFloat stamina = new AtomicFloat(); + protected float staminaMax; + protected AtomicFloat mana = new AtomicFloat(); + protected float manaMax; // Health/Mana/Stamina + protected AtomicBoolean isAlive = new AtomicBoolean(true); + protected Resists resists = new Resists("Genric"); + protected AbstractWorldObject combatTarget; + protected ConcurrentHashMap timers; + protected ConcurrentHashMap timestamps; + protected int atrHandOne; + protected int atrHandTwo; + protected int minDamageHandOne; + protected int maxDamageHandOne; + protected int minDamageHandTwo; + protected int maxDamageHandTwo; + protected float rangeHandOne; + protected float rangeHandTwo; + protected float speedHandOne; + protected float speedHandTwo; + protected int defenseRating; + protected boolean isActive; // <-Do not use this for deleting character! + protected float altitude = 0; // 0=on terrain, 1=tier 1, 2=tier 2, etc. + protected ConcurrentHashMap recycleTimers; + protected PlayerBonuses bonuses; + protected JobContainer lastChant; + protected boolean isCasting = false; + private final ReentrantReadWriteLock healthLock = new ReentrantReadWriteLock(); + private final ReentrantReadWriteLock teleportLock = new ReentrantReadWriteLock(); + protected long lastSetLocUpdate = 0L; + protected int inBuilding = -1; // -1 not in building 0 on ground floor, 1 on first floor etc + protected int inBuildingID = 0; + protected int inFloorID = -1; + protected int liveCounter = 0; + + protected int debug = 0; + private float hateValue = 0; + private long lastHateUpdate = 0; + private boolean collided = false; + protected Regions lastRegion = null; + + protected boolean movingUp = false; + + + /** + * No Id Constructor + */ + public AbstractCharacter( + final String firstName, + final String lastName, + final short statStrCurrent, + final short statDexCurrent, + final short statConCurrent, + final short statIntCurrent, + final short statSpiCurrent, + final short level, + final int exp, + final Vector3fImmutable bindLoc, + final Vector3fImmutable currentLoc, + final Vector3fImmutable faceDir, + final Guild guild, + final byte runningTrains + ) { + super(); + this.firstName = firstName; + this.lastName = lastName; + + this.statStrCurrent = statStrCurrent; + this.statDexCurrent = statDexCurrent; + this.statConCurrent = statConCurrent; + this.statIntCurrent = statIntCurrent; + this.statSpiCurrent = statSpiCurrent; + this.level = level; + this.exp = exp; + this.walkMode = true; + this.bindLoc = bindLoc; + if (ConfigManager.serverType.equals(Enum.ServerType.WORLDSERVER)) + this.setLoc(currentLoc); + this.faceDir = faceDir; + this.guild = guild; + this.runningTrains = runningTrains; + this.powers = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + this.skills = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + this.initializeCharacter(); + + // Dangerous to use THIS in a constructor!!! + this.charItemManager = new CharacterItemManager(this); + } + + /** + * Normal Constructor + */ + public AbstractCharacter( + final String firstName, + final String lastName, + final short statStrCurrent, + final short statDexCurrent, + final short statConCurrent, + final short statIntCurrent, + final short statSpiCurrent, + final short level, + final int exp, + final Vector3fImmutable bindLoc, + final Vector3fImmutable currentLoc, + final Vector3fImmutable faceDir, + final Guild guild, + final byte runningTrains, + final int newUUID + ) { + + super(newUUID); + this.firstName = firstName; + this.lastName = lastName; + + this.statStrCurrent = statStrCurrent; + this.statDexCurrent = statDexCurrent; + this.statConCurrent = statConCurrent; + this.statIntCurrent = statIntCurrent; + this.statSpiCurrent = statSpiCurrent; + this.level = level; + this.exp = exp; + this.walkMode = true; + + this.bindLoc = bindLoc; + if (ConfigManager.serverType.equals(Enum.ServerType.WORLDSERVER)) + this.setLoc(currentLoc); + this.faceDir = faceDir; + this.guild = guild; + + this.runningTrains = runningTrains; + this.powers = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + this.skills = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + this.initializeCharacter(); + + // Dangerous to use THIS in a constructor!!! + this.charItemManager = new CharacterItemManager(this); + } + + /** + * ResultSet Constructor for players + */ + public AbstractCharacter( + final ResultSet rs, + final boolean isPlayer + ) throws SQLException { + super(rs); + + this.firstName = rs.getString("char_firstname"); + this.lastName = rs.getString("char_lastname"); + + this.level = 1; + this.exp = rs.getInt("char_experience"); + this.walkMode = false; + + this.bindLoc = new Vector3fImmutable(0f, 0f, 0f); + this.endLoc = Vector3fImmutable.ZERO; + + this.faceDir = Vector3fImmutable.ZERO; + + final int guildID = rs.getInt("GuildUID"); + final Guild errantGuild = Guild.getErrantGuild(); + + if (guildID == errantGuild.getObjectUUID()) { + this.guild = errantGuild; + } + else { + this.guild = Guild.getGuild(guildID); + if (this.guild == null) { + this.guild = Guild.getErrantGuild(); + } + } + + if (this.guild == null) + this.guild = errantGuild; + + this.skills = new ConcurrentHashMap<>(); + this.powers = new ConcurrentHashMap<>(); + this.initializeCharacter(); + + // Dangerous to use THIS in a constructor!!! + this.charItemManager = new CharacterItemManager(this); + } + + /** + * ResultSet Constructor for NPC/Mobs + */ + public AbstractCharacter(final ResultSet rs) throws SQLException { + super(rs); + + this.firstName = ""; + this.lastName = ""; + + this.statStrCurrent = (short) 0; + this.statDexCurrent = (short) 0; + this.statConCurrent = (short) 0; + this.statIntCurrent = (short) 0; + this.statSpiCurrent = (short) 0; + + this.unusedStatPoints = (short) 0; + + this.level = (short) 0; // TODO get this from MobsBase later + this.exp = 1; + this.walkMode = true; + + //this.bindLoc = new Vector3fImmutable(rs.getFloat("spawnX"), rs.getFloat("spawnY"), rs.getFloat("spawnZ")); + this.bindLoc = Vector3fImmutable.ZERO; + //setLoc(this.bindLoc); + + this.faceDir = Vector3fImmutable.ZERO; + + this.runningTrains = (byte) 0; + + this.skills = new ConcurrentHashMap<>(); + this.powers = new ConcurrentHashMap<>(); + initializeCharacter(); + + // Dangerous to use THIS in a constructor!!! + this.charItemManager = new CharacterItemManager(this); + } + + /** + * ResultSet Constructor for static Mobs + */ + public AbstractCharacter(final ResultSet rs, final int objectUUID) throws SQLException { + + super(objectUUID); + + this.firstName = ""; + this.lastName = ""; + + this.statStrCurrent = (short) 0; + this.statDexCurrent = (short) 0; + this.statConCurrent = (short) 0; + this.statIntCurrent = (short) 0; + this.statSpiCurrent = (short) 0; + + this.unusedStatPoints = (short) 0; + + this.level = (short) 0; // TODO get this from MobsBase later + this.exp = 1; + this.walkMode = true; + + this.bindLoc = new Vector3fImmutable(rs.getFloat("spawnX"), rs.getFloat("spawnY"), rs.getFloat("spawnZ")); + + if (ConfigManager.serverType.equals(Enum.ServerType.WORLDSERVER)) + this.setLoc(this.bindLoc); + this.endLoc = Vector3fImmutable.ZERO; + + + this.faceDir = Vector3fImmutable.ZERO; + + final int guildID = rs.getInt("GuildID"); + + if (guildID == Guild.getErrantGuild().getObjectUUID()) { + this.guild = Guild.getErrantGuild(); + } + else { + this.guild = Guild.getGuild(guildID); + } + + if (this.guild == null) + this.guild = Guild.getErrantGuild(); + + this.runningTrains = (byte) 0; + this.skills = new ConcurrentHashMap<>(); + this.powers = new ConcurrentHashMap<>(); + + this.initializeCharacter(); + + // Dangerous to use THIS in a constructor!!! + this.charItemManager = new CharacterItemManager(this); + } + + private void initializeCharacter() { + this.timers = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + this.timestamps = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + final long l = System.currentTimeMillis(); + this.timestamps.put("Health Recovery", l); + this.timestamps.put("Stamina Recovery", l); + this.timestamps.put("Mana Recovery", l); + this.recycleTimers = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + } + + protected abstract ConcurrentHashMap initializePowers(); + + private byte aoecntr = 0; + + public final void addPersistantAoe( + final String name, + final int duration, + final PersistentAoeJob asj, + final EffectsBase eb, + final int trains + ) { + if (!isAlive()) { + return; + } + final JobContainer jc = JobScheduler.getInstance().scheduleJob(asj, duration); + final Effect eff = new Effect(jc, eb, trains); + aoecntr++; + this.effects.put(name + aoecntr, eff); + eff.setPAOE(); + } + + public final void setLastChant( + final int duration, + final ChantJob cj + ) { + if (!isAlive()) { + return; + } + if (this.lastChant != null) { + this.lastChant.cancelJob(); + } + this.lastChant = JobScheduler.getInstance().scheduleJob(cj, duration); + } + + + public final void cancelLastChant() { + if (this.lastChant != null) { + this.lastChant.cancelJob(); + this.lastChant = null; + } + } + + public final void cancelLastChantIfSame(final Effect eff) { + if (eff == null || this.lastChant == null) { + return; + } + final AbstractJob aj = this.lastChant.getJob(); + if (aj == null || (!(aj instanceof ChantJob))) { + return; + } + final int token = ((ChantJob) aj).getPowerToken(); + if (eff.getPowerToken() == token && token != 0) { + this.cancelLastChant(); + } + } + + /* + * Getters + */ + public final short getUnusedStatPoints() { + return this.unusedStatPoints; + } + + public final void setUnusedStatPoints(final short value) { + this.unusedStatPoints = value; + } + + public final CharacterItemManager getCharItemManager() { + return this.charItemManager; + } + + public final void setDebug( + final int value, + final boolean toggle + ) { + if (toggle) { + this.debug |= value; //turn on debug + } + else { + this.debug &= ~value; //turn off debug + } + } + + public final boolean getDebug(final int value) { + return ((this.debug & value) != 0); + } + + @Override + public String getName() { + if (this.firstName.length() == 0 && this.lastName.length() == 0) { + return "Unnamed " + '(' + this.getObjectUUID() + ')'; + } + else if (this.lastName.length() == 0) { + return this.getFirstName(); + } + else { + return this.getFirstName() + ' ' + this.getLastName(); + } + } + + public String getFirstName() { + return this.firstName; + } + + public String getLastName() { + return this.lastName; + } + + public void setFirstName(final String name) { + this.firstName = name; + } + + public void setLastName(final String name) { + this.lastName = name; + } + + public final short getStatStrCurrent() { + return this.statStrCurrent; + } + + public final short getStatDexCurrent() { + return this.statDexCurrent; + } + + public final short getStatConCurrent() { + return this.statConCurrent; + } + + public final short getStatIntCurrent() { + return this.statIntCurrent; + } + + public final short getStatSpiCurrent() { + return this.statSpiCurrent; + } + + public final void setStatStrCurrent(final short value) { + this.statStrCurrent = (value < 1) ? (short) 1 : value; + } + + public final void setStatDexCurrent(final short value) { + this.statDexCurrent = (value < 1) ? (short) 1 : value; + } + + public final void setStatConCurrent(final short value) { + this.statConCurrent = (value < 1) ? (short) 1 : value; + } + + public final void setStatIntCurrent(final short value) { + this.statIntCurrent = (value < 1) ? (short) 1 : value; + } + + public final void setStatSpiCurrent(final short value) { + this.statSpiCurrent = (value < 1) ? (short) 1 : value; + } + + public short getLevel() { + return this.level; + } + + public void setLevel(final short value) { + this.level = value; + } + + public final boolean isActive() { + return this.isActive; + } + + public final void setActive(final boolean value) { + this.isActive = value; + } + + public final Resists getResists() { + if (this.resists == null) + return Resists.getResists(0); + return this.resists; + } + + public final void setResists(final Resists value) { + this.resists = value; + } + + public final int getExp() { + return this.exp; + } + + public final void setExp(final int value) { + this.exp = value; + } + + public final void setLastPower(final JobContainer jc) { + if (this.timers != null) { + this.timers.put("LastPower", jc); + } + } + + public final JobContainer getLastPower() { + if (this.timers == null) { + return null; + } + return this.timers.get("LastPower"); + } + + public final void clearLastPower() { + if (this.timers != null) { + this.timers.remove("LastPower"); + } + } + + public final void setLastItem(final JobContainer jc) { + if (this.timers != null) { + this.timers.put("LastItem", jc); + } + } + + public final JobContainer getLastItem() { + if (this.timers == null) { + return null; + } + return this.timers.get("LastItem"); + } + + public final void clearLastItem() { + if (this.timers != null) { + this.timers.remove("LastItem"); + } + } + + public final int getIsSittingAsInt() { + if (!this.isAlive()) { + return 1; + } + + if (this.sit) { + return 4; + } + else { + if (this.isMoving()) + return 7; + else + return 5; + } + } + + public final int getIsWalkingAsInt() { + if (this.walkMode) { + return 1; + } + return 2; + } + + public final int getIsCombatAsInt() { + if (this.combat) { + return 2; + } + return 1; + } + + public final int getIsFlightAsInt() { + if (this.altitude > 0) { + return 3; + } + + if (this.getObjectType().equals(GameObjectType.PlayerCharacter)) + if (((PlayerCharacter)this).isLastSwimming()) + return 1; //swimming + + return 2; //ground + } + + + public final void clearTimer(final String name) { + if (this.timers != null) { + this.timers.remove(name); + } + } + + public abstract Vector3fImmutable getBindLoc(); + + + public final Vector3fImmutable getFaceDir() { + return this.faceDir; + } + + public final Vector3fImmutable getStartLoc() { + return this.startLoc; + } + + public final Vector3fImmutable getEndLoc() { + return this.endLoc; + } + + public final Vector3fImmutable getNextEndLoc() { + // this is only used when users are changing their end + // location while a timer like changeAltitude is ticking down + return this.nextEndLoc; + } + + public final void stopMovement(Vector3fImmutable stopLoc) { + + + locationLock.writeLock().lock(); + + try{ + this.setLoc(stopLoc); + this.endLoc = Vector3fImmutable.ZERO; + this.resetLastSetLocUpdate(); + }catch(Exception e){ + Logger.error(e); + }finally{ + locationLock.writeLock().unlock(); + } + } + + public final boolean isMoving() { + + // I might be on my way but my movement is paused + // due to a flight alt change + //TODO who the fuck wrote changeHeightJob. FIX THIS. + + + if (this.endLoc.equals(Vector3fImmutable.ZERO)) + return false; + + if (this.takeOffTime != 0) + return false; + + if (this.isCasting && this.getObjectType().equals(GameObjectType.PlayerCharacter)) + return false; + + return true; + } + + + public final boolean useFlyMoveRegen() { + + + if (this.endLoc.x != 0 && this.endLoc.z != 0) + return true; + + return false; + } + + public boolean asciiLastName() { + return true; + } + + public final ConcurrentHashMap getSkills() { + return this.skills; + } + + public final ConcurrentHashMap getPowers() { + return this.powers; + } + + public final int getInBuilding() { + return this.inBuilding; + } + + public Guild getGuild() { + return this.guild; + } + + public int getGuildUUID() { + return this.guild.getObjectUUID(); + } + + + public final int getRank() { + return (this.level / 10); + } + + public final int getAtrHandOne() { + return this.atrHandOne; + } + + public final int getAtrHandTwo() { + return this.atrHandTwo; + } + + public final int getMinDamageHandOne() { + return this.minDamageHandOne; + } + + public final int getMaxDamageHandOne() { + return this.maxDamageHandOne; + } + + public final int getMinDamageHandTwo() { + return this.minDamageHandTwo; + } + + public final int getMaxDamageHandTwo() { + return this.maxDamageHandTwo; + } + + public final int getDefenseRating() { + return this.defenseRating; + } + + public final float getRangeHandOne() { + return this.rangeHandOne; + } + + public final float getRangeHandTwo() { + return this.rangeHandTwo; + } + + public final float getSpeedHandOne() { + return this.speedHandOne; + } + + public final float getSpeedHandTwo() { + return this.speedHandTwo; + } + + public final float getRange() { + + // Treb range does not appear to be set here + // what gives? + + if (this.getObjectType() == GameObjectType.Mob) { + Mob mob = (Mob) this; + if (mob.isSiege()) { + return 300; + } + } + if (this.rangeHandOne > this.rangeHandTwo) { + return this.rangeHandOne; + } + return this.rangeHandTwo; + } + + public abstract float getPassiveChance( + final String type, + final int attackerLevel, + final boolean fromCombat); + + public abstract float getSpeed(); + + public static int getBankCapacity() { + return 500; + } + + public final int getBankCapacityRemaining() { + return (AbstractCharacter.getBankCapacity() - this.charItemManager.getBankWeight()); + } + + public static int getVaultCapacity() { + return 5000; + } + + public final int getVaultCapacityRemaining() { + return (AbstractCharacter.getVaultCapacity() - this.charItemManager.getVaultWeight()); + } + + public final ArrayList getInventory() { + return this.getInventory(false); + } + + public final ArrayList getInventory(final boolean getGold) { + if (this.charItemManager == null) { + return new ArrayList<>(); + } + return this.charItemManager.getInventory(getGold); + } + + @Override + public Vector3fImmutable getLoc() { + + return super.getLoc(); + } + + public Vector3fImmutable getMovementLoc() { + + if (this.endLoc.equals(Vector3fImmutable.ZERO)) + return super.getLoc(); + if (this.takeOffTime != 0) + return super.getLoc(); + + return super.getLoc().moveTowards(this.endLoc, this.getSpeed() * ((System.currentTimeMillis() - lastSetLocUpdate) * .001f)); + + } + + /* + * Setters + */ + public void setGuild(final Guild value) { + this.guild = value; + } + + public final void setBindLoc(final float x, final float y, final float z) { + this.bindLoc = new Vector3fImmutable(x, y, z); + } + + public final void setEndLoc(final Vector3fImmutable value) { + if(value.x > MBServerStatics.MAX_PLAYER_X_LOC) + return; + if (value.z < MBServerStatics.MAX_PLAYER_Y_LOC) + return; + + this.endLoc = value; + // reset the location timer so our next call to getLoc is correct + this.resetLastSetLocUpdate(); + } + + public final void resetLastSetLocUpdate() { + this.lastSetLocUpdate = System.currentTimeMillis(); + } + + public final void setBindLoc(final Vector3fImmutable value) { + this.bindLoc = value; + } + + @Override + public final void setLoc(final Vector3fImmutable value) { + super.setLoc(value); // set the location in the world + this.resetLastSetLocUpdate(); + //Logger.info("AbstractCharacter", "Setting char location to :" + value.getX() + " " + value.getZ()); + } + + public final void setFaceDir(final Vector3fImmutable value) { + this.faceDir = value; + } + + public void setIsCasting(final boolean isCasting) { + this.isCasting = isCasting; + } + + public final boolean isCasting() { + return this.isCasting; + } + + @Override + public final boolean isAlive() { + return this.isAlive.get(); + } + + public final boolean isSafeMode() { + + if (this.resists == null) + return false; + + for (Effect eff: this.getEffects().values()){ + if (eff.getEffectToken() == -1661750486) + return true; + } + return this.resists.immuneToAll(); + } + + public abstract void killCharacter(final AbstractCharacter killer); + + public abstract void killCharacter(final String reason); + + /** + * Determines if the character is in a lootable state. + * + * @return True if lootable. + */ + public abstract boolean canBeLooted(); + /* + * Utils + */ + + public float calcHitBox() { + if (this.getObjectType() == GameObjectType.PlayerCharacter) { + // hit box radius is str/100 (gets diameter of hitbox) /2 (as we want a radius) + // note this formula is guesswork + if (MBServerStatics.COMBAT_TARGET_HITBOX_DEBUG) { + Logger.info( "Hit box radius for " + this.getFirstName() + " is " + (this.statStrCurrent / 200f)); + } + return ((PlayerCharacter) this).getStrForClient() / 200f; + //TODO CALCULATE MOB HITBOX BECAUSE FAIL EMU IS FAIL!!!!!!! + } + else if (this.getObjectType() == GameObjectType.Mob) { + if (MBServerStatics.COMBAT_TARGET_HITBOX_DEBUG) { + Logger.info("Hit box radius for " + this.getFirstName() + " is " + ((Mob) this).getMobBase().getHitBoxRadius()); + } + return ((Mob) this).getMobBase().getHitBoxRadius(); + } + return 0f; + } + + public final boolean isSit() { + return this.sit; + } + + public final boolean isWalk() { + return this.walkMode; + } + + public final boolean isCombat() { + return this.combat; + } + + public final void setSit(final boolean value) { + + if (this.sit != value) { + // change sit/stand and sync location + this.sit = value; + if (value == true) // we have been told to sit + { + this.stopMovement(this.getLoc()); + } + } + + } + + public final void setWalkMode(final boolean value) { + // sync movement location as getLoc gets where we are at the exact moment in time (i.e. not the last updated loc) + this.setLoc(this.getLoc()); + if (this.walkMode == value) { + return; + } + else { + this.walkMode = value; + } + } + + public final void setCombat(final boolean value) { + this.combat = value; + } + + public final void setInBuilding(final int floor) { + this.inBuilding = floor; + } + + public final AbstractWorldObject getCombatTarget() { + return this.combatTarget; + } + + public final void setCombatTarget(final AbstractWorldObject value) { + this.combatTarget = value; + } + + public final ConcurrentHashMap getTimers() { + if (this.timers == null) { + this.timers = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + } + return this.timers; + } + + public final int getLiveCounter() { + return this.liveCounter; + } + + public final void addTimer( + final String name, + final AbstractJob asj, + final int duration + ) { + final JobContainer jc = JobScheduler.getInstance().scheduleJob(asj, duration); + if (this.timers == null) { + this.timers = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + } + this.timers.put(name, jc); + } + + public final void renewTimer( + final String name, + final AbstractJob asj, + final int duration + ) { + this.cancelTimer(name); + this.addTimer(name, asj, duration); + } + + public final ConcurrentHashMap getRecycleTimers() { + return this.recycleTimers; + } + + public final ConcurrentHashMap getTimestamps() { + return this.timestamps; + } + + public final long getTimeStamp(final String name) { + if (this.timestamps.containsKey(name)) { + return this.timestamps.get(name); + } + return 0L; + } + + public final void setTimeStamp(final String name, final long value) { + this.timestamps.put(name, value); + } + + public final void setTimeStampNow(final String name) { + this.timestamps.put(name, System.currentTimeMillis()); + } + + public final void cancelTimer(final String name) { + cancelTimer(name, true); + } + + public final void cancelTimer(final String name, final boolean jobRunning) { + if (this.timers == null) { + this.timers = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + } + if (this.timers.containsKey(name)) { + if (jobRunning) { + this.timers.get(name).cancelJob(); + } + this.timers.remove(name); + } + } + + public final float modifyHealth( + final float value, + final AbstractCharacter attacker, + final boolean fromCost) { + + try{ + + try{ + boolean ready = this.healthLock.writeLock().tryLock(1, TimeUnit.SECONDS); + + while (!ready) + ready = this.healthLock.writeLock().tryLock(1, TimeUnit.SECONDS); + + if (!this.isAlive()) + return 0; + + Float oldHealth, newHealth; + + if (!this.isAlive()) + return 0f; + + oldHealth = this.health.get(); + newHealth = oldHealth + value; + + if (newHealth > this.healthMax) + newHealth = healthMax; + + this.health.set(newHealth); + + if (newHealth <= 0) { + if (this.isAlive.compareAndSet(true, false)) { + killCharacter(attacker); + return newHealth - oldHealth; + } + else + return 0f; //already dead, don't send damage again + } // past this lock! + + //TODO why is Handle REtaliate and cancelontakedamage in modifyHealth? shouldnt this be outside this method? + if (value < 0f && !fromCost) { + this.cancelOnTakeDamage(); + CombatManager.handleRetaliate(this, attacker); + } + + return newHealth - oldHealth; + }finally{ + this.healthLock.writeLock().unlock(); + } + + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return 0; + } + + public float getCurrentHitpoints() { + return this.health.get(); + } + + public final float modifyMana( + final float value, + final AbstractCharacter attacker + ) { + return this.modifyMana(value, attacker, false); + } + + public final float modifyMana( + final float value, + final AbstractCharacter attacker, + final boolean fromCost + ) { + + if (!this.isAlive()) { + return 0f; + } + boolean worked = false; + Float oldMana = 0f, newMana = 0f; + while (!worked) { + oldMana = this.mana.get(); + newMana = oldMana + value; + if (newMana > this.manaMax) { + newMana = manaMax; + } + else if (newMana < 0) { + newMana = 0f; + } + worked = this.mana.compareAndSet(oldMana, newMana); + } + if (value < 0f && !fromCost) { + this.cancelOnTakeDamage(); + CombatManager.handleRetaliate(this, attacker); + } + return newMana - oldMana; + } + + public final float modifyStamina( + final float value, + final AbstractCharacter attacker + ) { + return this.modifyStamina(value, attacker, false); + } + + public final float modifyStamina( + final float value, + final AbstractCharacter attacker, + final boolean fromCost + ) { + + if (!this.isAlive()) { + return 0f; + } + boolean worked = false; + Float oldStamina = 0f, newStamina = 0f; + while (!worked) { + oldStamina = this.stamina.get(); + newStamina = oldStamina + value; + if (newStamina > this.staminaMax) { + newStamina = staminaMax; + } + else if (newStamina < 0) { + newStamina = 0f; + } + worked = this.stamina.compareAndSet(oldStamina, newStamina); + } + if (value < 0f && !fromCost) { + this.cancelOnTakeDamage(); + CombatManager.handleRetaliate(this, attacker); + } + return newStamina - oldStamina; + } + + public final float setMana( + final float value, + final AbstractCharacter attacker + ) { + return setMana(value, attacker, false); + } + + public final float setMana( + final float value, + final AbstractCharacter attacker, + final boolean fromCost + ) { + + if (!this.isAlive()) { + return 0f; + } + boolean worked = false; + Float oldMana = 0f, newMana = 0f; + while (!worked) { + oldMana = this.mana.get(); + newMana = value; + if (newMana > this.manaMax) { + newMana = manaMax; + } + else if (newMana < 0) { + newMana = 0f; + } + worked = this.mana.compareAndSet(oldMana, newMana); + } + if (oldMana > newMana && !fromCost) { + this.cancelOnTakeDamage(); + CombatManager.handleRetaliate(this, attacker); + } + return newMana - oldMana; + } + + public final float setStamina( + final float value, + final AbstractCharacter attacker + ) { + return setStamina(value, attacker, false); + } + + public final float setStamina( + final float value, + final AbstractCharacter attacker, + final boolean fromCost + ) { + + if (!this.isAlive()) { + return 0f; + } + boolean worked = false; + Float oldStamina = 0f, newStamina = 0f; + while (!worked) { + oldStamina = this.stamina.get(); + newStamina = value; + if (newStamina > this.staminaMax) { + newStamina = staminaMax; + } + else if (newStamina < 0) { + newStamina = 0f; + } + worked = this.stamina.compareAndSet(oldStamina, newStamina); + } + if (oldStamina > newStamina && !fromCost) { + this.cancelOnTakeDamage(); + CombatManager.handleRetaliate(this, attacker); + } + return newStamina - oldStamina; + + } + + public final float getStamina() { + if (this.getObjectType() == GameObjectType.Mob) + return this.getStaminaMax(); + return this.stamina.get(); + } + + public final float getMana() { + if (this.getObjectType() == GameObjectType.Mob) + return this.getManaMax(); + return this.mana.get(); + } + + public final float getStaminaMax() { + if (this.getObjectType() == GameObjectType.Mob) + return 2000; + return this.staminaMax; + } + + public final float getManaMax() { + if (this.getObjectType() == GameObjectType.Mob) + return 2000; + return this.manaMax; + } + + public final PlayerBonuses getBonuses() { + return this.bonuses; + } + + public void teleport(final Vector3fImmutable targetLoc) { + locationLock.writeLock().lock(); + try{ + MovementManager.translocate(this, targetLoc, null); + MovementManager.sendRWSSMsg(this); + }catch(Exception e){ + Logger.error(e); + }finally{ + locationLock.writeLock().unlock(); + } + } + + + public void teleportToObject(final AbstractWorldObject worldObject) { + locationLock.writeLock().lock(); + try{ + MovementManager.translocateToObject(this, worldObject); + }catch(Exception e){ + Logger.error(e); + }finally{ + locationLock.writeLock().unlock(); + } + } + + + + /* + * Serializing + */ + + public static void _serializeForClientMsg(AbstractCharacter abstractCharacter,final ByteBufferWriter writer) throws SerializationException { + AbstractCharacter.__serializeForClientMsg(abstractCharacter,writer); + } + + public static void __serializeForClientMsg(AbstractCharacter abstractCharacter,final ByteBufferWriter writer) throws SerializationException { + } + + + public static void serializeForClientMsgOtherPlayer(AbstractCharacter abstractCharacter,final ByteBufferWriter writer) throws SerializationException { + } + + public static void serializeForClientMsgOtherPlayer(AbstractCharacter abstractCharacter,final ByteBufferWriter writer, final boolean asciiLastName) throws SerializationException { + + switch (abstractCharacter.getObjectType()){ + case PlayerCharacter: + PlayerCharacter.serializePlayerForClientMsgOtherPlayer((PlayerCharacter)abstractCharacter, writer, asciiLastName); + break; + case Mob: + Mob.serializeMobForClientMsgOtherPlayer((Mob)abstractCharacter, writer,asciiLastName); + break; + case NPC: + NPC.serializeNpcForClientMsgOtherPlayer((NPC)abstractCharacter, writer, asciiLastName); + break; + } + + + //TODO INPUT SWITCH CASE ON GAME OBJECTS TO CALL SPECIFIC METHODS. + } + + public static final void serializeForTrack(AbstractCharacter abstractCharacter, final ByteBufferWriter writer, boolean isGroup) { + writer.putInt(abstractCharacter.getObjectType().ordinal()); + writer.putInt(abstractCharacter.getObjectUUID()); + + if (abstractCharacter.getObjectType().equals(GameObjectType.PlayerCharacter)) { + writer.putString(abstractCharacter.getName()); + } + else { + writer.putString(abstractCharacter.getFirstName()); + } + writer.put(isGroup ? (byte) 1 : (byte) 0); + if (abstractCharacter.guild != null) { + Guild.serializeForTrack(abstractCharacter.guild,writer); + } + else { + Guild.serializeErrantForTrack(writer); + } + } + + /* + * Cancel effects upon actions + */ + public final void cancelOnAttack() { // added to one spot + + boolean changed = false; + + for (String s : this.effects.keySet()) { + + Effect eff = this.effects.get(s); + + if (eff == null) + continue; + if (eff.cancelOnAttack() && eff.cancel()) { + eff.cancelJob(); + this.effects.remove(s); + changed = true; + } + } + + if (changed) { + applyBonuses(); + } + + PowersManager.cancelOnAttack(this); + } + + public final void cancelOnAttackSwing() { // added + boolean changed = false; + for (String s : this.effects.keySet()) { + Effect eff = this.effects.get(s); + if (eff == null) + continue; + if (eff.cancelOnAttackSwing() && eff.cancel()) { + //System.out.println("canceling on AttackSwing"); + eff.cancelJob(); + this.effects.remove(s); + changed = true; + } + } + if (changed) { + applyBonuses(); + } + PowersManager.cancelOnAttackSwing(this); + } + + public final void cancelOnCast() { + boolean changed = false; + for (String s : this.effects.keySet()) { + Effect eff = this.effects.get(s); + + if (eff == null) + continue; + if (eff.cancelOnCast() && eff.cancel()) { + + // Don't cancel the track effect on the character being tracked + if (eff.getJob() != null && eff.getJob() instanceof TrackJob) { + if (((TrackJob) eff.getJob()).getSource().getObjectUUID() + == this.getObjectUUID()) { + continue; + } + } + + //System.out.println("canceling on Cast"); + eff.cancelJob(); + this.effects.remove(s); + changed = true; + } + } + if (changed) { + applyBonuses(); + } + PowersManager.cancelOnCast(this); + } + + public final void cancelOnSpell() { + boolean changed = false; + for (String s : this.effects.keySet()) { + Effect eff = this.effects.get(s); + if (eff == null) + continue; + if (eff.cancelOnCastSpell() && eff.cancel()) { + //System.out.println("canceling on CastSpell"); + eff.cancelJob(); + this.effects.remove(s); + changed = true; + } + } + if (changed) { + applyBonuses(); + } + PowersManager.cancelOnSpell(this); + } + + public final void cancelOnMove() { // added + boolean changed = false; + for (String s : this.effects.keySet()) { + Effect eff = this.effects.get(s); + if (eff == null) + continue; + if (eff.cancelOnMove() && eff.cancel()) { + //System.out.println("canceling on Move"); + eff.cancelJob(); + this.effects.remove(s); + changed = true; + } + } + if (changed) { + applyBonuses(); + } + PowersManager.cancelOnMove(this); + } + + public final void cancelOnSit() { // added + boolean changed = false; + for (String s : this.effects.keySet()) { + Effect eff = this.effects.get(s); + if (eff == null) + continue; + if (eff.cancelOnSit() && eff.cancel()) { + //System.out.println("canceling on Sit"); + eff.cancelJob(); + this.effects.remove(s); + changed = true; + } + } + if (changed) { + applyBonuses(); + } + PowersManager.cancelOnSit(this); + } + + public final void cancelOnTakeDamage() { + boolean changed = false; + for (String s : this.effects.keySet()) { + Effect eff = this.effects.get(s); + if (eff == null) + continue; + if (eff.cancelOnTakeDamage() && eff.cancel()) { + //System.out.println("canceling on Take Damage"); + eff.cancelJob(); + this.effects.remove(s); + changed = true; + } + } + if (changed) { + applyBonuses(); + } + PowersManager.cancelOnTakeDamage(this); + } + + public final void cancelOnTakeDamage(final DamageType type, final float amount) { + boolean changed = false; + for (String s : this.effects.keySet()) { + Effect eff = this.effects.get(s); + if (eff == null) + continue; + if (eff.cancelOnTakeDamage(type, amount) && eff.cancel()) { + eff.cancelJob(); + this.effects.remove(s); + changed = true; + } + } + if (changed) { + applyBonuses(); + } + } + + public final Effect getDamageAbsorber() { + for (String s : this.effects.keySet()) { + Effect eff = this.effects.get(s); + if (eff == null) + continue; + if (eff.isDamageAbsorber()) { + return eff; + } + } + return null; + } + + public final void cancelOnUnEquip() { + boolean changed = false; + for (String s : this.effects.keySet()) { + Effect eff = this.effects.get(s); + if (eff == null) + continue; + if (eff.cancelOnUnEquip() && eff.cancel()) { + //System.out.println("canceling on UnEquip"); + eff.cancelJob(); + this.effects.remove(s); + changed = true; + } + } + if (changed) { + applyBonuses(); + } + PowersManager.cancelOnUnEquip(this); + } + + public final void cancelOnStun() { + boolean changed = false; + for (String s : this.effects.keySet()) { + Effect eff = this.effects.get(s); + + if (eff == null){ + Logger.error("null effect for " + this.getObjectUUID() + " : effect " + s); + continue; + } + if (eff.cancelOnStun() && eff.cancel()) { + //System.out.println("canceling on Stun"); + eff.cancelJob(); + this.effects.remove(s); + changed = true; + } + } + if (changed) { + applyBonuses(); + } + PowersManager.cancelOnStun(this); + } + + //Call to apply any new effects to player + public synchronized void applyBonuses() { + //tell the player to applyBonuses because something has changed + + //start running the bonus calculations + try{ + runBonuses(); + }catch(Exception e){ + Logger.error("Error in run bonuses for object UUID " + this.getObjectUUID()); + Logger.error(e); + } + } + + //Don't call this function directly. linked from ac.applyBonuses() + //through BonusCalcJob. Designed to only run from one worker thread + public final void runBonuses() { + // synchronized with getBonuses() + synchronized (this.bonuses) { + try { + //run until no new bonuses are applied + + // clear bonuses and reapply rune bonuses + if (this.getObjectType().equals(GameObjectType.PlayerCharacter)) { + this.bonuses.calculateRuneBaseEffects((PlayerCharacter) this); + } + else { + this.bonuses.clearRuneBaseEffects(); + } + + // apply effect bonuses + for (Effect eff : this.effects.values()) { + eff.applyBonus(this); + } + + //apply item bonuses for equipped items + ConcurrentHashMap equip = null; + + if (this.charItemManager != null) { + equip = this.charItemManager.getEquipped(); + } + if (equip != null) { + for (Item item : equip.values()) { + item.clearBonuses(); + if (item != null) { + ConcurrentHashMap effects = item.getEffects(); + if (effects != null) { + for (Effect eff : effects.values()) { + eff.applyBonus(item, this); + } + } + } + } + } + + //recalculate passive defenses + if (this.getObjectType().equals(GameObjectType.PlayerCharacter)) { + ((PlayerCharacter) this).setPassives(); + } + + + + // recalculate everything + if (this.getObjectType().equals(GameObjectType.PlayerCharacter)) { + PlayerCharacter pc = (PlayerCharacter) this; + + //calculate item bonuses + pc.calculateItemBonuses(); + + //recalculate formulas + pc.recalculatePlayerStats(true); + + + } + else if (this.getObjectType().equals(GameObjectType.Mob)) { + Mob mob = (Mob) this; + + //recalculate formulas + mob.recalculateStats(); + } + } catch (Exception e) { + Logger.error( e); + } + } + } + + public static void runBonusesOnLoad(PlayerCharacter pc) { + // synchronized with getBonuses() + synchronized (pc.bonuses) { + try { + //run until no new bonuses are applied + + // clear bonuses and reapply rune bonuses + if (pc.getObjectType() == GameObjectType.PlayerCharacter) { + pc.bonuses.calculateRuneBaseEffects(pc); + } + else { + pc.bonuses.clearRuneBaseEffects(); + } + + // apply effect bonuses + for (Effect eff : pc.effects.values()) { + eff.applyBonus(pc); + } + + //apply item bonuses for equipped items + ConcurrentHashMap equip = null; + + if (pc.charItemManager != null) + equip = pc.charItemManager.getEquipped(); + + if (equip != null) { + for (Item item : equip.values()) { + item.clearBonuses(); + if (item != null) { + ConcurrentHashMap effects = item.getEffects(); + if (effects != null) { + for (Effect eff : effects.values()) { + eff.applyBonus(item, pc); + } + } + } + } + } + + //recalculate passive defenses + pc.setPassives(); + + //flip the active bonus set for synchronization purposes + //do this after all bonus updates, but before recalculations. + // recalculate everything + //calculate item bonuses + pc.calculateItemBonuses(); + + //recalculate formulas + PlayerCharacter.recalculatePlayerStatsOnLoad(pc); + + } catch (Exception e) { + Logger.error( e); + } + + } + // TODO remove later, for debugging. + //this.bonuses.printBonuses(); + + } + + public int getInBuildingID() { + return inBuildingID; + } + + public void setInBuildingID(int inBuildingID) { + this.inBuildingID = inBuildingID; + } + + public float getHateValue() { + if (this.hateValue <= 0) { + this.hateValue = 0; + return hateValue; + } + + if (this.lastHateUpdate == 0) { + this.lastHateUpdate = System.currentTimeMillis(); + return this.hateValue; + } + long duration = System.currentTimeMillis() - this.lastHateUpdate; + //convert duration to seconds and multiply Hate Delimiter. + float modAmount = duration / 1000 * MBServerStatics.PLAYER_HATE_DELIMITER; + this.hateValue -= modAmount; + this.lastHateUpdate = System.currentTimeMillis(); + return this.hateValue; + } + + public void setHateValue(float hateValue) { + this.lastHateUpdate = System.currentTimeMillis(); + this.hateValue = hateValue; + } + + public int getInFloorID() { + return inFloorID; + } + + public void setInFloorID(int inFloorID) { + this.inFloorID = inFloorID; + } + + public boolean isCollided() { + return collided; + } + + public void setCollided(boolean collided) { + this.collided = collided; + } + + + public static void SetBuildingLevelRoom(AbstractCharacter character, int buildingID, int buildingLevel, int room, Regions region){ + character.inBuildingID = buildingID; + character.inBuilding = buildingLevel; + character.inFloorID = room; + character.lastRegion = region; + } + + public static Regions InsideBuildingRegion(AbstractCharacter player){ + + Regions currentRegion = null; + HashSet buildings = WorldGrid.getObjectsInRangePartial(player, 300, MBServerStatics.MASK_BUILDING); + + for (AbstractWorldObject awo: buildings){ + + Building building = (Building)awo; + + if (building.getBounds() == null) + continue; + + if (building.getBounds().getRegions() == null) + continue; + + for (Regions region : building.getBounds().getRegions()){ + //TODO ADD NEW REGION CODE + } + } + return currentRegion; + } + + public static Regions InsideBuildingRegionGoingDown(AbstractCharacter player){ + + HashSet buildings = WorldGrid.getObjectsInRangePartial(player, 1000, MBServerStatics.MASK_BUILDING); + + Regions tempRegion = null; + for (AbstractWorldObject awo: buildings){ + + Building building = (Building)awo; + if (building.getBounds() == null) + continue; + + if (!Bounds.collide(player.getLoc(), building.getBounds())) + continue; + + for (Regions region : building.getBounds().getRegions()){ + + if (!region.isPointInPolygon(player.getLoc())) + continue; + + if (!region.isOutside()) + continue; + if (tempRegion == null) + tempRegion = region; + + if (tempRegion.highLerp.y < region.highLerp.y) + tempRegion = region; + } + + if (tempRegion != null) + break; + } + return tempRegion; + } + + public float getDesiredAltitude() { + return desiredAltitude; + } + + public void setDesiredAltitude(float desiredAltitude) { + this.desiredAltitude = desiredAltitude; + } + + + public long getTakeOffTime() { + return takeOffTime; + } + + public void setTakeOffTime(long takeOffTime) { + this.takeOffTime = takeOffTime; + } + + public static boolean CanFly(AbstractCharacter flyer){ + boolean canFly = false; + PlayerBonuses bonus = flyer.getBonuses(); + + if (bonus != null && !bonus.getBool(ModType.NoMod, SourceType.Fly) && bonus.getBool(ModType.Fly,SourceType.None) && flyer.isAlive()) + canFly = true; + + return canFly; + + } + + public boolean isItemCasting() { + return itemCasting; + } + + public void setItemCasting(boolean itemCasting) { + this.itemCasting = itemCasting; + } + + public static void MoveInsideBuilding(PlayerCharacter source, AbstractCharacter ac){ + MoveToPointMsg moveMsg = new MoveToPointMsg(); + moveMsg.setPlayer(ac); + moveMsg.setTarget(ac, BuildingManager.getBuildingFromCache(ac.inBuildingID)); + + Dispatch dispatch = Dispatch.borrow(source, moveMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + + } + + //updates + public void update(){ + } + public void updateRegen(){ + } + public void updateMovementState(){ + } + public void updateLocation(){ + } + public void updateFlight(){ + } + + + public void dynamicUpdate(UpdateType updateType){ + if (this.updateLock.writeLock().tryLock()){ + try{ + switch(updateType){ + case ALL: + update(); + break; + case REGEN: + updateRegen(); + break; + case LOCATION: + update(); + break; + case MOVEMENTSTATE: + update(); + break; + case FLIGHT: + updateFlight(); + break; + } + + }catch(Exception e){ + Logger.error(e); + }finally{ + this.updateLock.writeLock().unlock(); + } + } + + } + + public Regions getLastRegion() { + return lastRegion; + } + + public boolean isMovingUp() { + return movingUp; + } + + public void setMovingUp(boolean movingUp) { + this.movingUp = movingUp; + } + public static void UpdateRegion(AbstractCharacter worldObject){ + worldObject.region = AbstractWorldObject.GetRegionByWorldObject(worldObject); + } + + public static void teleport(AbstractCharacter worldObject, final Vector3fImmutable targetLoc) { + worldObject.locationLock.writeLock().lock(); + try{ + MovementManager.translocate(worldObject, targetLoc,null); + if (worldObject.getObjectType().equals(GameObjectType.PlayerCharacter)) + InterestManager.INTERESTMANAGER.HandleLoadForTeleport((PlayerCharacter)worldObject); + }catch(Exception e){ + Logger.error(e); + }finally{ + worldObject.locationLock.writeLock().unlock(); + } + } +} diff --git a/src/engine/objects/AbstractGameObject.java b/src/engine/objects/AbstractGameObject.java new file mode 100644 index 00000000..aad22fbf --- /dev/null +++ b/src/engine/objects/AbstractGameObject.java @@ -0,0 +1,235 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum.GameObjectType; +import engine.gameManager.BuildingManager; +import engine.gameManager.DbManager; +import engine.job.JobContainer; +import engine.job.JobScheduler; +import engine.jobs.DatabaseUpdateJob; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.concurrent.ConcurrentHashMap; + + +public abstract class AbstractGameObject { + private GameObjectType objectType = GameObjectType.unknown; + private int objectUUID; + + private byte ver = 1; + + private ConcurrentHashMap databaseJobs = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + + /** + * No Table ID Constructor + */ + public AbstractGameObject() { + super(); + setObjectType(); + this.objectUUID = MBServerStatics.NO_DB_ROW_ASSIGNED_YET; + } + + /** + * Normal Constructor + */ + public AbstractGameObject(int objectUUID) { + this(); + this.objectUUID = objectUUID; + } + + /** + * ResultSet Constructor + * + * @param rs + * ResultSet containing record for this object + */ + public AbstractGameObject(ResultSet rs, int objectUUID) throws SQLException { + this(); + this.objectUUID = objectUUID; + } + + /** + * ResultSet Constructor; assumes first column in ResultSet is ID + * + * @param rs + * ResultSet containing record for this object + */ + public AbstractGameObject(ResultSet rs) throws SQLException { + this(rs, rs.getInt(1)); + } + + /* + * Getters + */ + public GameObjectType getObjectType() { + return this.objectType; + } + + protected final void setObjectType() { + try { + this.objectType = GameObjectType.valueOf(this.getClass().getSimpleName()); + } catch (SecurityException | IllegalArgumentException e) { + Logger.error("Failed to find class " + this.getClass().getSimpleName() + + " in GameObjectTypes file. Defaulting ObjectType to 0."); + } + } + + public int getObjectUUID() { + return this.objectUUID; + } + + protected void setObjectUUID(int objectUUID) { + this.objectUUID = objectUUID; + } + + public byte getVer() { + return this.ver; + } + + public void incVer() { + this.ver++; + if (this.ver == (byte)-1) //-1 reserved + this.ver++; + } + + /* + * Util + */ + + public static int extractUUID(GameObjectType type, long compositeID) { + if (type == null || type == GameObjectType.unknown || compositeID == 0L) { + return -1; + } + int out = (int) compositeID; + if (out > Long.MAX_VALUE || out < 0) { + Logger.error("There was a problem reverse calculating a UUID from a compositeID. \tcomposID: " + + compositeID + " \ttype: " + type.toString() + "\tresult: " + out); + } + return out; + } + + public static GameObjectType extractTypeID(long compositeID) { + int ordinal = (int) (compositeID >>> 32); + return GameObjectType.values()[ordinal]; + } + + public boolean equals(AbstractGameObject obj) { + + if (obj == null) + return false; + + if (obj.objectType != this.objectType) { + return false; + } + + return obj.getObjectUUID() == this.getObjectUUID(); + } + + public void removeFromCache() { + DbManager.removeFromCache(this); + } + + /** + * Generates a {@link PreparedStatementShared} based on the specified query. + *

+ * If {@link AbstractGameObject} Database functions will properly release + * the PreparedStatementShared upon completion. If these functions are not + * used, then {@link PreparedStatementShared#release release()} must be + * called when finished with this object. + * + * @param sql + * The SQL string used to generate the PreparedStatementShared + * @return {@link PreparedStatementShared} + * @throws {@link SQLException} + **/ + protected static PreparedStatementShared prepareStatement(String sql) throws SQLException { + return new PreparedStatementShared(sql); + } + + public ConcurrentHashMap getDatabaseJobs() { + return this.databaseJobs; + } + + public void addDatabaseJob(String type, int duration) { + DatabaseUpdateJob updateJob; + + if (databaseJobs.containsKey(type)) + return; + + updateJob = new DatabaseUpdateJob(this, type); + JobContainer jc = JobScheduler.getInstance().scheduleJob(updateJob, duration); + databaseJobs.put(type, jc); + } + + public void removeDatabaseJob(String type, boolean canceled) { + if (databaseJobs.containsKey(type)) { + if (canceled) { + JobContainer jc = databaseJobs.get(type); + if (jc != null) + jc.cancelJob(); + } + databaseJobs.remove(type); + } + } + + public static AbstractGameObject getFromTypeAndID(long compositeID) { + int objectTypeID = extractTypeOrdinal(compositeID); + int tableID = extractTableID(objectTypeID, compositeID); + GameObjectType objectType = GameObjectType.values()[objectTypeID]; + + switch (objectType) { + case PlayerCharacter: + return PlayerCharacter.getPlayerCharacter(tableID); + + case NPC: + return NPC.getNPC(tableID); + + case Mob: + return Mob.getMob(tableID); + + case Building: + return BuildingManager.getBuilding(tableID); + + case Guild: + return Guild.getGuild(tableID); + + case Item: + return Item.getFromCache(tableID); + + case MobLoot: + return MobLoot.getFromCache(tableID); + + default: + Logger.error("Failed to convert compositeID to AbstractGameObject. " + "Unsupported type encountered. " + + "CompositeID: " + compositeID + " ObjectType: 0x" + Integer.toHexString(objectTypeID) + " TableID: " + tableID); + } + return null; + } + + public static int extractTypeOrdinal(long compositeID) { + return (int) (compositeID >>> 32); + } + public static int extractTableID(int type, long compositeID) { + if (type == 0 || compositeID == 0L) { + return -1; + } + return (int) compositeID; + } + + /* + * Abstract Methods + */ + + public abstract void updateDatabase(); +} diff --git a/src/engine/objects/AbstractIntelligenceAgent.java b/src/engine/objects/AbstractIntelligenceAgent.java new file mode 100644 index 00000000..f3f3c601 --- /dev/null +++ b/src/engine/objects/AbstractIntelligenceAgent.java @@ -0,0 +1,244 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.Enum.ModType; +import engine.Enum.SourceType; +import engine.InterestManagement.WorldGrid; +import engine.ai.MobileFSM.STATE; +import engine.gameManager.ZoneManager; +import engine.math.Vector3fImmutable; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.msg.PetMsg; +import engine.server.MBServerStatics; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; + + +public abstract class AbstractIntelligenceAgent extends AbstractCharacter { + private boolean assist = false; + private AbstractCharacter callForHelpAggro = null; + private int type = 0; //Mob: 0, Pet: 1, Guard: 2 + protected Vector3fImmutable lastBindLoc; + private boolean clearAggro = false; + + + public AbstractIntelligenceAgent(ResultSet rs) throws SQLException { + super(rs); + } + + public AbstractIntelligenceAgent(ResultSet rs, boolean isPlayer) + throws SQLException { + super(rs, isPlayer); + } + + + public AbstractIntelligenceAgent(ResultSet rs, + int UUID) throws SQLException { + super(rs, UUID); + } + + public AbstractIntelligenceAgent( String firstName, + String lastName, short statStrCurrent, short statDexCurrent, + short statConCurrent, short statIntCurrent, short statSpiCurrent, + short level, int exp, boolean sit, boolean walk, boolean combat, + Vector3fImmutable bindLoc, Vector3fImmutable currentLoc, Vector3fImmutable faceDir, + short healthCurrent, short manaCurrent, short stamCurrent, + Guild guild, byte runningTrains) { + super(firstName, lastName, statStrCurrent, statDexCurrent, statConCurrent, + statIntCurrent, statSpiCurrent, level, exp, bindLoc, + currentLoc, faceDir, guild, + runningTrains); + } + + public AbstractIntelligenceAgent(String firstName, + String lastName, short statStrCurrent, short statDexCurrent, + short statConCurrent, short statIntCurrent, short statSpiCurrent, + short level, int exp, boolean sit, boolean walk, boolean combat, + Vector3fImmutable bindLoc, Vector3fImmutable currentLoc, Vector3fImmutable faceDir, + short healthCurrent, short manaCurrent, short stamCurrent, + Guild guild, byte runningTrains, int newUUID) { + super(firstName, lastName, statStrCurrent, statDexCurrent, statConCurrent, + statIntCurrent, statSpiCurrent, level, exp, bindLoc, + currentLoc, faceDir, guild, + runningTrains, newUUID); + } + + @Override + public void setObjectTypeMask(int mask) { + mask |= MBServerStatics.MASK_IAGENT; + super.setObjectTypeMask(mask); + } + + /* AI Job Management */ + + public MobBase getMobBase() { + + if (this.getObjectType().equals(GameObjectType.Mob)) + return this.getMobBase(); + return null; + } + + public void setCallForHelpAggro(AbstractCharacter ac) { + this.callForHelpAggro = ac; + } + + public AbstractCharacter getCallForHelpAggro() { + return callForHelpAggro; + } + + public void setMob() { + this.type = 0; + } + + public void setPet(PlayerCharacter owner, boolean summoned) { + if (summoned) + this.type = 1; //summoned + else + this.type = 2; //charmed + if (this.getObjectType().equals(GameObjectType.Mob)) { + ((Mob)this).setOwner(owner); + } + } + + public void setGuard() { + this.type = 3; + } + + public boolean isMob() { + return (this.type == 0); + } + + public boolean isPet() { + return (this.type == 1 || this.type == 2); + } + + public boolean isSummonedPet() { + return (this.type == 1); + } + + public boolean isCharmedPet() { + return (this.type == 2); + } + + public boolean isGuard() { + return (this.type == 3); + } + + public boolean assist() { + return this.assist; + } + + public void setAssist(boolean value) { + this.assist = value; + } + + public void toggleAssist() { + this.assist = (this.assist) ? false : true; + } + + public int getDBID() { + + if (this.getObjectType().equals(GameObjectType.Mob)) + return this.getDBID(); + return 0; + } + + public boolean clearAggro() { + return clearAggro; + } + + public void setClearAggro(boolean value) { + this.clearAggro = value; + } + + public Vector3fImmutable getLastBindLoc() { + if (this.lastBindLoc == null) + this.lastBindLoc = this.getBindLoc(); + return this.lastBindLoc; + } + + public PlayerCharacter getOwner() { + + if (this .getObjectType().equals(GameObjectType.Mob)) + return this.getOwner(); + return null; + } + + public boolean getSafeZone() { + ArrayListallIn = ZoneManager.getAllZonesIn(this.getLoc()); + for (Zone zone : allIn) { + if (zone.getSafeZone() == (byte)1) + return true; + } + return false; + //return this.safeZone; + } + + public abstract AbstractWorldObject getFearedObject(); + + public float getAggroRange() { + float ret = MBServerStatics.AI_BASE_AGGRO_RANGE; + if (this.bonuses != null) + ret *= (1 +this.bonuses.getFloatPercentAll(ModType.ScanRange, SourceType.None)); + return ret; + } + + public void dismiss() { + + if (this.isPet()) { + + if (this.isSummonedPet()) { //delete summoned pet + + WorldGrid.RemoveWorldObject(this); + if (this.getObjectType() == GameObjectType.Mob){ + ((Mob)this).setState(STATE.Disabled); + if (((Mob)this).getParentZone() != null) + ((Mob)this).getParentZone().zoneMobSet.remove(this); + } + + } else { //revert charmed pet + this.setMob(); + this.setCombatTarget(null); + // if (this.isAlive()) + // WorldServer.updateObject(this); + } + //clear owner + PlayerCharacter owner = this.getOwner(); + + //close pet window + if (owner != null) { + Mob pet = owner.getPet(); + PetMsg pm = new PetMsg(5, null); + Dispatch dispatch = Dispatch.borrow(owner, pm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + if (pet != null && pet.getObjectUUID() == this.getObjectUUID()) + owner.setPet(null); + + if (this.getObjectType().equals(GameObjectType.Mob)) + ((Mob)this).setOwner(null); + } + + + } + } + + + + + +} + diff --git a/src/engine/objects/AbstractWorldObject.java b/src/engine/objects/AbstractWorldObject.java new file mode 100644 index 00000000..61e69d31 --- /dev/null +++ b/src/engine/objects/AbstractWorldObject.java @@ -0,0 +1,638 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum.DispatchChannel; +import engine.Enum.EffectSourceType; +import engine.Enum.GameObjectType; +import engine.Enum.GridObjectType; +import engine.InterestManagement.HeightMap; +import engine.InterestManagement.WorldGrid; +import engine.job.AbstractScheduleJob; +import engine.job.JobContainer; +import engine.job.JobScheduler; +import engine.jobs.NoTimeJob; +import engine.math.AtomicFloat; +import engine.math.Bounds; +import engine.math.Vector3f; +import engine.math.Vector3fImmutable; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.UpdateEffectsMsg; +import engine.powers.EffectsBase; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +public abstract class AbstractWorldObject extends AbstractGameObject { + + private String name = ""; + + protected final ReadWriteLock locationLock = new ReentrantReadWriteLock(true); + protected final ReadWriteLock updateLock = new ReentrantReadWriteLock(true); + + protected Vector3fImmutable loc = new Vector3fImmutable(0.0f, 0.0f, 0.0f); + private byte tier = 0; + private Vector3f rot = new Vector3f(0.0f, 0.0f, 0.0f); + protected AtomicFloat health = new AtomicFloat(); + public float healthMax; + protected boolean load = true; + protected ConcurrentHashMap effects = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private int objectTypeMask = 0; + private Bounds bounds; + + public int gridX = -1; + public int gridZ = -1; + + protected GridObjectType gridObjectType; + + protected float altitude = 0; + protected Regions region; + protected boolean movingUp = false; + public Regions landingRegion = null; + public Vector3fImmutable lastLoc = Vector3fImmutable.ZERO; + + /** + * No Id Constructor + */ + public AbstractWorldObject() { + super(); + } + + /** + * Normal Constructor + */ + public AbstractWorldObject(int objectUUID) { + super(objectUUID); + } + + + /** + * ResultSet Constructor + */ + public AbstractWorldObject(ResultSet rs) throws SQLException { + super(rs); + } + + //this should be called to handle any after load functions. + public abstract void runAfterLoad(); + + /* + * Getters + */ + public float getHealth() { + + + return this.health.get(); + + } + public float getCurrentHitpoints(){ + return this.health.get(); + } + + public float getHealthMax() { + return this.healthMax; + } + + public ConcurrentHashMap getEffects() { + return this.effects; + } + + //Add new effect + public void addEffect(String name, int duration, AbstractScheduleJob asj, EffectsBase eb, int trains) { + + if (!isAlive() && eb.getToken() != 1672601862) { + return; + } + JobContainer jc = JobScheduler.getInstance().scheduleJob(asj, duration); + Effect eff = new Effect(jc, eb, trains); + this.effects.put(name, eff); + applyAllBonuses(); + } + + public Effect addEffectNoTimer(String name, EffectsBase eb, int trains, boolean isStatic) { + NoTimeJob ntj = new NoTimeJob(this, name, eb, trains); //infinite timer + + if (this.getObjectType() == GameObjectType.Item || this.getObjectType() == GameObjectType.City){ + ntj.setEffectSourceType(this.getObjectType().ordinal()); + ntj.setEffectSourceID(this.getObjectUUID()); + } + + JobContainer jc = new JobContainer(ntj); + Effect eff = new Effect(jc, eb, trains); + if (isStatic) + eff.setIsStatic(isStatic); + this.effects.put(name, eff); + applyAllBonuses(); + return eff; + } + + //called when an effect runs it's course + public void endEffect(String name) { + + Effect eff = this.effects.get(name); + if (eff == null) { + return; + } + if (!isAlive() && eff.getEffectsBase().getToken() != 1672601862) { + return; + } + + if (eff.cancel()) { + + eff.endEffect(); + this.effects.remove(name); + if (this.getObjectType().equals(GameObjectType.PlayerCharacter)) + if (name.equals("Flight")){ + ((PlayerCharacter)this).update(); + PlayerCharacter.GroundPlayer((PlayerCharacter)this); + } + } + applyAllBonuses(); + } + + public void endEffectNoPower(String name) { + + Effect eff = this.effects.get(name); + if (eff == null) { + return; + } + if (!isAlive() && eff.getEffectsBase().getToken() != 1672601862) { + return; + } + + if (eff.cancel()) { + eff.cancelJob(); + eff.endEffectNoPower(); + this.effects.remove(name); + } + applyAllBonuses(); + } + + //Called to cancel an effect prematurely. + public void cancelEffect(String name, boolean overwrite) { + + Effect eff = this.effects.get(name); + if (eff == null) { + return; + } + if (!isAlive() && eff.getEffectsBase().getToken() != 1672601862) { + return; + } + + if (eff.cancel()) { + eff.cancelJob(); + this.effects.remove(name); + if (AbstractWorldObject.IsAbstractCharacter(this)) { + ((AbstractCharacter) this).cancelLastChantIfSame(eff); + } + } + if (!overwrite) { + applyAllBonuses(); + } + } + + //Called when an object dies/is destroyed + public void clearEffects() { + for (String name : this.effects.keySet()) { + Effect eff = this.effects.get(name); + if (eff == null) { + return; + } + + //Dont remove deathshroud here! + if (eff.getEffectToken() == 1672601862) + continue; + + if (eff.cancel()) { + if (eff.getPower() == null) { + if (!eff.isStatic()) + eff.endEffectNoPower(); + } + if (!eff.isStatic()) + eff.cancelJob(); + } + + this.effects.remove(name); + } + if (AbstractWorldObject.IsAbstractCharacter(this)) { + ((AbstractCharacter) this).cancelLastChant(); + } + applyAllBonuses(); + } + + public void removeEffectBySource(EffectSourceType source, int trains, boolean removeAll) { + if (!isAlive() && source.equals(EffectSourceType.DeathShroud) == false) { + return; + } + + //hacky way to dispell trebs. + if (this.getObjectType() == GameObjectType.Mob){ + Mob mob = (Mob)this; + if (mob.isSiege()){ + if (mob.isPet()){ + PlayerCharacter petOwner = mob.getOwner(); + if (petOwner != null && source.equals(EffectSourceType.Effect)){ + petOwner.dismissPet(); + return; + } + } + } + } + boolean changed = false; + String toRemove = ""; + int toRemoveToken = Integer.MAX_VALUE; + for (String name : this.effects.keySet()) { + Effect eff = this.effects.get(name); + if (eff == null) { + continue; + } + if (eff.containsSource(source) && trains >= eff.getTrains()) { + if (removeAll) { + //remove all effects of source type + if (eff.cancel()) { + eff.cancelJob(); + } + this.effects.remove(name); + changed = true; + + if (source.equals("Flight")){ + //ground player + if (this.getObjectType().equals(GameObjectType.PlayerCharacter)){ + ((PlayerCharacter)this).update(); + PlayerCharacter.GroundPlayer((PlayerCharacter)this); + } + } + } else { + //find lowest token of source type to remove + int tok = eff.getEffectToken(); + if (tok != 0 && tok < toRemoveToken) { + toRemove = name; + toRemoveToken = tok; + } + } + } + } + + //WTF IS THIS? + if (toRemoveToken < Integer.MAX_VALUE && this.effects.containsKey(toRemove)) { + //remove lowest found token of source type + Effect eff = this.effects.get(toRemove); + if (eff != null) { + changed = true; + if (eff.cancel()) { + eff.cancelJob(); + } + this.effects.remove(toRemove); + + if (source.equals("Flight")){ + //ground player + if (this.getObjectType().equals(GameObjectType.PlayerCharacter)){ + ((PlayerCharacter)this).update(); + PlayerCharacter.GroundPlayer((PlayerCharacter)this); + } + } + + } + } + if (changed) { + applyAllBonuses(); + } + } + + public void sendAllEffects(ClientConnection cc) { + UpdateEffectsMsg msg = new UpdateEffectsMsg(this); + Dispatch dispatch = Dispatch.borrow((PlayerCharacter)this, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + } + + public void applyAllBonuses() { + if (AbstractWorldObject.IsAbstractCharacter(this)) { + ((AbstractCharacter) this).applyBonuses(); + } + } + + public JobContainer getEffectJobContainer(String name) { + Effect ef = this.effects.get(name); + if (ef != null) { + return ef.getJobContainer(); + } + return null; + } + + public AbstractScheduleJob getEffectJob(String name) { + Effect ef = this.effects.get(name); + if (ef == null) { + return null; + } + JobContainer jc = ef.getJobContainer(); + if (jc != null) { + return (AbstractScheduleJob) jc.getJob(); + } + return null; + } + + public boolean containsEffect(int token) { + for (Effect eff : this.effects.values()) { + if (eff != null) { + if (eff.getEffectsBase() != null) { + if (eff.getEffectsBase().getToken() == token) { + return true; + } + } + } + } + return false; + } + + public int getObjectTypeMask() { + return objectTypeMask; + } + + + public Vector3fImmutable getLoc() { + return this.loc; + } + + public Vector3f getRot() { + return rot; + } + + public byte getTier() { + return tier; + } + + public boolean isAlive() { + if (AbstractWorldObject.IsAbstractCharacter(this)) { + return this.isAlive(); + } else if (this.getObjectType().equals(GameObjectType.Building)) { + return (!(((Building) this).getRank() < 0)); + } else { + return true; + } + } + + /* + * Setters + */ + public void setObjectTypeMask(int mask) { + this.objectTypeMask = mask; + } + + //TODO return false if something goes wrong? resync player? + public void setLoc(Vector3fImmutable loc) { + locationLock.writeLock().lock(); + try { + if (Float.isNaN(loc.x) || Float.isNaN(loc.z)) + return; + + if (loc.equals(Vector3fImmutable.ZERO)) + return; + + if (loc.x > MBServerStatics.MAX_WORLD_WIDTH || loc.z < MBServerStatics.MAX_WORLD_HEIGHT) + return; + this.lastLoc = new Vector3fImmutable(this.loc); + this.loc = loc; + this.loc = this.loc.setY(HeightMap.getWorldHeight(this) + this.getAltitude()); + + //lets not add mob to world grid if he is currently despawned. + if (this.getObjectType().equals(GameObjectType.Mob) && ((Mob)this).despawned) + return; + + //do not add objectUUID 0 to world grid. dunno da fuck this doing why its doing but its doing... da fuck. + if (this.getObjectUUID() == 0) + return; + WorldGrid.addObject(this,loc.x,loc.z); + + }catch(Exception e){ + Logger.error("Failed to set location for World Object. Type = " + this.getObjectType().name() + " : Name = " + this.getName()); + e.printStackTrace(); + } finally { + locationLock.writeLock().unlock(); + } + + } + + public void setY(float y){ + this.loc = this.loc.setY(y); + } + + + public void setRot(Vector3f rotation) { + synchronized (this.rot) { + this.rot = rotation; + } + } + + public void setTier(byte tier) { + synchronized (this.rot) { + this.tier = tier; + } + } + + public static int getType() { + return 0; + } + + public boolean load() { + return this.load; + } + + /* + * Utils + */ + public String getName() { + if (this.name.length() == 0) { + return "Unnamed " + '(' + + this.getObjectUUID() + ')'; + } else { + return this.name; + } + } + + public String getSimpleName() { + return this.name; + } + + + /** + * @return the bounds + */ + public Bounds getBounds() { + return bounds; + } + + public void setBounds(Bounds bounds) { + + this.bounds = bounds; + } + + /** + * @param health the health to set + */ + public void setHealth(float health) { + + this.health.set(health); + } + + public static boolean IsAbstractCharacter(AbstractWorldObject awo){ + + if (awo == null) + return false; + + if (awo.getObjectType() == GameObjectType.PlayerCharacter || awo.getObjectType() == GameObjectType.Mob || awo.getObjectType() == GameObjectType.NPC) + return true; + return false; + } + + public static void RemoveFromWorldGrid(AbstractWorldObject gridObjectToRemove){ + if (gridObjectToRemove.gridX < 0 || gridObjectToRemove.gridZ < 0) + return; + + ConcurrentHashMap gridMap; + switch(gridObjectToRemove.gridObjectType){ + case STATIC: + gridMap = WorldGrid.StaticGridMap[gridObjectToRemove.gridX][gridObjectToRemove.gridZ]; + break; + case DYNAMIC: + gridMap = WorldGrid.DynamicGridMap[gridObjectToRemove.gridX][gridObjectToRemove.gridZ]; + break; + default: + gridMap = WorldGrid.StaticGridMap[gridObjectToRemove.gridX][gridObjectToRemove.gridZ]; + break; + + } + + if (gridMap == null){ + Logger.info("Null gridmap for Object UUD: " + gridObjectToRemove); + return; + } + + + + + gridMap.remove(gridObjectToRemove.getObjectUUID()); + gridObjectToRemove.gridX = -1; + gridObjectToRemove.gridZ = -1; + + } + + public static boolean AddToWorldGrid(AbstractWorldObject gridObjectToAdd, int x, int z){ + try{ + + ConcurrentHashMap gridMap; + if (gridObjectToAdd.gridObjectType.equals(GridObjectType.STATIC)) + gridMap = WorldGrid.StaticGridMap[x][z]; + else + gridMap = WorldGrid.DynamicGridMap[x][z]; + + gridMap.put(gridObjectToAdd.getObjectUUID(),gridObjectToAdd); + gridObjectToAdd.gridX = x; + gridObjectToAdd.gridZ = z; + return true; + }catch(Exception e){ + Logger.error(e); + return false; + } + + } + + public static Regions GetRegionByWorldObject(AbstractWorldObject worldObject){ + Regions region = null; + + if (worldObject.getObjectType().equals(GameObjectType.PlayerCharacter)) + if (((PlayerCharacter)worldObject).isFlying()) + return null; + //Find building + for (AbstractWorldObject awo:WorldGrid.getObjectsInRangePartial(worldObject.getLoc(), MBServerStatics.STRUCTURE_LOAD_RANGE, MBServerStatics.MASK_BUILDING)){ + Building building = (Building)awo; + if (!Bounds.collide(worldObject.getLoc(), building.getBounds())) + continue; + + //find regions that intersect x and z, check if object can enter. + for (Regions toEnter: building.getBounds().getRegions()){ + if (toEnter.isPointInPolygon(worldObject.getLoc())){ + if (Regions.CanEnterRegion(worldObject, toEnter)) + if (region == null) + region = toEnter; + else // we're using a low level to high level tree structure, database not always in order low to high. + //check for highest level index. + if(region != null && toEnter.highLerp.y > region.highLerp.y) + region = toEnter; + + + } + } + } + + //set players new altitude to region lerp altitude. + if (region != null) + if (region.center.y == region.highLerp.y) + worldObject.loc = worldObject.loc.setY(region.center.y + worldObject.getAltitude()); + else + worldObject.loc = worldObject.loc.setY(region.lerpY(worldObject) + worldObject.getAltitude()); + + return region; + } + + public static Regions GetRegionFromBuilding(Vector3fImmutable worldLoc, Building building){ + Regions region = null; + + + return region; + } + + public float getAltitude() { + return altitude; + } + + + public ReadWriteLock getUpdateLock() { + return updateLock; + } + + public GridObjectType getGridObjectType() { + return gridObjectType; + } + + public Regions getRegion() { + return region; + } + + + public boolean isMovingUp() { + return movingUp; + } + + public void setMovingUp(boolean movingUp) { + this.movingUp = movingUp; + } + + public void setRegion(Regions region) { + this.region = region; + } + + //used for interestmanager loading and unloading objects to client. + // if not in grid, unload from player. + public boolean isInWorldGrid(){ + if (this.gridX == -1 && this.gridZ == -1) + return false; + + return true; + } + + +} diff --git a/src/engine/objects/Account.java b/src/engine/objects/Account.java new file mode 100644 index 00000000..f910faf2 --- /dev/null +++ b/src/engine/objects/Account.java @@ -0,0 +1,390 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum; +import engine.Enum.DispatchChannel; +import engine.Enum.ItemContainerType; +import engine.gameManager.ConfigManager; +import engine.gameManager.DbManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.ClientMessagePump; +import engine.net.client.msg.*; +import engine.util.ByteUtils; +import org.pmw.tinylog.Logger; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; + +public class Account extends AbstractGameObject { + + private final String uname; + private String passwd; + private int lastCharIDUsed; + private String salt; + public String discordAccount; + private byte loginAttempts = 0; + private long lastLoginFailure = System.currentTimeMillis(); + public HashMap characterMap = new HashMap<>(); + public static ConcurrentHashMap AccountsMap = new ConcurrentHashMap<>(); + private ArrayList vault = new ArrayList<>(); + public Item vaultGold = null; + public long lastPasswordCheck = 0; + public Enum.AccountStatus status; + + public ArrayList getVault() { + return vault; + } + + public Account(ResultSet resultSet) throws SQLException { + super(resultSet); + + this.uname = resultSet.getString("acct_uname"); + this.passwd = resultSet.getString("acct_passwd"); + this.lastCharIDUsed = resultSet.getInt("acct_lastCharUID"); + this.salt = resultSet.getString("acct_salt"); + this.discordAccount = resultSet.getString("discordAccount"); + this.status = Enum.AccountStatus.valueOf(resultSet.getString("status")); + } + + public String getUname() { + return uname; + } + + public String getPasswd() { + return passwd; + } + + public String getSalt() { + return salt; + } + + public int getLastCharIDUsed() { + return lastCharIDUsed; + } + + public byte getLoginAttempts() { + return loginAttempts; + } + + public long getLastLoginFailure() { + return this.lastLoginFailure; + } + + public void setLastCharIDUsed(int lastCharIDUsed) { + this.lastCharIDUsed = lastCharIDUsed; + } + + public void setLastLoginFailure() { + this.lastLoginFailure = System.currentTimeMillis(); + } + + public void setLastCharacter(int uuid) { + this.lastCharIDUsed = uuid; + // this.updateDatabase(); + } + + public void incrementLoginAttempts() { + ++this.loginAttempts; + this.setLastLoginFailure(); + } + + public void resetLoginAttempts() { + this.loginAttempts = 0; + } + + /* + * on successfully matching the password, this method additionally calls to + * associateIpToAccount for IPAddress tracking. dokks + */ + public boolean passIsValid(String pw, String ip, String machineID) throws IllegalArgumentException { + boolean result = false; + // see if it was entered in plain text first, if the plain text matches, + // hash it and save to the database. + try { + pw = ByteUtils.byteArrayToSafeStringHex(MessageDigest + .getInstance("md5").digest(pw.getBytes("UTF-8"))) + + salt; + pw = ByteUtils.byteArrayToSafeStringHex(MessageDigest + .getInstance("md5").digest(pw.getBytes())); + result = this.passwd.equals(pw); + } catch ( NoSuchAlgorithmException | UnsupportedEncodingException e) { + Logger.error( e.toString()); + } + + if (result) { + // TODO: should use an executor here so that we can + // fire and forget this update. + // this is a valid user, so let's also update the + // database with login time and IP. + if((ip==null)||(ip.length()==0)) { + throw new IllegalArgumentException(); + } + } + return result; + } + + public ClientConnection getClientConnection() { + return SessionManager.getClientConnection(this); + } + + public PlayerCharacter getPlayerCharacter() { + return SessionManager.getPlayerCharacter(this); + } + + @Override + public void updateDatabase() { + DbManager.AccountQueries.updateDatabase(this); + } + + //this should be called to handle any after load functions. + + public void runAfterLoad() { + + try { + + if (ConfigManager.serverType.equals(Enum.ServerType.LOGINSERVER)){ + ArrayList playerList = DbManager.PlayerCharacterQueries.GET_CHARACTERS_FOR_ACCOUNT(this.getObjectUUID()); + + for(PlayerCharacter player:playerList) { + PlayerCharacter.initializePlayer(player); + this.characterMap.putIfAbsent(player.getObjectUUID(), player); + } + + playerList.clear(); + } + + if (ConfigManager.serverType.equals(Enum.ServerType.WORLDSERVER)) { + this.vault = DbManager.ItemQueries.GET_ITEMS_FOR_ACCOUNT(this.getObjectUUID()); + + for (Item item : this.vault) { + if (item.getItemBase().getUUID() == 7) { + this.vaultGold = item; + } + } + + if (this.vaultGold == null) { + this.vaultGold = Item.newGoldItem(this.getObjectUUID(), ItemBase.getItemBase(7), ItemContainerType.VAULT); + + if (this.vaultGold != null) + this.vault.add(this.vaultGold); + } + } + + } catch (Exception e) { + Logger.error( e); + } + } + + public synchronized void transferItemFromInventoryToVault(TransferItemFromInventoryToVaultMsg msg, ClientConnection origin) { + + PlayerCharacter player = origin.getPlayerCharacter(); + Dispatch dispatch; + + if (player == null) + return; + + if (!ClientMessagePump.NPCVaultBankRangeCheck(player, origin, "vault")) { + ClientMessagePump.forceTransferFromVaultToInventory(msg, origin, "You are out of range of the vault."); + return; + } + + int uuid = msg.getUUID(); + Item item = Item.getFromCache(uuid); + + if (item == null) { + ClientMessagePump.forceTransferFromVaultToInventory(msg, origin, "Can't find the item."); + return; + } + + //dupe check + if (!item.validForInventory(origin, player, player.getCharItemManager())) + return; + + if (item.containerType == Enum.ItemContainerType.INVENTORY && player.getCharItemManager().isVaultOpen()) { + if (!player.getCharItemManager().hasRoomVault(item.getItemBase().getWeight())) { + ClientMessagePump.forceTransferFromVaultToInventory(msg, origin, "There is no room in your vault."); + return; + } + + if (player.getCharItemManager().moveItemToVault(item)) { + this.vault.add(item); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + } else + ClientMessagePump.forceTransferFromVaultToInventory(msg, origin, "Failed to transfer item."); + } + } + + public synchronized void transferItemFromVaultToInventory(TransferItemFromVaultToInventoryMsg msg, ClientConnection origin) { + + PlayerCharacter player = origin.getPlayerCharacter(); + Dispatch dispatch; + + if (player == null) + return; + + if (!ClientMessagePump.NPCVaultBankRangeCheck(player, origin, "vault")) { + ClientMessagePump.forceTransferFromInventoryToVault(msg, origin, "You are out of range of the vault."); + return; + } + + CharacterItemManager itemManager = player.getCharItemManager(); + + if (itemManager == null) { + ClientMessagePump.forceTransferFromInventoryToVault(msg, origin, "Can't find your item manager."); + return; + } + + Item item = Item.getFromCache(msg.getUUID()); + + if (item == null) { + ClientMessagePump.forceTransferFromInventoryToVault(msg, origin, "Can't find the item."); + return; + } + + //dupe check + if (!item.validForVault(origin, player, itemManager)) + return; + + if (item.containerType == Enum.ItemContainerType.VAULT && itemManager.isVaultOpen()) { + if (!itemManager.hasRoomInventory(item.getItemBase().getWeight())) { + ClientMessagePump.forceTransferFromInventoryToVault(msg, origin, "There is no room in your inventory."); + return; + } + if (itemManager.moveItemToInventory(item)) { + this.vault.remove(item); + + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + } else + ClientMessagePump.forceTransferFromInventoryToVault(msg, origin, "Failed to transfer item."); + } + } + + public synchronized void transferGoldFromVaultToInventory(TransferGoldFromVaultToInventoryMsg msg, ClientConnection origin) { + + PlayerCharacter player = origin.getPlayerCharacter(); + Dispatch dispatch; + + if (player == null) + return; + + Account account = player.getAccount(); + + if (account == null) + return; + + if (!ClientMessagePump.NPCVaultBankRangeCheck(player, origin, "vault")) + return; + + NPC npc = player.getLastNPCDialog(); + + if (npc == null) + return; + + CharacterItemManager itemManager = player.getCharItemManager(); + + if (itemManager == null) + return; + + if (itemManager.isVaultOpen() == false) + return; + + if (itemManager.moveGoldToInventory(itemManager.getGoldVault(), msg.getAmount()) == false) + return; + + OpenVaultMsg open = new OpenVaultMsg(player, npc); + ShowVaultInventoryMsg show = new ShowVaultInventoryMsg(player, account, npc); // 37?? + + UpdateGoldMsg ugm = new UpdateGoldMsg(player); + ugm.configure(); + dispatch = Dispatch.borrow(player, ugm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + UpdateVaultMsg uvm = new UpdateVaultMsg(account); + dispatch = Dispatch.borrow(player, uvm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + dispatch = Dispatch.borrow(player, open); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + dispatch = Dispatch.borrow(player, show); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + } + + public synchronized void transferGoldFromInventoryToVault(TransferGoldFromInventoryToVaultMsg msg, ClientConnection origin) { + + PlayerCharacter player = origin.getPlayerCharacter(); + Dispatch dispatch; + + if (player == null) + return; + + Account account = player.getAccount(); + + if (account == null) + return; + + if (!ClientMessagePump.NPCVaultBankRangeCheck(player, origin, "vault")) + return; + + CharacterItemManager itemManager = player.getCharItemManager(); + + if (itemManager == null) + return; + + NPC npc = player.getLastNPCDialog(); + + if (npc == null) + return; + + // Cannot have bank and vault open concurrently + // Dupe prevention + + if (itemManager.isVaultOpen() == false) + return; + + // Something went horribly wrong. Should be log this? + + if (itemManager.moveGoldToVault(itemManager.getGoldInventory(), msg.getAmount()) == false) + return; + + UpdateGoldMsg ugm = new UpdateGoldMsg(player); + ugm.configure(); + dispatch = Dispatch.borrow(player, ugm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + UpdateVaultMsg uvm = new UpdateVaultMsg(account); + dispatch = Dispatch.borrow(player, uvm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + OpenVaultMsg open = new OpenVaultMsg(player, npc); + dispatch = Dispatch.borrow(player, open); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + // + // + ShowVaultInventoryMsg show = new ShowVaultInventoryMsg(player, account, npc); // 37?? + dispatch = Dispatch.borrow(player, show); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + } +} diff --git a/src/engine/objects/Bane.java b/src/engine/objects/Bane.java new file mode 100644 index 00000000..22370a67 --- /dev/null +++ b/src/engine/objects/Bane.java @@ -0,0 +1,650 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.objects; + +import engine.Enum; +import engine.Enum.ProtectionState; +import engine.Enum.SiegePhase; +import engine.Enum.SiegeResult; +import engine.InterestManagement.HeightMap; +import engine.InterestManagement.WorldGrid; +import engine.db.archive.BaneRecord; +import engine.db.archive.DataWarehouse; +import engine.gameManager.BuildingManager; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.gameManager.ZoneManager; +import engine.job.JobScheduler; +import engine.jobs.ActivateBaneJob; +import engine.jobs.BaneDefaultTimeJob; +import engine.math.Vector3fImmutable; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.PlaceAssetMsg; +import engine.net.client.msg.chat.ChatSystemMsg; +import engine.server.MBServerStatics; +import org.joda.time.DateTime; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Date; +import java.util.concurrent.ConcurrentHashMap; + +public final class Bane { + + private final int cityUUID; + private int ownerUUID; + private final int stoneUUID; + private DateTime placementDate = null; + private DateTime liveDate = null; + private BaneDefaultTimeJob defaultTimeJob; + private ActivateBaneJob activateBaneJob; + + // Internal cache for banes + + public static ConcurrentHashMap banes = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + + /** + * ResultSet Constructor + */ + public Bane(ResultSet rs) throws SQLException { + + Date sqlDateTime; + ActivateBaneJob abtj; + + this.cityUUID = rs.getInt("cityUUID"); + this.ownerUUID = rs.getInt("ownerUUID"); + this.stoneUUID = rs.getInt("stoneUUID"); + + sqlDateTime = rs.getTimestamp("placementDate"); + + if (sqlDateTime != null) + this.placementDate = new DateTime(sqlDateTime); + + sqlDateTime = rs.getTimestamp("liveDate"); + + if (sqlDateTime != null) + this.liveDate = new DateTime(sqlDateTime); + + switch (this.getSiegePhase()) { + + case CHALLENGE: + break; + case WAR: + + // cancel old job if exists + + if (this.activateBaneJob != null) + this.activateBaneJob.cancelJob(); + + abtj = new ActivateBaneJob(cityUUID); + JobScheduler.getInstance().scheduleJob(abtj, this.liveDate.getMillis()); + this.activateBaneJob = abtj; + + break; + case STANDOFF: + + // cancel old job if exists + + if (this.activateBaneJob != null) + this.activateBaneJob.cancelJob(); + + abtj = new ActivateBaneJob(cityUUID); + JobScheduler.getInstance().scheduleJob(abtj, this.liveDate.getMillis()); + this.activateBaneJob = abtj; + + break; + } + + if (this.liveDate == null) + setDefaultTime(); + } + + public static boolean summonBanestone(PlayerCharacter player, ClientConnection origin, int rank) { + + Guild baningGuild; + Zone cityZone; + City targetCity; + + ArrayList nationBanes; + + baningGuild = player.getGuild(); + + if (baningGuild.getNation().isErrant()) { + PlaceAssetMsg.sendPlaceAssetError( origin, 55, ""); // You must be in a Nation + return false; + } + + if (baningGuild.getNation().isNPCGuild()) { + PlaceAssetMsg.sendPlaceAssetError( origin, 72, ""); // Cannot be in an NPC nation + return false; + } + + if (GuildStatusController.isInnerCouncil(player.getGuildStatus()) == false) { + PlaceAssetMsg.sendPlaceAssetError( origin, 10, ""); // You must be a guild leader + return false; + } + + // Cannot place banestones underwater; + + if (HeightMap.isLocUnderwater(player.getLoc())) { + PlaceAssetMsg.sendPlaceAssetError(origin, 6, ""); // Cannot place underwater + return false; + } + + // figure out which city we're standing on + // must be within a city's seige Bounds + + targetCity = ZoneManager.getCityAtLocation(player.getLoc()); + + if (targetCity == null) { + PlaceAssetMsg.sendPlaceAssetError( origin, 59, ""); // No city to siege at this location! + return false; + } + + if (targetCity.isSafeHold()) { + PlaceAssetMsg.sendPlaceAssetError( origin, 15, ""); // Cannot place assets in peace zone + return false; + } + + if (targetCity.getRank() > rank) { + PlaceAssetMsg.sendPlaceAssetError( origin, 60, ""); // Bane rank is too low + return false; + } + + cityZone = targetCity.getParent(); + + // Cannot place assets on a dead tree + + if ((cityZone.isPlayerCity()) && + (City.getCity(cityZone.getPlayerCityUUID()).getTOL().getRank() == -1)) { + PlaceAssetMsg.sendPlaceAssetError(origin, 1, "Cannot bane a dead tree!"); + return false; + } + + if (baningGuild.getNation() == targetCity.getGuild().getNation()) { + PlaceAssetMsg.sendPlaceAssetError( origin, 20, ""); //Cannot bane yourself! + return false; + } + + if (targetCity.getTOL() == null) { + PlaceAssetMsg.sendPlaceAssetError( origin, 65, ""); // Cannot find tree to target + return false; + } + + if (targetCity.getBane() != null) { + PlaceAssetMsg.sendPlaceAssetError( origin, 23, ""); // Tree is already baned. + return false; + } + + if (getBaneByAttackerGuild(baningGuild) != null) { + PlaceAssetMsg.sendPlaceAssetError( origin, 1, "Your guild has already placed a bane!"); + return false; + } + + nationBanes = getBanesByNation(baningGuild.getNation()); + + // A nation can only have 3 concurrent banes + + if (nationBanes.size() == 3) { + PlaceAssetMsg.sendPlaceAssetError( origin, 64, ""); // Your nation is already at war and your limit has been reached + return false; + } + + if (targetCity.isLocationOnCityGrid(player.getLoc()) == true) { + PlaceAssetMsg.sendPlaceAssetError( origin, 1, "Cannot place banestone on city grid."); + return false; + } + + Blueprint blueprint = Blueprint.getBlueprint(24300); // Banestone + + //Let's drop a banestone! + + Vector3fImmutable localLocation = ZoneManager.worldToLocal(player.getLoc(), cityZone); + + if (localLocation == null) { + PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + Logger.info("Failed to Convert World coordinates to local zone coordinates"); + return false; + } + + Building stone = DbManager.BuildingQueries.CREATE_BUILDING( + cityZone.getObjectUUID(), player.getObjectUUID(), blueprint.getName(), blueprint.getBlueprintUUID(), + localLocation, 1.0f, blueprint.getMaxHealth(rank), ProtectionState.PROTECTED, 0, rank, + null, blueprint.getBlueprintUUID(), 1, 0.0f); + + if (stone == null) { + PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + return false; + } + + stone.addEffectBit((1 << 19)); + stone.setMaxHitPoints(stone.getBlueprint().getMaxHealth(stone.getRank())); + stone.setCurrentHitPoints(stone.getMaxHitPoints()); + BuildingManager.setUpgradeDateTime(stone, null, 0); + + //Make the bane + + Bane bane = makeBane(player, targetCity, stone); + + if (bane == null) { + //delete bane stone, failed to make bane object + DbManager.BuildingQueries.DELETE_FROM_DATABASE(stone); + PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + return false; + } + + WorldGrid.addObject(stone, player); + + //Add bane effect to TOL + + targetCity.getTOL().addEffectBit((1 << 16)); + targetCity.getTOL().updateEffects(); + + Vector3fImmutable movePlayerOutsideStone = player.getLoc(); + movePlayerOutsideStone = movePlayerOutsideStone.setX(movePlayerOutsideStone.x + 10); + movePlayerOutsideStone = movePlayerOutsideStone.setZ(movePlayerOutsideStone.z + 10); + player.teleport(movePlayerOutsideStone); + + // Notify players + + ChatSystemMsg chatMsg = new ChatSystemMsg(null, "[Bane Channel] " + targetCity.getCityName() + " has been baned by " + baningGuild.getName() + ". Standoff phase has begun!"); + chatMsg.setMessageType(4); + chatMsg.setChannel(Enum.ChatChannelType.SYSTEM.getChannelID()); + + DispatchMessage.dispatchMsgToAll(chatMsg); + + // Push this event to the DataWarehouse + + BaneRecord baneRecord = BaneRecord.borrow(bane, Enum.RecordEventType.PENDING); + DataWarehouse.pushToWarehouse(baneRecord); + + return true; + } + + public SiegePhase getSiegePhase() { + + SiegePhase phase; + + if (this.liveDate == null) { + phase = SiegePhase.CHALLENGE; //challenge + return phase; + } + + if (DateTime.now().isAfter(this.liveDate)) { + phase = SiegePhase.WAR; //war + return phase; + } + + // If code reaches this point we are in standoff mode. + phase = SiegePhase.STANDOFF; //standoff + + return phase; + } + + private void setDefaultTime() { + + DateTime timeToSetDefault = new DateTime(this.placementDate); + timeToSetDefault = timeToSetDefault.plusDays(1); + + DateTime currentTime = DateTime.now(); + DateTime defaultTime = new DateTime(this.placementDate); + defaultTime = defaultTime.plusDays(2); + defaultTime = defaultTime.hourOfDay().setCopy(22); + defaultTime = defaultTime.minuteOfHour().setCopy(0); + defaultTime = defaultTime.secondOfMinute().setCopy(0); + + if (currentTime.isAfter(timeToSetDefault)) + this.setLiveDate(defaultTime); + else { + + if (this.defaultTimeJob != null) + this.defaultTimeJob.cancelJob(); + + BaneDefaultTimeJob bdtj = new BaneDefaultTimeJob(this); + JobScheduler.getInstance().scheduleJob(bdtj, timeToSetDefault.getMillis()); + this.defaultTimeJob = bdtj; + } + } + + public void setLiveDate(DateTime baneTime) { + + if (DbManager.BaneQueries.SET_BANE_TIME(baneTime, this.getCity().getObjectUUID())) { + this.liveDate = new DateTime(baneTime); + + // Push event to warehouse + + BaneRecord.updateLiveDate(this, baneTime); + + ChatManager.chatGuildInfo(this.getOwner().getGuild(), "The bane on " + this.getCity().getGuild().getName() + " has been set! Standoff phase has begun!"); + Guild attackerNation = this.getOwner().getGuild().getNation(); + + if (attackerNation != null) + + for (Guild subGuild : attackerNation.getSubGuildList()) { + + //Don't send the message twice. + if (subGuild.equals(attackerNation)) + continue; + + ChatManager.chatGuildInfo(subGuild, "The bane on " + this.getCity().getGuild().getName() + " has been set! Standoff phase has begun!"); + } + + ChatManager.chatGuildInfo(this.getCity().getGuild(), "The bane on " + this.getCity().getGuild().getName() + " has been set! Standoff phase has begun!"); + + Guild defenderNation = this.getCity().getGuild().getNation(); + + if (defenderNation != null) { + + if (defenderNation != this.getCity().getGuild()) + ChatManager.chatGuildInfo(defenderNation, "The bane on " + this.getCity().getGuild().getName() + " has been set! Standoff phase has begun!"); + + for (Guild subGuild : defenderNation.getSubGuildList()) { + //Don't send the message twice. + if (subGuild == this.getCity().getGuild()) + continue; + ChatManager.chatGuildInfo(subGuild, "The bane on " + this.getCity().getGuild().getName() + " has been set! Standoff phase has begun!"); + } + } + + if (activateBaneJob != null) + this.activateBaneJob.cancelJob(); + + ActivateBaneJob abtj = new ActivateBaneJob(cityUUID); + + JobScheduler.getInstance().scheduleJob(abtj, this.liveDate.getMillis()); + this.activateBaneJob = abtj; + } else { + Logger.debug( "error with city " + this.getCity().getName()); + ChatManager.chatGuildInfo(this.getOwner().getGuild(), "A Serious error has occurred. Please post details for to ensure transaction integrity"); + ChatManager.chatGuildInfo(this.getCity().getGuild(), "A Serious error has occurred. Please post details for to ensure transaction integrity"); + } + + } + + /* + * Getters + */ + public City getCity() { + return City.getCity(this.cityUUID); + } + + public PlayerCharacter getOwner() { + return (PlayerCharacter) DbManager.getObject(Enum.GameObjectType.PlayerCharacter, ownerUUID); + } + + //Call this to prematurely end a bane + + public boolean remove() { + + Building baneStone; + + baneStone = this.getStone(); + + if (baneStone == null) { + Logger.debug("Removing bane without a stone."); + return false; + } + + // Reassert protection contracts + + this.getCity().protectionEnforced = true; + + // Remove visual effects on Bane and TOL. + + this.getStone().removeAllVisualEffects(); + this.getStone().updateEffects(); + + this.getCity().getTOL().removeAllVisualEffects(); + this.getCity().getTOL().updateEffects(); + + // Remove bane from the database + + if (DbManager.BaneQueries.REMOVE_BANE(this) == false) { + Logger.error("Database call failed for city UUID: " + this.getCity().getObjectUUID()); + return false; + } + + // Remove bane from ingame cache + + Bane.banes.remove(cityUUID); + + // Delete stone from database + + if (DbManager.BuildingQueries.DELETE_FROM_DATABASE(baneStone) == false) { + Logger.error( "Database error when deleting stone object"); + return false; + } + + // Remove object from simulation + + baneStone.removeFromCache(); + WorldGrid.RemoveWorldObject(baneStone); + WorldGrid.removeObject(baneStone); + return true; + } + + // Cache access + + public static Bane getBane(int cityUUID) { + + Bane outBane; + + // Check cache first + + outBane = banes.get(cityUUID); + + // Last resort attempt to load from database + + if (outBane == null) + outBane = DbManager.BaneQueries.LOAD_BANE(cityUUID); + else + return outBane; + + // As we loaded from the db, store it in the internal cache + + if (outBane != null) + banes.put(cityUUID, outBane); + + return outBane; + } + + //Returns a guild's bane + + public static Bane getBaneByAttackerGuild(Guild guild) { + + + if (guild == null || guild.isErrant()) + return null; + ArrayList baneList; + + baneList = new ArrayList<>(banes.values()); + + for (Bane bane : baneList) { + if (bane.getOwner().getGuild().equals(guild)) + return bane; + } + + return null; + } + + public static ArrayList getBanesByNation(Guild guild) { + + ArrayList baneList; + ArrayList returnList; + + baneList = new ArrayList<>(banes.values()); + returnList = new ArrayList<>(); + + for (Bane bane : baneList) { + if (bane.getOwner().getGuild().getNation().equals(guild)) + returnList.add(bane); + } + + return returnList; + } + + public static void addBane(Bane bane) { + + Bane.banes.put(bane.cityUUID, bane); + + } + + public static Bane makeBane(PlayerCharacter owner, City city, Building stone) { + + Bane newBane; + + if (DbManager.BaneQueries.CREATE_BANE(city, owner, stone) == false) { + Logger.error("Error writing to database"); + return null; + } + + newBane = DbManager.BaneQueries.LOAD_BANE(city.getObjectUUID()); + + return newBane; + } + + public final DateTime getPlacementDate() { + return placementDate; + } + + public final boolean isAccepted() { + + return (this.getSiegePhase() != SiegePhase.CHALLENGE); + } + + public final DateTime getLiveDate() { + return liveDate; + } + + public final void endBane(SiegeResult siegeResult) { + + boolean baneRemoved; + + // No matter what the outcome of a bane, we re-asset + // protection contracts at this time. They don't quite + // matter if the city falls, as they are invalidated. + + this.getCity().protectionEnforced = true; + + switch (siegeResult) { + case DEFEND: + + // Push event to warehouse + + BaneRecord.updateResolution(this, Enum.RecordEventType.DEFEND); + + baneRemoved = remove(); + + if (baneRemoved) { + + // Update seieges withstood + + this.getCity().setSiegesWithstood(this.getCity().getSiegesWithstood() + 1); + + // Notify players + + ChatSystemMsg msg = new ChatSystemMsg(null, "[Bane Channel]" + this.getCity().getGuild().getName() + " has rallied against " + this.getOwner().getGuild().getName() + ". The siege on " + this.getCity().getCityName() + " has been broken!"); + msg.setMessageType(4); + msg.setChannel(engine.Enum.ChatChannelType.SYSTEM.getChannelID()); + + DispatchMessage.dispatchMsgToAll(msg); + + } + + break; + case CAPTURE: + + // Push event to warehouse + + BaneRecord.updateResolution(this, Enum.RecordEventType.CAPTURE); + + baneRemoved = this.remove(); + + if (baneRemoved) { + + ChatSystemMsg msg = new ChatSystemMsg(null, "[Bane Channel]" + this.getOwner().getGuild().getName() + " have defeated " + this.getCity().getGuild().getName() + " and captured " + this.getCity().getCityName() + '!'); + msg.setMessageType(4); + msg.setChannel(engine.Enum.ChatChannelType.SYSTEM.getChannelID()); + + DispatchMessage.dispatchMsgToAll(msg); + } + break; + case DESTROY: + + // Push event to warehouse + + BaneRecord.updateResolution(this, Enum.RecordEventType.DESTROY); + + baneRemoved = this.remove(); + + if (baneRemoved) { + + ChatSystemMsg msg = new ChatSystemMsg(null, "[Bane Channel]" + this.getOwner().getGuild().getName() + " have defeated " + this.getCity().getGuild().getName() + " and razed " + this.getCity().getCityName() + '!'); + msg.setMessageType(4); + msg.setChannel(engine.Enum.ChatChannelType.SYSTEM.getChannelID()); + + DispatchMessage.dispatchMsgToAll(msg); + } + break; + } + + Zone cityZone = this.getCity().getParent(); + + if (cityZone == null) + return; + + //UNPROTECT ALL SIEGE EQUIPMENT AFTER A BANE + for (Building toUnprotect: cityZone.zoneBuildingSet){ + if (toUnprotect.getBlueprint() != null && toUnprotect.getBlueprint().isSiegeEquip() && toUnprotect.assetIsProtected() == true) + toUnprotect.setProtectionState(ProtectionState.NONE); + } + + } + + public boolean isErrant() { + + boolean isErrant = true; + + if (this.getOwner() == null) + return isErrant; + + + if (this.getOwner().getGuild().isErrant() == true) + return isErrant; + + if (this.getOwner().getGuild().getNation().isErrant() == true) + return isErrant; + + // Bane passes validation + + isErrant = false; + + return isErrant; + } + + /** + * @return the stone + */ + public Building getStone() { + return BuildingManager.getBuilding(this.stoneUUID); + } + + /** + * @return the cityUUID + */ + public int getCityUUID() { + return cityUUID; + } + +} diff --git a/src/engine/objects/BaseClass.java b/src/engine/objects/BaseClass.java new file mode 100644 index 00000000..a00c208e --- /dev/null +++ b/src/engine/objects/BaseClass.java @@ -0,0 +1,220 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.gameManager.DbManager; +import engine.net.ByteBufferWriter; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; + + +public class BaseClass extends AbstractGameObject { + + private final String name; + private final String description; + + private final byte strMod; + private final byte dexMod; + private final byte conMod; + private final byte intMod; + private final byte spiMod; + + private final float healthMod; + private final float manaMod; + private final float staminaMod; + + private int token = 0; + + private final ArrayList skillsGranted; + private final ArrayList powersGranted; + private ArrayList effectsList = new ArrayList<>(); + + + /** + * No Table ID Constructor + */ + public BaseClass(String name, String description, byte strMod, byte dexMod, byte conMod, byte intMod, byte spiMod, + ArrayList allowedRunes, ArrayList skillsGranted, ArrayList powersGranted) { + super(); + this.name = name; + this.description = description; + this.strMod = strMod; + this.dexMod = dexMod; + this.conMod = conMod; + this.intMod = intMod; + this.spiMod = spiMod; + this.healthMod = 1; + this.manaMod = 1; + this.staminaMod = 1; + + this.skillsGranted = skillsGranted; + this.powersGranted = powersGranted; + + } + + /** + * Normal Constructor + */ + public BaseClass(String name, String description, byte strMod, byte dexMod, byte conMod, byte intMod, byte spiMod, + ArrayList allowedRunes, ArrayList skillsGranted, ArrayList powersGranted, int newUUID) { + super(newUUID); + this.name = name; + this.description = description; + this.strMod = strMod; + this.dexMod = dexMod; + this.conMod = conMod; + this.intMod = intMod; + this.spiMod = spiMod; + this.healthMod = 1; + this.manaMod = 1; + this.staminaMod = 1; + this.skillsGranted = skillsGranted; + this.powersGranted = powersGranted; + + } + + + /** + * ResultSet Constructor + */ + public BaseClass(ResultSet rs) throws SQLException { + super(rs); + + this.name = rs.getString("name"); + this.description = rs.getString("description"); + this.strMod = rs.getByte("strMod"); + this.dexMod = rs.getByte("dexMod"); + this.conMod = rs.getByte("conMod"); + this.intMod = rs.getByte("intMod"); + this.spiMod = rs.getByte("spiMod"); + this.token = rs.getInt("token"); + this.healthMod = rs.getInt("healthMod"); + this.manaMod = rs.getInt("manaMod"); + this.staminaMod = rs.getInt("staminaMod"); + this.skillsGranted = DbManager.SkillReqQueries.GET_REQS_FOR_RUNE(this.getObjectUUID()); + this.powersGranted = PowerReq.getPowerReqsForRune(this.getObjectUUID()); + this.effectsList = (DbManager.MobBaseQueries.GET_RUNEBASE_EFFECTS(this.getObjectUUID())); + + } + + /* + * Getters + */ + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public byte getStrMod() { + return strMod; + } + + public byte getDexMod() { + return dexMod; + } + + public byte getConMod() { + return conMod; + } + + public byte getIntMod() { + return intMod; + } + + public byte getSpiMod() { + return spiMod; + } + + public int getToken() { + return this.token; + } + + public float getHealthMod() { + return this.healthMod; + } + + public float getManaMod() { + return this.manaMod; + } + + public float getStaminaMod() { + return this.staminaMod; + } + + public ArrayList getRuneList() { + return RuneBase.AllowedBaseClassRunesMap.get(this.getObjectUUID()); + } + + public ArrayList getSkillsGranted() { + return this.skillsGranted; + } + + public ArrayList getPowersGranted() { + return this.powersGranted; + } + + public ArrayList getEffectsGranted() { + return RuneBaseEffect.RuneIDBaseEffectMap.get(this.getObjectUUID()); + } + + /* + * Utils + */ + public boolean isAllowedRune(RuneBase rb) { + + if (this.getRuneList().contains(rb.getObjectUUID())) + return true; + + if (RuneBase.AllowedBaseClassRunesMap.containsKey(111111)){ + if (RuneBase.AllowedBaseClassRunesMap.get(111111).contains(rb.getObjectUUID())) + return true; + } + return false; + } + + public static void LoadAllBaseClasses(){ + DbManager.BaseClassQueries.GET_ALL_BASE_CLASSES(); + } + + /* + * Serializing + */ + + public static void serializeForClientMsg(BaseClass baseClass, ByteBufferWriter writer) { + serializeForClientMsg(baseClass,writer, 3); + } + + public static void serializeForClientMsg(BaseClass baseClass,ByteBufferWriter writer, int type) { + writer.putInt(type); // For BaseClass + writer.putInt(0); // Pad + writer.putInt(baseClass.getObjectUUID()); + writer.putInt(baseClass.getObjectType().ordinal()); // Is this correct? + writer.putInt(baseClass.getObjectUUID()); + } + + public static BaseClass getBaseClass(final int UUID) { + return DbManager.BaseClassQueries.GET_BASE_CLASS(UUID); + } + + @Override + public void updateDatabase() { + ; //Never update.. + } + + public ArrayList getEffectsList() { + return effectsList; + } + +} diff --git a/src/engine/objects/Blueprint.java b/src/engine/objects/Blueprint.java new file mode 100644 index 00000000..f3c34d47 --- /dev/null +++ b/src/engine/objects/Blueprint.java @@ -0,0 +1,630 @@ +package engine.objects; + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +import engine.Enum.BuildingGroup; +import engine.gameManager.DbManager; +import engine.math.Vector2f; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; + +/* @Summary - Blueprint class is used for determining + characteristics of instanced player owned + structures such as available slots, upgrade + cost/time and the target window symbol icon. + */ +public class Blueprint { + + public final static Vector2f IrikieForgeExtents = new Vector2f(32, 32); + public final static Vector2f IrikieBarracksExtents = new Vector2f(32, 32); + + private static HashMap _blueprints = new HashMap<>(); + private static HashMap _doorNumbers = new HashMap<>(); + public static HashMap _meshLookup = new HashMap<>(); + + private final int blueprintUUID; + private final String name; + private final BuildingGroup buildingGroup; + private final int icon; + private final int maxRank; + private final int maxSlots; + private final int rank1UUID; + private final int rank3UUID; + private final int rank7UUID; + private final int destroyedUUID; + + private Blueprint() { + this.blueprintUUID = 0; + this.name = ""; + this.icon = 0; + this.buildingGroup = BuildingGroup.BANESTONE; + this.maxRank = 0; + this.maxSlots = 0; + this.rank1UUID = 0; + this.rank3UUID = 0; + this.rank7UUID = 0; + this.destroyedUUID = 0; + } + + public Blueprint(ResultSet rs) throws SQLException { + + this.blueprintUUID = rs.getInt("Rank0UUID"); + this.name = rs.getString("MeshName"); + this.icon = rs.getInt("Icon"); + this.buildingGroup = BuildingGroup.valueOf(rs.getString("BuildingGroup")); + this.maxRank = rs.getInt("MaxRank"); + this.maxSlots = rs.getInt("MaxSlots"); + this.rank1UUID = rs.getInt("Rank1UUID"); + this.rank3UUID = rs.getInt("Rank3UUID"); + this.rank7UUID = rs.getInt("Rank7UUID"); + this.destroyedUUID = rs.getInt("DestroyedUUID"); + + } + + // Accessors + + public static Blueprint getBlueprint(int blueprintUUID) { + + return _blueprints.get(blueprintUUID); + + } + + public static BuildingGroup getBuildingGroup(int blueprintUUID) { + + Blueprint blueprint; + + blueprint = _blueprints.get(blueprintUUID); + + return blueprint.buildingGroup; + } + + public static int getMaxShrines(int treeRank) { + + // Returns the number of allowed spires/shrines + // for a given rank. + + int maxShrines; + + switch (treeRank) { + case 0: + case 1: + case 2: + maxShrines = 0; + break; + case 3: + case 4: + maxShrines = 1; + break; + case 5: + case 6: + maxShrines = 2; + break; + case 7: + case 8: + maxShrines = 3; + break; + default: + maxShrines = 0; + + } + + return maxShrines; + } + + public static void loadAllBlueprints() { + + _blueprints = DbManager.BlueprintQueries.LOAD_ALL_BLUEPRINTS(); + + } + + // Method returns a blueprint based on a blueprintUUID + + public static void loadAllDoorNumbers() { + + _doorNumbers = DbManager.BlueprintQueries.LOAD_ALL_DOOR_NUMBERS(); + + } + + public static int getDoorNumberbyMesh(int doorMeshUUID) { + + if (_doorNumbers.containsKey(doorMeshUUID)) + return _doorNumbers.get(doorMeshUUID); + + return 0; + } + + public static boolean isMeshWallPiece(int meshUUID) { + + Blueprint buildingBlueprint = Blueprint.getBlueprint(meshUUID); + + if (buildingBlueprint == null) + return false; + + switch (buildingBlueprint.buildingGroup) { + case WALLSTRAIGHT: + case ARTYTOWER: + case WALLCORNER: + case SMALLGATE: + case WALLSTAIRS: + return true; + default: + break; + } + return false; + + } + + // Method calculates available vendor slots + // based upon the building's current rank + + public static int getNpcMaintCost(int rank) { + int maintCost = Integer.MAX_VALUE; + + maintCost = (9730 * rank) + 1890; + + return maintCost; + } + + public int getMaxRank() { + return maxRank; + } + + public int getMaxSlots() { + if (this.buildingGroup != null && this.buildingGroup.equals(BuildingGroup.BARRACK)) + return 1; + return maxSlots; + } + + // Method returns a mesh UUID for this blueprint + // based upon a given rank. + + public BuildingGroup getBuildingGroup() { + return this.buildingGroup; + } + + // Method returns a cost to upgrade a building to a given rank + // based upon this blueprint's maintenance group + + public int getMaxHealth(int currentRank) { + + int maxHealth; + + // Return 0 health for a destroyed building + // or 1 for a destroyed mine (cleint looting restriction) + + if (currentRank == -1) { + + return this.buildingGroup == BuildingGroup.MINE ? 1 : 0; + } + + // Return 15k for a constructing mesh + + if (currentRank == 0) + return 15000; + + switch (this.buildingGroup) { + + case TOL: + maxHealth = (70000 * currentRank) + 10000; + break; + case BARRACK: + maxHealth = (35000 * currentRank) + 5000; + break; + case BANESTONE: + maxHealth = (170000 * currentRank) - 120000; + break; + case CHURCH: + maxHealth = (28000 * currentRank) + 4000; + break; + case MAGICSHOP: + case FORGE: + case INN: + case TAILOR: + maxHealth = (17500 * currentRank) + 2500; + break; + case VILLA: + case ESTATE: + case FORTRESS: + maxHealth = 300000; + break; + case CITADEL: + maxHealth = 500000; + break; + case SPIRE: + maxHealth = (37000 * currentRank) - 9000; + break; + case GENERICNOUPGRADE: + case SHACK: + case SIEGETENT: + maxHealth = 40000; + break; + case BULWARK: + if (currentRank == 1) + maxHealth = 110000; + else + maxHealth = 40000; + break; + case WALLSTRAIGHT: + case WALLSTRAIGHTTOWER: + case WALLSTAIRS: + maxHealth = 1000000; + break; + case WALLCORNER: + case ARTYTOWER: + maxHealth = 900000; + break; + case SMALLGATE: + maxHealth = 1100000; + break; + case AMAZONHALL: + case CATHEDRAL: + case GREATHALL: + case KEEP: + case THIEFHALL: + case TEMPLEHALL: + case WIZARDHALL: + case ELVENHALL: + case ELVENSANCTUM: + case IREKEIHALL: + case FORESTHALL: + maxHealth = (28000 * currentRank) + 4000; + break; + case MINE: + maxHealth = 125000; + break; + case RUNEGATE: + maxHealth = 100000; + break; + case SHRINE: + maxHealth = 100000; + break; + case WAREHOUSE: + maxHealth = 40000; + break; + + default: + maxHealth = 40000; + break; + + } + return maxHealth; + } + + // Returns number of vendor slots available + // for the building's current rank. + + public int getSlotsForRank(int currentRank) { + + int availableSlots; + + // Early exit for buildings not yet constructed + + if (currentRank == 0) + return 0; + + // Early exit for buildings with single or no slots + + if (this.maxSlots <= 1) + return maxSlots; + + if (this.maxRank == 1 && currentRank == 1) + return getMaxSlots(); + + switch (currentRank) { + + case 1: + case 2: + availableSlots = 1; + break; + case 3: + case 4: + case 5: + case 6: + availableSlots = 2; + break; + case 7: + availableSlots = 3; + break; + case 8: + availableSlots = 1; + break; + default: + availableSlots = 0; + break; + } + + return availableSlots; + } + + // Returns the half extents of this blueprint's + // bounding box, based upon it's buildinggroup + + public int getIcon() { + return this.icon; + } + + public String getName() { + return this.name; + } + + public int getMeshForRank(int targetRank) { + + int targetMesh = this.blueprintUUID; + + // The Blueprint UUID is the 'constructing' mesh so + // we return that value if the rank passed is 0. + + if ((maxRank == 1) && (this.rank1UUID == 0)) { + return blueprintUUID; + } + + // Set the return value to the proper mesh UID for rank + + switch (targetRank) { + + case -1: + targetMesh = this.destroyedUUID; // -1 Rank is a destroyed mesh + break; + case 0: + targetMesh = this.blueprintUUID; // Rank 0 is the 'constructing' mesh + break; + case 1: + case 2: + targetMesh = this.rank1UUID; + break; + case 3: + case 4: + case 5: + case 6: + targetMesh = this.rank3UUID; + break; + case 7: + case 8: + targetMesh = this.rank7UUID; + break; + default: + break; + } + + return targetMesh; + } + + public int getRankCost(int targetRank) { + + // Set a MAXINT rankcost in case something goes wrong + + int rankCost = Integer.MAX_VALUE; + + // Sanity chack for retrieving a rankcost outside proper range + + if ((targetRank > maxRank) || (targetRank < 0)) { + Logger.error( "Attempt to retrieve rankcost for rank of" + targetRank); + return rankCost; + } + + // Select linear equation for rank cost based upon the + // buildings current Maintenance BuildingGroup. + + switch (this.buildingGroup) { + + case GENERICNOUPGRADE: + case WALLSTRAIGHT: + case WALLSTAIRS: + case WALLCORNER: + case SMALLGATE: + case ARTYTOWER: + case SIEGETENT: + case BULWARK: + case BANESTONE: + case SHACK: + break; // This set cannot be upgraded. Returns max integer. + + case TOL: + rankCost = (880000 * targetRank) - 440000; + break; + case BARRACK: + case VILLA: + case ESTATE: + case FORTRESS: + case CITADEL: + rankCost = (451000 * targetRank) - 308000; + break; + case CHURCH: + rankCost = (682000 * targetRank) - 110000; + break; + case FORGE: + case INN: + case TAILOR: + case MAGICSHOP: + rankCost = (440000 * targetRank) - 550000; + break; + case SPIRE: + rankCost = (176000 * targetRank) - 88000; + break; + case AMAZONHALL: + case CATHEDRAL: + case GREATHALL: + case KEEP: + case THIEFHALL: + case TEMPLEHALL: + case WIZARDHALL: + case ELVENHALL: + case ELVENSANCTUM: + case IREKEIHALL: + case FORESTHALL: + rankCost = (682000 * targetRank) - 110000; + break; + default: + Logger.error("Attempt to retrieve rankcost without MaintGroup for " + this.buildingGroup.name()); + break; + } + + return rankCost; + } + + public int getRankTime(int targetRank) { + + // Set a very long rankTime in case something goes wrong + + int rankTime = (Integer.MAX_VALUE / 2); + + // Set all initial construction to a default of 4 hours. + + if (targetRank == 1) + return 4; + + // Sanity chack for retrieving a ranktime outside proper range + + if ((targetRank > maxRank) || (targetRank < 1)) { + Logger.error( "Attempt to retrieve ranktime for rank of" + targetRank); + return rankTime; + } + + // Select equation for rank time based upon the + // buildings current Maintenance BuildingGroup. These values + // are expressed in hours + + switch (this.buildingGroup) { + + case GENERICNOUPGRADE: + break; // Cannot be upgraded + case VILLA: + case ESTATE: + case FORTRESS: + case CITADEL: + rankTime = (7 * targetRank) - 7; + break; + case TOL: + rankTime = (7 * targetRank) - 7; + break; + case BARRACK: + rankTime = (7 * targetRank) - 7; + break; + case CHURCH: + rankTime = (7 * targetRank) - 7; + break; + case FORGE: + case INN: + case TAILOR: + case MAGICSHOP: + rankTime = (7 * targetRank) - 7; + break; + case SPIRE: + rankTime = (4 * targetRank) + 4; + break; + case AMAZONHALL: + case CATHEDRAL: + case GREATHALL: + case KEEP: + case THIEFHALL: + case TEMPLEHALL: + case WIZARDHALL: + case ELVENHALL: + case ELVENSANCTUM: + case IREKEIHALL: + case FORESTHALL: + rankTime = (7 * targetRank) - 7; + break; + default: + Logger.error("Attempt to retrieve ranktime without MaintGroup"); + break; + } + + return rankTime; + } + + public Vector2f getExtents() { + + if (blueprintUUID == 1302600) + return Blueprint.IrikieForgeExtents; + else if (blueprintUUID == 1300600) + return Blueprint.IrikieBarracksExtents; + + return this.buildingGroup.getExtents(); + + } + + public boolean isWallPiece() { + + switch (this.buildingGroup) { + case WALLSTRAIGHT: + case WALLSTAIRS: + case ARTYTOWER: + case WALLCORNER: + case SMALLGATE: + return true; + default: + break; + } + return false; + } + + public boolean isSiegeEquip() { + + switch (this.buildingGroup) { + case BULWARK: + case SIEGETENT: + return true; + default: + break; + } + return false; + + } + + public int getBlueprintUUID() { + return blueprintUUID; + } + + + @Override + public boolean equals(Object object) { + + if ((object instanceof Blueprint) == false) + return false; + + Blueprint blueprint = (Blueprint) object; + + return this.blueprintUUID == blueprint.blueprintUUID; + } + + @Override + public int hashCode() { + + return this.blueprintUUID ; + } + + public int getMaintCost(int rank) { + + int maintCost = Integer.MAX_VALUE; + + switch (this.buildingGroup) { + case TOL: + case BARRACK: + maintCost = (61500 * rank) + 19500; + break; + case SPIRE: + maintCost = (4800 * rank) + 1200; + break; + default: + if (maxRank == 1) + maintCost = 22500; + else + maintCost = (15900 * rank) + 3300; + break; + } + + return maintCost; + } +} diff --git a/src/engine/objects/Boon.java b/src/engine/objects/Boon.java new file mode 100644 index 00000000..3c3bcb6d --- /dev/null +++ b/src/engine/objects/Boon.java @@ -0,0 +1,64 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum.ShrineType; +import engine.gameManager.DbManager; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; + + + +public class Boon { + + private ShrineType shrineType; + private int amount; + private int itemBaseID; + public static HashMap> GetBoonsForItemBase = new HashMap<>(); + + + /** + * ResultSet Constructor + */ + public Boon(ResultSet rs) throws SQLException { + + this.shrineType = ShrineType.valueOf(rs.getString("shrineType")); + this.itemBaseID = rs.getInt("itemBaseID"); + this.amount = rs.getInt("amount"); + } + + public int getAmount() { + return this.amount; + } + + + + public int getItemBaseID() { + return itemBaseID; + } + + + public ShrineType getShrineType() { + return shrineType; + } + + + public static void HandleBoonListsForItemBase(int itemBaseID){ + ArrayList boons = null; + boons = DbManager.BoonQueries.GET_BOON_AMOUNTS_FOR_ITEMBASEUUID(itemBaseID); + if (boons != null) + GetBoonsForItemBase.put(itemBaseID, boons); + } + + +} diff --git a/src/engine/objects/Building.java b/src/engine/objects/Building.java new file mode 100644 index 00000000..d9fca2d5 --- /dev/null +++ b/src/engine/objects/Building.java @@ -0,0 +1,1761 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.objects; + +import engine.Enum; +import engine.Enum.*; +import engine.InterestManagement.HeightMap; +import engine.InterestManagement.RealmMap; +import engine.InterestManagement.WorldGrid; +import engine.db.archive.CityRecord; +import engine.db.archive.DataWarehouse; +import engine.db.archive.MineRecord; +import engine.gameManager.BuildingManager; +import engine.gameManager.DbManager; +import engine.gameManager.ZoneManager; +import engine.job.JobContainer; +import engine.job.JobScheduler; +import engine.jobs.DoorCloseJob; +import engine.jobs.SiegeSpireWithdrawlJob; +import engine.math.Bounds; +import engine.math.Vector3f; +import engine.math.Vector3fImmutable; +import engine.net.ByteBufferWriter; +import engine.net.DispatchMessage; +import engine.net.client.msg.ApplyBuildingEffectMsg; +import engine.net.client.msg.UpdateObjectMsg; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantReadWriteLock; + + +public class Building extends AbstractWorldObject { + + // Used for thread safety + + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + + /* The Blueprint class has methods able to derive + * all defining characteristics of this building, + */ + private int blueprintUUID = 0; + public int meshUUID; + private float w = 1.0f; + private Vector3f meshScale = new Vector3f(1.0f, 1.0f, 1.0f); + private int doorState = 0; + private int ownerUUID = 0; //NPC or Character--check ownerIsNPC flag + private int _strongboxValue = 0; + private int maxGold; + private int effectFlags = 0; + private String name = ""; + private int rank; + private boolean ownerIsNPC = true; + private boolean spireIsActive = false; + public Zone parentZone; + public boolean reverseKOS; + public int reserve = 0; + + // Variables NOT to be stored in db + + protected Resists resists; + public float statLat; + public float statLon; + public float statAlt; + private ConcurrentHashMap timers = null; + private ConcurrentHashMap timestamps = null; + private final ConcurrentHashMap hirelings = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private final HashMap doorJobs = new HashMap<>(); + private ConcurrentHashMap friends = new ConcurrentHashMap<>(); + private ConcurrentHashMap condemned = new ConcurrentHashMap<>(); + public LocalDateTime upgradeDateTime = null; + public LocalDateTime taxDateTime = null; + private ProtectionState protectionState = ProtectionState.NONE; + + public ArrayList patrolPoints = new ArrayList<>(); + public ArrayList sentryPoints = new ArrayList<>(); + public TaxType taxType = TaxType.NONE; + public int taxAmount; + public boolean enforceKOS = false; + + public int parentBuildingID; + public boolean isFurniture = false; + + public int floor; + public int level; + public HashMap fidelityNpcs = new HashMap<>(); + public AtomicBoolean isDeranking = new AtomicBoolean(false); + private ArrayList children = null; + public LocalDateTime maintDateTime; + + /** + * ResultSet Constructor + */ + + public Building(ResultSet rs) throws SQLException { + super(rs); + + float scale; + Blueprint blueprint = null; + + try { + this.meshUUID = rs.getInt("meshUUID"); + this.setObjectTypeMask(MBServerStatics.MASK_BUILDING); + this.blueprintUUID = rs.getInt("blueprintUUID"); + this.gridObjectType = GridObjectType.STATIC; + this.parentZone = DbManager.ZoneQueries.GET_BY_UID(rs.getLong("parent")); + this.name = rs.getString("name"); + this.ownerUUID = rs.getInt("ownerUUID"); + + // Orphaned Object Sanity Check + //This was causing ABANDONED Tols. + // if (objectType == DbObjectType.INVALID) + // this.ownerUUID = 0; + + this.doorState = rs.getInt("doorState"); + this.setHealth(rs.getInt("currentHP")); + this.w = rs.getFloat("w"); + this.setRot(new Vector3f(0f, rs.getFloat("rotY"), 0f)); + this.reverseKOS = rs.getByte("reverseKOS") == 1 ? true : false; + + scale = rs.getFloat("scale"); + this.meshScale = new Vector3f(scale, scale, scale); + + this.rank = rs.getInt("rank"); + this.parentBuildingID = rs.getInt("parentBuildingID"); + + //create a new list if the building is a parent and not a child. + + if (this.parentBuildingID == 0) + this.children = new ArrayList<>(); + + this.floor = rs.getInt("floor"); + this.level = rs.getInt("level"); + this.isFurniture = (rs.getBoolean("isFurniture")); + + // Lookup building blueprint + + if (this.blueprintUUID == 0) + blueprint = Blueprint._meshLookup.get(meshUUID); + else + blueprint = this.getBlueprint(); + + // Log error if something went horrible wrong + + if ((this.blueprintUUID != 0) && (blueprint == null)) + Logger.error( "Invalid blueprint for object: " + this.getObjectUUID()); + + // Note: We handle R8 tree edge case for mesh and health + // after city is loaded to avoid recursive result set call + // in City resulting in a stack ovreflow. + + if (blueprint != null) { + + // Only switch mesh for player dropped structures + + if (this.blueprintUUID != 0) + this.meshUUID = blueprint.getMeshForRank(rank); + + this.healthMax = blueprint.getMaxHealth(this.rank); + + // If this object has no blueprint but is a blueprint + // mesh then set it's current health to max health + + if (this.blueprintUUID == 0) + this.setHealth(healthMax); + + if (blueprint.getBuildingGroup().equals(BuildingGroup.BARRACK)) + this.patrolPoints = DbManager.BuildingQueries.LOAD_PATROL_POINTS(this); + + } else{ + this.healthMax = 100000; // Structures with no blueprint mesh + this.setHealth(healthMax); + } + + // Null out blueprint if not needed (npc building) + + if (blueprintUUID == 0) + blueprint = null; + + resists = new Resists("Building"); + this.statLat = rs.getFloat("locationX"); + this.statAlt = rs.getFloat("locationY"); + this.statLon = rs.getFloat("locationZ"); + + if (this.parentZone != null){ + if (this.parentBuildingID != 0){ + Building parentBuilding = BuildingManager.getBuilding(this.parentBuildingID); + if (parentBuilding != null){ + this.setLoc(new Vector3fImmutable(this.statLat + this.parentZone.absX + parentBuilding.statLat, this.statAlt + this.parentZone.absY + parentBuilding.statAlt, this.statLon + this.parentZone.absZ + parentBuilding.statLon)); + }else{ + this.setLoc(new Vector3fImmutable(this.statLat + this.parentZone.absX, this.statAlt + this.parentZone.absY, this.statLon + this.parentZone.absZ)); + + } + } else { + + // Altitude of this building is derived from the heightmap engine. + + Vector3fImmutable tempLoc = new Vector3fImmutable(this.statLat + this.parentZone.absX, 0, this.statLon + this.parentZone.absZ); + tempLoc = new Vector3fImmutable(tempLoc.x, HeightMap.getWorldHeight(tempLoc), tempLoc.z); + this.setLoc(tempLoc); + } + } + + this._strongboxValue = rs.getInt("currentGold"); + this.maxGold = 15000000; // *** Refactor to blueprint method + this.reserve = rs.getInt("reserve"); + + // Does building have a protection contract? + this.taxType = TaxType.valueOf(rs.getString("taxType")); + this.taxAmount = rs.getInt("taxAmount"); + this.protectionState = ProtectionState.valueOf(rs.getString("protectionState")); + + java.sql.Timestamp maintTimeStamp = rs.getTimestamp("maintDate"); + + if (maintTimeStamp != null) + this.maintDateTime = LocalDateTime.ofInstant(maintTimeStamp.toInstant(), ZoneId.systemDefault()); + + java.sql.Timestamp taxTimeStamp = rs.getTimestamp("taxDate"); + + if (taxTimeStamp != null) + this.taxDateTime = LocalDateTime.ofInstant(taxTimeStamp.toInstant(), ZoneId.systemDefault()); + + java.sql.Timestamp upgradeTimeStamp = rs.getTimestamp("upgradeDate"); + + if (upgradeTimeStamp != null) + this.upgradeDateTime = LocalDateTime.ofInstant(upgradeTimeStamp.toInstant(), ZoneId.systemDefault()); + + } catch (Exception e) { + + Logger.error( "Failed for object " + this.blueprintUUID + ' ' + this.getObjectUUID() + e.toString()); + } + } + + /* + * Getters + */ + + public final boolean isRanking() { + + return this.upgradeDateTime != null; + } + + public final int getRank() { + return rank; + } + + public final int getOwnerUUID() { + return ownerUUID; + } + + public final boolean isOwnerIsNPC() { + return ownerIsNPC; + } + + public final City getCity() { + + if (this.parentZone == null) + return null; + + if (this.getBlueprint() != null && this.getBlueprint().isSiegeEquip() && this.protectionState.equals(ProtectionState.PROTECTED)){ + if (this.getGuild() != null){ + if (this.getGuild().getOwnedCity() != null){ + if (this.getLoc().isInsideCircle(this.getGuild().getOwnedCity().getLoc(), Enum.CityBoundsType.SIEGE.extents)) + return this.getGuild().getOwnedCity(); + }else{ + Bane bane = Bane.getBaneByAttackerGuild(this.getGuild()); + + if (bane != null){ + if (bane.getCity() != null){ + if (this.getLoc().isInsideCircle(bane.getCity().getLoc(), Enum.CityBoundsType.SIEGE.extents)) + return bane.getCity(); + } + } + } + } + } + if (this.parentZone.isPlayerCity() == false) + return null; + + return City.getCity(this.parentZone.getPlayerCityUUID()); + + } + + public final String getCityName() { + + City city = getCity(); + + if (city != null) + return city.getName(); + + return ""; + } + + public final Blueprint getBlueprint() { + + if (this.blueprintUUID == 0) + return null; + + return Blueprint.getBlueprint(this.blueprintUUID); + + } + + public final int getBlueprintUUID() { + + return this.blueprintUUID; + } + + public final void setCurrentHitPoints(Float CurrentHitPoints) { + this.addDatabaseJob("health", MBServerStatics.ONE_MINUTE); + this.setHealth(CurrentHitPoints); + } + + public final LocalDateTime getUpgradeDateTime() { + lock.readLock().lock(); + try { + return upgradeDateTime; + } finally { + lock.readLock().unlock(); + } + } + + public final float modifyHealth(final float value, final AbstractCharacter attacker) { + + if (this.rank == -1) + return 0f; + + boolean worked = false; + Float oldHealth=0f, newHealth=0f; + while (!worked) { + if (this.rank == -1) + return 0f; + oldHealth = this.health.get(); + newHealth = oldHealth + value; + if (newHealth > this.healthMax) + newHealth = healthMax; + worked = this.health.compareAndSet(oldHealth, newHealth); + } + + if (newHealth < 0) { + if (this.isDeranking.compareAndSet(false, true)) { + this.destroyOrDerank(attacker); + } + + return newHealth - oldHealth; + } else + this.addDatabaseJob("health", MBServerStatics.ONE_MINUTE); + + if (value < 0) + Mine.SendMineAttackMessage(this); + + return newHealth - oldHealth; + + + } + + //This method is to handle when a building is damaged below 0 health. + //Either destroy or derank it. + + public final void destroyOrDerank(AbstractCharacter attacker) { + + Blueprint blueprint; + City city; + + // Sanity check: Early exit if a non + // blueprinted object is attempting to + // derank. + + if (this.blueprintUUID == 0) + return; + + blueprint = this.getBlueprint(); + city = this.getCity(); + + // Special handling of destroyed Banes + + if (blueprint.getBuildingGroup() == BuildingGroup.BANESTONE) { + city.getBane().endBane(SiegeResult.DEFEND); + return; + } + + // Special handling of warehouses + + if (blueprint.getBuildingGroup() == BuildingGroup.WAREHOUSE) + if (city != null) + city.setWarehouseBuildingID(0); + + // Special handling of destroyed Spires + + if ((blueprint.getBuildingGroup() == BuildingGroup.SPIRE) && this.rank == 1) + this.disableSpire(true); + + // Special handling of destroyed Mines + + if (blueprint.getBuildingGroup() == BuildingGroup.MINE + && this.rank == 1) { + + Mine mine = Mine.getMineFromTower(this.getObjectUUID()); + + if (mine != null) { + + // Warehouse mine destruction event + + MineRecord mineRecord = MineRecord.borrow(mine, attacker, RecordEventType.DESTROY); + DataWarehouse.pushToWarehouse(mineRecord); + + this.setRank(-1); + this.setCurrentHitPoints((float) 1); + this.healthMax = (float) 1; + this.meshUUID = this.getBlueprint().getMeshForRank(this.rank); + mine.handleDestroyMine(); + this.getBounds().setBounds(this); + this.refresh(true); + return; + } + } + + // Special handling of deranking Trees + + if (blueprint.getBuildingGroup() == BuildingGroup.TOL) { + derankTreeOfLife(); + return; + } + + // If codepath reaches here then it's a regular + // structure not requiring special handling. + // Time to either derank or destroy the building. + + if ((this.rank - 1) < 1) + this.setRank(-1); + else + this.setRank(this.rank - 1); + + } + + private void derankTreeOfLife() { + + City city; + Bane bane; + Realm cityRealm;ArrayList spireBuildings = new ArrayList<>(); + ArrayList shrineBuildings = new ArrayList<>(); + ArrayList barracksBuildings = new ArrayList<>(); + Building spireBuilding; + Building shrineBuilding; + SiegeResult siegeResult; + AbstractCharacter newOwner; + + city = this.getCity(); + + if (city == null) { + Logger.error("No city for tree of uuid" + this.getObjectUUID()); + return; + } + + bane = city.getBane(); + + // We need to collect the spires and shrines on the citygrid in case + // they will be deleted as excess as the tree deranks. + + for (Building building : city.getParent().zoneBuildingSet) { + + //dont add -1 rank buildings. + if (building.rank <= 0) + continue; + if (building.getBlueprint() != null && building.getBlueprint().getBuildingGroup() == BuildingGroup.SPIRE) + spireBuildings.add(building); + + if (building.getBlueprint() != null && building.getBlueprint().getBuildingGroup() == BuildingGroup.SHRINE) + shrineBuildings.add(building); + + if (building.getBlueprint() != null && building.getBlueprint().getBuildingGroup() == BuildingGroup.BARRACK) + barracksBuildings.add(building); + } + + // A tree can only hold so many spires. As it deranks we need to delete + // the excess + + if (spireBuildings.size() > Blueprint.getMaxShrines(this.rank - 1)) { + + spireBuilding = spireBuildings.get(0); + + // Disable and delete a random spire + + if (spireBuilding != null) { + spireBuilding.disableSpire(true); + spireBuilding.setRank(-1); + } + } + + if (shrineBuildings.size() > Blueprint.getMaxShrines(this.rank - 1)) { + + shrineBuilding = shrineBuildings.get(0); + + // Delete a random shrine + + if (shrineBuilding != null) + shrineBuilding.setRank(-1); + } + + if (barracksBuildings.size() > this.rank - 1) { + + Building barracksBuilding = barracksBuildings.get(0); + + // Delete a random barrack + + if (barracksBuilding != null) + barracksBuilding.setRank(-1); + } + + // If the tree is R8 and deranking, we need to update it's + // mesh along with buildings losing their health bonus + + if (this.rank == 8) { + + cityRealm = city.getRealm(); + + if (cityRealm != null) + cityRealm.abandonRealm(); + + for (Building cityBuilding : this.parentZone.zoneBuildingSet) { + + if ((cityBuilding.getBlueprint() != null && cityBuilding.getBlueprint().getBuildingGroup() != BuildingGroup.TOL) + && (cityBuilding.getBlueprint().getBuildingGroup() != BuildingGroup.BANESTONE)) { + cityBuilding.healthMax = cityBuilding.getBlueprint().getMaxHealth(cityBuilding.rank); + } + + if (cityBuilding.health.get() > cityBuilding.healthMax) + cityBuilding.setHealth(cityBuilding.healthMax); + } + } + + // Tree is simply deranking. + // Let's do so and early exit + + if (this.rank > 1) { + this.setRank(rank - 1); + City.lastCityUpdate = System.currentTimeMillis(); + return; + } + + // Handling of exploding TOL's + + // Must remove a bane before considering destruction of a TOL + + if (bane != null) { + + // Cache the new owner + + newOwner = Guild.GetGL(bane.getOwner().getGuild()); + + this.isDeranking.compareAndSet(false, true); + + if ((bane.getOwner().getGuild().getGuildState() == GuildState.Sovereign) || + (bane.getOwner().getGuild().getGuildState() == GuildState.Protectorate) || + (bane.getOwner().getGuild().getGuildState() == GuildState.Province) || + (bane.getOwner().getGuild().getGuildState() == GuildState.Nation)) + siegeResult = SiegeResult.DESTROY; + else + siegeResult = SiegeResult.CAPTURE; + + // Remove realm if city had one + + Realm realm = RealmMap.getRealmAtLocation(city.getLoc()); + + if (realm != null) + if (realm.isRealmFullAfterBane()) + siegeResult = SiegeResult.DESTROY; + + city.getBane().endBane(siegeResult); + + // If it's a capture bane transfer the tree and exit + + if (siegeResult.equals(SiegeResult.CAPTURE)) { + city.transfer(newOwner); + CityRecord cityRecord = CityRecord.borrow(city, RecordEventType.CAPTURE); + DataWarehouse.pushToWarehouse(cityRecord); + return; + } + } // end removal of bane + + // if codepath reaches here then we can now destroy the tree and the city + + CityRecord cityRecord = CityRecord.borrow(city, RecordEventType.DESTROY); + DataWarehouse.pushToWarehouse(cityRecord); + + city.destroy(); + + } + + public float getCurrentHitpoints(){ + return this.health.get(); + } + + // Return the maint cost in gold associated with this structure + + public int getMaintCost() { + + int maintCost =0; + + // Add cost for building structure + + maintCost += this.getBlueprint().getMaintCost(rank); + + // Add costs associated with hirelings + + for (AbstractCharacter npc : this.hirelings.keySet()) { + + if (npc.getObjectType() != GameObjectType.NPC) + continue; + + + + maintCost += Blueprint.getNpcMaintCost(npc.getRank()); + } + + return maintCost; + } + + + public final void submitOpenDoorJob(int doorID) { + + //cancel any outstanding door close jobs for this door + + if (this.doorJobs.containsKey(doorID)) { + this.doorJobs.get(doorID).cancelJob(); + this.doorJobs.remove(doorID); + } + + //add new door close job + + DoorCloseJob dcj = new DoorCloseJob(this, doorID); + this.doorJobs.put(doorID, dcj); + JobScheduler.getInstance().scheduleJob(dcj, MBServerStatics.DOOR_CLOSE_TIMER); + } + + public final float getMaxHitPoints() { + return this.healthMax; + } + + public final void setMaxHitPoints(float maxHealth) { + this.healthMax = maxHealth; + } + + public final void setName(String value) { + + if (DbManager.BuildingQueries.CHANGE_NAME(this, value) == false) + return; + + this.name = value; + this.updateName(); + } + + public final void setw(float value) { + this.w = value; + } + + public final float getw() { + return this.w; + } + + public final void setMeshScale(Vector3f value) { + this.meshScale = value; + } + + public final Vector3f getMeshScale() { + return this.meshScale; + } + + public final int getMeshUUID() { + return this.meshUUID; + } + + public final Resists getResists() { + return this.resists; + } + + public final Zone getParentZone() { + return this.parentZone; + } + + public final int getParentZoneID() { + + if (this.parentZone == null) + return 0; + + return this.parentZone.getObjectUUID(); + } + + public final void setParentZone(Zone zone) { + + //update ZoneManager's zone building list + if (zone != null) + if (this.parentZone != null) { + + this.parentZone.zoneBuildingSet.remove(this); + zone.zoneBuildingSet.add(this); + + } else + zone.zoneBuildingSet.add(this); + else if (this.parentZone != null) + this.parentZone.zoneBuildingSet.remove(this); + + if (this.parentZone == null) { + this.parentZone = zone; + this.setLoc(new Vector3fImmutable(this.statLat + zone.absX, this.statAlt + zone.absY, this.statLon + zone.absZ)); + } else + this.parentZone = zone; + } + + //Sets the relative position to a parent zone + + public final void setRelPos(Zone zone, float locX, float locY, float locZ) { + + //update ZoneManager's zone building list + + if (zone != null) + if (this.parentZone != null) { + if (zone.getObjectUUID() != this.parentZone.getObjectUUID()) { + this.parentZone.zoneBuildingSet.remove(this); + zone.zoneBuildingSet.add(this); + } + } else + zone.zoneBuildingSet.add(this); + else if (this.parentZone != null) + this.parentZone.zoneBuildingSet.remove(this); + + this.statLat = locX; + this.statAlt = locY; + this.statLon = locZ; + this.parentZone = zone; + } + + public float getStatLat() { + return statLat; + } + + public float getStatLon() { + return statLon; + } + + public float getStatAlt() { + return statAlt; + } + + public Guild getGuild() { + + AbstractCharacter buildingOwner; + + buildingOwner = this.getOwner(); + + if (buildingOwner != null) + return buildingOwner.getGuild(); + else + return Guild.getErrantGuild(); + } + + public int getEffectFlags() { + return this.effectFlags; + } + + public void addEffectBit(int bit) { + this.effectFlags |= bit; + } + + public void removeAllVisualEffects() { + this.effectFlags = 0; + ApplyBuildingEffectMsg applyBuildingEffectMsg = new ApplyBuildingEffectMsg(3276859, 1, this.getObjectType().ordinal(), this.getObjectUUID(), 0); + DispatchMessage.sendToAllInRange(this, applyBuildingEffectMsg); + } + + public void removeEffectBit(int bit) { + this.effectFlags &= (~bit); + + } + + @Override + public String getName() { + return this.name; + } + + /* + * Utils + */ + + public final AbstractCharacter getOwner() { + + if (this.ownerUUID == 0) + return null; + if (this.ownerIsNPC) + return NPC.getFromCache(this.ownerUUID); + + return PlayerCharacter.getFromCache(this.ownerUUID); + + } + + public final String getOwnerName() { + AbstractCharacter owner = this.getOwner(); + if (owner != null) + return owner.getName(); + return ""; + } + + public final String getGuildName() { + Guild g = getGuild(); + if (g != null) + return g.getName(); + return "None"; + } + + + /* + * Serializing + */ + + public static void _serializeForClientMsg(Building building, ByteBufferWriter writer) { + writer.putInt(building.getObjectType().ordinal()); + writer.putInt(building.getObjectUUID()); + writer.putInt(0); // pad + + writer.putInt(building.meshUUID); + + writer.putInt(0); // pad + + if (building.parentBuildingID != 0){ + + writer.putFloat(building.statLat); + writer.putFloat(building.statAlt); + writer.putFloat(building.statLon); + + }else{ + writer.putFloat(building.getLoc().getX()); + writer.putFloat(building.getLoc().getY()); // Y location + writer.putFloat(building.getLoc().getZ()); + } + + writer.putFloat(building.w); + writer.putFloat(0f); + writer.putFloat(building.getRot().y); + + writer.putFloat(0f); + writer.putFloat(building.meshScale.getX()); + writer.putFloat(building.meshScale.getY()); + writer.putFloat(building.meshScale.getZ()); + + if (building.parentBuildingID != 0){ + writer.putInt(GameObjectType.Building.ordinal()); + writer.putInt(building.parentBuildingID); + writer.putInt(building.floor); + writer.putInt(building.level); + + }else{ + writer.putInt(0); // Pad //Parent + writer.putInt(0); // Pad + writer.putInt(-1); // Static + writer.putInt(-1); // Static + } + + writer.put((byte)0); // 0 + writer.putFloat(3); // 3 + writer.putInt(GameObjectType.Building.ordinal()); + writer.putInt(building.getObjectUUID()); + + if (building.ownerIsNPC) + writer.putInt(GameObjectType.NPC.ordinal()); + else + writer.putInt(GameObjectType.PlayerCharacter.ordinal()); + + writer.putInt(building.ownerUUID); + + writer.put((byte) 1); // End Datablock + writer.putFloat(building.health.get()); + writer.putFloat(building.healthMax); + + if (building.blueprintUUID == 0) + writer.putInt(0); + else + writer.putInt(building.getBlueprint().getIcon()); + + writer.putInt(building.effectFlags); + + writer.put((byte) 1); // End Datablock + Guild g = building.getGuild(); + Guild nation = null; + + if (g == null) { + + for (int i = 0; i < 3; i++) { + writer.putInt(16); + } + for (int i = 0; i < 2; i++) { + writer.putInt(0); + } + } else { + GuildTag._serializeForDisplay(g.getGuildTag(),writer); + nation = g.getNation(); + } + writer.put((byte) 1); // End Datablock? + if (nation == null) { + for (int i = 0; i < 3; i++) { + writer.putInt(16); + } + for (int i = 0; i < 2; i++) { + writer.putInt(0); + } + } else + GuildTag._serializeForDisplay(nation.getGuildTag(),writer); + writer.putString(building.name); + writer.put((byte) 0); // End datablock + } + + /* + * Database + */ + + @Override + public void updateDatabase() { + + // *** Refactor : Log error here to see if it's ever called + } + + public final LocalDateTime getDateToUpgrade() { + return upgradeDateTime; + } + + public final boolean setStrongboxValue(int newValue) { + + boolean success = true; + + try { + DbManager.BuildingQueries.SET_PROPERTY(this, "currentGold", newValue); + this._strongboxValue = newValue; + } catch (Exception e) { + success = false; + Logger.error( "Error writing to database"); + } + + return success; + } + + public final int getStrongboxValue() { + return _strongboxValue; + } + + public final void setMeshUUID(int value) { + this.meshUUID = value; + } + + public final void setRank(int newRank) { + + int newMeshUUID; + boolean success; + + + // If this building has no blueprint then set rank and exit immediatly. + + if (this.blueprintUUID == 0 || this.getBlueprint() != null && this.getBlueprint().getBuildingGroup().equals(BuildingGroup.MINE)) { + this.rank = newRank; + DbManager.BuildingQueries.CHANGE_RANK(this.getObjectUUID(), newRank); + return; + } + + // Delete any upgrade jobs before doing anything else. It won't quite work + // if in a few lines we happen to delete this building. + + JobContainer jc = this.getTimers().get("UPGRADE"); + + if (jc != null) { + if (!JobScheduler.getInstance().cancelScheduledJob(jc)) + Logger.error( "failed to cancel existing upgrade job."); + } + + // Attempt write to database, or delete the building + // if we are destroying it. + + if (newRank == -1) + success = DbManager.BuildingQueries.DELETE_FROM_DATABASE(this); + else + success = DbManager.BuildingQueries.updateBuildingRank(this, newRank); + + if (success == false) { + Logger.error("Error writing to database UUID: " + this.getObjectUUID()); + return; + } + + this.isDeranking.compareAndSet(false, true); + + // Change the building's rank + + this.rank = newRank; + + // New rank means new mesh + + newMeshUUID = this.getBlueprint().getMeshForRank(this.rank); + this.meshUUID = newMeshUUID; + + // New rank mean new max hitpoints. + + this.healthMax = this.getBlueprint().getMaxHealth(this.rank); + this.setCurrentHitPoints(this.healthMax); + + if (this.getUpgradeDateTime() != null) + BuildingManager.setUpgradeDateTime(this, null, 0); + + // If we destroyed this building make sure to turn off + // protection + + if (this.rank == -1) + this.protectionState = ProtectionState.NONE; + + if ((this.getBlueprint().getBuildingGroup() == BuildingGroup.TOL) + && (this.rank == 8)) + this.meshUUID = Realm.getRealmMesh(this.getCity());; + + // update object to clients + + this.refresh(true); + if (this.getBounds() != null) + this.getBounds().setBounds(this); + + // Cleanup hirelings resulting from rank change + + BuildingManager.cleanupHirelings(this); + + this.isDeranking.compareAndSet(true, false); + } + + public final void refresh(boolean newMesh) { + + if (newMesh) + WorldGrid.updateObject(this); + else { + UpdateObjectMsg uom = new UpdateObjectMsg(this, 3); + DispatchMessage.sendToAllInRange(this, uom); + } + } + + public final void updateName() { + + UpdateObjectMsg uom = new UpdateObjectMsg(this, 2); + DispatchMessage.sendToAllInRange(this, uom); + + } + + // *** Refactor: Can't we just use setRank() for this? + + public final void rebuildMine(){ + this.setRank(1); + this.meshUUID = this.getBlueprint().getMeshForRank(this.rank); + // New rank mean new max hitpoints. + this.healthMax = this.getBlueprint().getMaxHealth(this.rank); + this.setCurrentHitPoints(this.healthMax); + this.getBounds().setBounds(this); + } + + public final void refreshGuild() { + + UpdateObjectMsg uom = new UpdateObjectMsg(this, 5); + DispatchMessage.sendToAllInRange(this, uom); + + } + + public int getMaxGold() { + return maxGold; + } + + //This returns if a player is allowed access to control the building + + @Override + public void runAfterLoad() { + + try { + + this.parentZone.zoneBuildingSet.add(this); + + // Submit upgrade job if building is currently set to rank. + + + + try { + DbObjectType objectType = DbManager.BuildingQueries.GET_UID_ENUM(this.ownerUUID); + this.ownerIsNPC = (objectType == DbObjectType.NPC); + } catch (Exception e) { + this.ownerIsNPC = false; + Logger.error("Failed to find Object Type for owner " + this.ownerUUID+ " Location " + this.getLoc().toString()); + } + + try{ + DbManager.BuildingQueries.LOAD_ALL_FRIENDS_FOR_BUILDING(this); + DbManager.BuildingQueries.LOAD_ALL_CONDEMNED_FOR_BUILDING(this); + }catch(Exception e){ + Logger.error( this.getObjectUUID() + " failed to load friends/condemned." + e.getMessage()); + } + + //LOad Owners in Cache so we do not have to continuely look in the db for owner. + + if (this.ownerIsNPC){ + if (NPC.getNPC(this.ownerUUID) == null) + Logger.info( "Building UID " + this.getObjectUUID() + " Failed to Load NPC Owner with ID " + this.ownerUUID+ " Location " + this.getLoc().toString()); + + }else if (this.ownerUUID != 0){ + if (PlayerCharacter.getPlayerCharacter(this.ownerUUID) == null){ + Logger.info( "Building UID " + this.getObjectUUID() + " Failed to Load Player Owner with ID " + this.ownerUUID + " Location " + this.getLoc().toString()); + } + } + + // Apply health bonus and special mesh for realm if applicable + if ((this.getCity() != null) && this.getCity().getTOL() != null && (this.getCity().getTOL().rank == 8)) { + + // Update mesh accordingly + if (this.getBlueprint() != null && this.getBlueprint().getBuildingGroup() == BuildingGroup.TOL) + this.meshUUID = Realm.getRealmMesh(this.getCity()); + + // Apply realm capital health bonus. + // Do not apply bonus to banestones or TOL's. *** Refactor: + // Possibly only protected buildings? Needs some thought. + + float missingHealth = 0; + + if (this.health.get() != 0) + missingHealth = this.healthMax-this.health.get(); + + if ((this.getBlueprint() != null && this.getBlueprint().getBuildingGroup() != BuildingGroup.TOL) + && (this.getBlueprint().getBuildingGroup() != BuildingGroup.BANESTONE)){ + this.healthMax += (this.healthMax * Realm.getRealmHealthMod(this.getCity())); + + if (this.health.get() != 0) + this.health.set(this.healthMax - missingHealth); + + if (this.health.get() > this.healthMax) + this.health.set(this.healthMax); + } + } + + // Set bounds for this building + + Bounds buildingBounds = Bounds.borrow(); + buildingBounds.setBounds(this); + this.setBounds(buildingBounds); + + //create a new list for children if the building is not a child. children list default is null. + //TODO Remove Furniture/Child buildings from building class and move them into a seperate class. + if (this.parentBuildingID == 0) + this.children = new ArrayList<>(); + + if (this.parentBuildingID != 0){ + Building parent = BuildingManager.getBuildingFromCache(this.parentBuildingID); + + if (parent != null){ + parent.children.add(this); + //add furniture to region cache. floor and level are reversed in database, //TODO Fix + Regions region = BuildingManager.GetRegion(parent, this.level,this.floor, this.getLoc().x, this.getLoc().z); + if (region != null) + Regions.FurnitureRegionMap.put(this.getObjectUUID(), region); + } + + } + + if (this.upgradeDateTime != null) + BuildingManager.submitUpgradeJob(this); + + // Run Once move buildings + // 64 / -64 to align with pads + + // Don't move furniture +/* + if (parentBuildingID != 0) + return; + + // Don't move buildings not on a city zone + // or buildings that are in npc owned city + + City city = getCity(); + + if (city == null) + return; + + if (city.getIsNpcOwned() == 1) + return; + + PullCmd.MoveBuilding(this, null, getLoc().add(new Vector3fImmutable(0, 0, 0)), getParentZone()); +*/ + + }catch (Exception e){ + e.printStackTrace(); + } + } + + public synchronized boolean setOwner(AbstractCharacter newOwner) { + + int newOwnerID; + if (newOwner == null) + newOwnerID = 0; + else + newOwnerID = newOwner.getObjectUUID(); + + // ***BONUS CODE BELOW! + /* + if (newOwner == null) { + this.ownerIsNPC = false; + this.ownerUUID = 0; + } else if (newOwner instanceof PlayerCharacter) { + this.ownerIsNPC = false; + this.ownerUUID = newOwner.getObjectUUID(); + } else { + this.ownerIsNPC = true; + this.ownerUUID = newOwner.getObjectUUID(); + } + */ + + try { + // Save new owner to database + + if (!DbManager.BuildingQueries.updateBuildingOwner(this, newOwnerID)) + return false; + + if (newOwner == null) { + this.ownerIsNPC = false; + this.ownerUUID = 0; } + else { + this.ownerUUID = newOwner.getObjectUUID(); + this.ownerIsNPC = (newOwner.getObjectType() == GameObjectType.NPC); + } + + + // Set new guild for hirelings and refresh all clients + + this.refreshGuild(); + BuildingManager.refreshHirelings(this); + + } catch (Exception e) { + Logger.error( "Error updating owner! UUID: " + this.getObjectUUID()); + return false; + } + + return true; + + } + + //This turns on and off low damage effect for building + + public void toggleDamageLow(boolean on) { + if (on) + addEffectBit(2); + else + removeEffectBit(2); + } + + //This turns on and off medium damage effect for building + + public void toggleDamageMedium(boolean on) { + if (on) + addEffectBit(4); + else + removeEffectBit(4); + } + + //This turns on and off high damage effect for building + + public void toggleDamageHigh(boolean on) { + if (on) + addEffectBit(8); + else + removeEffectBit(8); + } + + //This clears all damage effects on a building + public void clearDamageEffect() { + toggleDamageLow(false); + toggleDamageMedium(false); + toggleDamageHigh(false); + } + + public Vector3fImmutable getStuckLocation() { + + BuildingModelBase bmb = BuildingModelBase.getModelBase(this.meshUUID); + Vector3fImmutable convertLoc = null; + + + if (bmb != null) { + BuildingLocation bl = bmb.getStuckLocation(); + + if (bl != null){ + + Vector3fImmutable buildingWorldLoc = ZoneManager.convertLocalToWorld(this, bl.getLoc()); + return buildingWorldLoc; + } + + + } + + return null; + } + + public boolean isDoorOpen(int doorNumber) { + + if (this.doorState == 0) + return false; + + return (this.doorState & (1 << doorNumber + 16)) != 0; + + } + + public boolean isDoorLocked(int doorNumber) { + + if (this.doorState == 0) + return false; + + return (this.doorState & (1 << doorNumber)) != 0; + + } + + public boolean setDoorState(int doorNumber, DoorState doorState) { + + boolean updateRecord; + + updateRecord = false; + + // Can't have an invalid door number + // Log error? + if (doorNumber < 1 || doorNumber > 16) + return false; + + switch (doorState) { + + case OPEN: + this.doorState |= (1 << (doorNumber + 16)); + break; + case CLOSED: + this.doorState &= ~(1 << (doorNumber + 16)); + break; + case UNLOCKED: + this.doorState &= ~(1 << doorNumber); + updateRecord = true; + break; + case LOCKED: + this.doorState |= (1 << doorNumber); + updateRecord = true; + break; + } + + // Save to database ? + if (updateRecord == true) + return DbManager.BuildingQueries.UPDATE_DOOR_LOCK(this.getObjectUUID(), this.doorState); + else + return true; + } + + public int getDoorstate(){ + return this.doorState; + } + + public void updateEffects() { + + ApplyBuildingEffectMsg applyBuildingEffectMsg = new ApplyBuildingEffectMsg(0x00720063, 1, this.getObjectType().ordinal(), this.getObjectUUID(), this.effectFlags); + DispatchMessage.sendToAllInRange(this, applyBuildingEffectMsg); + + } + + public final void enableSpire() { + + SpireType spireType; + + if (this.getCity() == null) + return; + + // Blueprint sanity check + + if (this.blueprintUUID == 0) + return; + + spireType = SpireType.getByBlueprintUUID(this.blueprintUUID); + + SiegeSpireWithdrawlJob spireJob = new SiegeSpireWithdrawlJob(this); + JobContainer jc = JobScheduler.getInstance().scheduleJob(spireJob, 300000); + this.getTimers().put("SpireWithdrawl", jc); + + this.getCity().addCityEffect(spireType.getEffectBase(), rank); + addEffectBit(spireType.getEffectFlag()); + this.spireIsActive = true; + this.updateEffects(); + + + } + + public final void disableSpire(boolean refreshEffect) { + + SpireType spireType; + + if (this.getCity() == null) + return; + + // Blueprint sanity check + + if (this.blueprintUUID == 0) + return; + + spireType = SpireType.getByBlueprintUUID(this.blueprintUUID); + + this.getCity().removeCityEffect(spireType.getEffectBase(), rank, refreshEffect); + + JobContainer toRemove = this.getTimers().get("SpireWithdrawl"); + + if (toRemove != null) { + toRemove.cancelJob(); + this.getTimers().remove("SpireWithdrawl"); + } + + this.spireIsActive = false; + this.removeEffectBit(spireType.getEffectFlag()); + this.updateEffects(); + } + + public ConcurrentHashMap getHirelings() { + return hirelings; + } + + public final boolean isSpireIsActive() { + return spireIsActive; + } + + public final boolean isVulnerable() { + + // NPC owned buildings are never vulnerable + + if (ownerIsNPC) + return false; + + // Buildings on an npc citygrid are never vulnerable + + if (this.getCity() != null) { + if (this.getCity().getParent().isNPCCity() == true) + return false; + } + + // Destroyed buildings are never vulnerable + + if (rank < 0) + return false; + + // Any structure without a blueprint was not placed by a + // player and we can assume to be invulnerable regardless + // of a protection contract or not. + + if (this.getBlueprint() == null) + return false; + + // Runegates are never vulerable. + + if (this.getBlueprint().getBuildingGroup() == BuildingGroup.RUNEGATE) + return false; + + // Shrines are never vulerable. They blow up as a + // tree deranks. + + if (this.getBlueprint().getBuildingGroup() == BuildingGroup.SHRINE) + return false; + + // Mines are vulnerable only if they are active + + if (this.getBlueprint().getBuildingGroup() == BuildingGroup.MINE) { + + // Cannot access mine + + if (Mine.getMineFromTower(this.getObjectUUID()) == null) + return false; + + return Mine.getMineFromTower(this.getObjectUUID()).getIsActive() == true; + } + + // Errant banestones are vulnerable by default + + if ((this.getBlueprint().getBuildingGroup() == BuildingGroup.BANESTONE) && + this.getCity().getBane().isErrant() == true) + return true; + + // There is an active protection contract. Is there also + // an active bane? If so, it's meaningless. + + if (this.assetIsProtected() == true) { + + // Building protection is meaningless without a city + + if (this.getCity() == null) + return true; + + // All buildings are vulnerable during an active bane + + return (this.getCity().protectionEnforced == false); + + } + + // No protection contract? Oh well, you're vunerable! + + return true; + } + + public final void setSpireIsActive(boolean spireIsActive) { + this.spireIsActive = spireIsActive; + } + + public final ConcurrentHashMap getTimers() { + if (this.timers == null) + this.timers = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + return this.timers; + } + + public final ConcurrentHashMap getTimestamps() { + if (this.timestamps == null) + this.timestamps = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + return this.timestamps; + } + + public final long getTimeStamp(final String name) { + if (this.getTimestamps().containsKey(name)) + return this.timestamps.get(name); + return 0L; + } + + public final void setTimeStamp(final String name, final long value) { + this.getTimestamps().put(name, value); + } + + public ConcurrentHashMap getFriends() { + return this.friends; + } + + public final void claim(AbstractCharacter sourcePlayer) { + + // Clear any existing friend or condemn entries + + this.friends.clear(); + DbManager.BuildingQueries.CLEAR_FRIENDS_LIST(this.getObjectUUID()); + + condemned.clear(); + DbManager.BuildingQueries.CLEAR_CONDEMNED_LIST(this.getObjectUUID()); + + // Transfer the building asset ownership + + this.setOwner(sourcePlayer); + + } + + /** + * @return the protectionState + */ + public ProtectionState getProtectionState() { + return protectionState; + } + + /** + * @param protectionState the protectionState to set + */ + public void setProtectionState(ProtectionState protectionState) { + + // Early exit if protection state is already set to input value + + if (this.protectionState.equals(protectionState)) + return; + + // if building is destroyed, just set the protection state. There isn't a DB + // record to write anything to. + + if (rank == -1) { + this.protectionState = protectionState; + return; + } + + if (DbManager.BuildingQueries.UPDATE_PROTECTIONSTATE(this.getObjectUUID(), protectionState) == true) { + this.protectionState = protectionState; + return; + } + + Logger.error("Protection update failed for UUID: " + this.getObjectUUID() + "\n" + + this.getBlueprint().getName() + " From " + this.protectionState.name() + " To: " + protectionState.name()); + + } + + public ConcurrentHashMap getCondemned() { + return condemned; + } + + public boolean setReverseKOS(boolean reverseKOS) { + if (!DbManager.BuildingQueries.updateReverseKOS(this, reverseKOS)) + return false; + this.reverseKOS = reverseKOS; + return true; + } + + public boolean assetIsProtected() { + + boolean outValue = false; + + if (protectionState.equals(ProtectionState.PROTECTED)) + outValue = true; + + if (protectionState.equals(ProtectionState.CONTRACT)) + outValue = true; + + return outValue; + } + + public synchronized boolean transferGold(int amount,boolean tax){ + + if (amount < 0) + if (!this.hasFunds(-amount)) + return false; + + if (_strongboxValue + amount < 0) + return false; + + if (_strongboxValue + amount > maxGold) + return false; + + //Deduct Profit taxes. + if (tax) + if (taxType == TaxType.PROFIT && protectionState == ProtectionState.CONTRACT && amount > 0) + amount = this.payProfitTaxes(amount); + + + if (amount != 0) + return this.setStrongboxValue(_strongboxValue + amount); + return true; + } + + public synchronized int payProfitTaxes(int amount){ + + if (this.getCity() == null) + return amount; + if (this.getCity().getWarehouse() == null) + return amount; + + if (this.getCity().getWarehouse().getResources().get(ItemBase.getGoldItemBase()) >= Warehouse.getMaxResources().get(ItemBase.getGoldItemBase().getUUID())) + return amount; + + int profitAmount = (int) (amount * (taxAmount *.01f)); + + if (this.getCity().getWarehouse().getResources().get(ItemBase.getGoldItemBase()) + profitAmount <= Warehouse.getMaxResources().get(ItemBase.getGoldItemBase().getUUID())){ + this.getCity().getWarehouse().depositProfitTax(ItemBase.getGoldItemBase(), profitAmount,this); + return amount - profitAmount; + } + //overDrafting + int warehouseDeposit = Warehouse.getMaxResources().get(ItemBase.getGoldItemBase().getUUID()) - this.getCity().getWarehouse().getResources().get(ItemBase.getGoldItemBase()); + this.getCity().getWarehouse().depositProfitTax(ItemBase.getGoldItemBase(), warehouseDeposit,this); + return amount - warehouseDeposit; + } + + public synchronized boolean setReserve(int amount, PlayerCharacter player){ + + if (!BuildingManager.playerCanManageNotFriends(player, this)) + return false; + + if (amount < 0) + return false; + + if (!DbManager.BuildingQueries.SET_RESERVE(this, amount)) + return false; + + this.reserve = amount; + + return true; + } + + public synchronized boolean hasFunds(int amount){ + return amount <= (this._strongboxValue - reserve); + } + + public ArrayList getPatrolPoints() { + return patrolPoints; + } + + public void setPatrolPoints(ArrayList patrolPoints) { + this.patrolPoints = patrolPoints; + } + + public ArrayList getSentryPoints() { + return sentryPoints; + } + + public void setSentryPoints(ArrayList sentryPoints) { + this.sentryPoints = sentryPoints; + } + + public synchronized boolean addProtectionTax(Building building, PlayerCharacter pc, final TaxType taxType, int amount, boolean enforceKOS){ + if (building == null) + return false; + + if (this.getBlueprint() == null) + return false; + + if (this.getBlueprint().getBuildingGroup() != BuildingGroup.TOL) + return false; + + if (building.assetIsProtected()) + return false; + + if (!DbManager.BuildingQueries.addTaxes(building, taxType, amount, enforceKOS)) + return false; + + building.taxType = taxType; + building.taxAmount = amount; + building.enforceKOS = enforceKOS; + + return true; + + } + + public synchronized boolean declineTaxOffer(){ + return true; + } + + public synchronized boolean acceptTaxOffer(){ + return true; + } + + public synchronized boolean acceptTaxes(){ + + if (!DbManager.BuildingQueries.acceptTaxes(this)) + return false; + + this.setProtectionState(Enum.ProtectionState.CONTRACT); + this.taxDateTime = LocalDateTime.now().plusDays(7); + + return true; + } + + public synchronized boolean removeTaxes(){ + + if (!DbManager.BuildingQueries.removeTaxes(this)) + return false; + + this.taxType = TaxType.NONE; + this.taxAmount = 0; + this.taxDateTime = null; + this.enforceKOS = false; + + return true; + } + + public boolean isTaxed(){ + if (this.taxType == TaxType.NONE) + return false; + if (this.taxAmount == 0) + return false; + return this.taxDateTime != null; + } +} diff --git a/src/engine/objects/BuildingFriends.java b/src/engine/objects/BuildingFriends.java new file mode 100644 index 00000000..f6518759 --- /dev/null +++ b/src/engine/objects/BuildingFriends.java @@ -0,0 +1,51 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class BuildingFriends { + + private int playerUID; + private int buildingUID; + private int guildUID; + private int friendType; + + /** + * ResultSet Constructor + */ + + public BuildingFriends(ResultSet rs) throws SQLException { + this.playerUID = rs.getInt("playerUID"); + this.buildingUID = rs.getInt("buildingUID"); + this.guildUID = rs.getInt("guildUID"); + this.friendType = rs.getInt("friendType"); + } + + public BuildingFriends(int playerUID, int buildingUID, int guildUID, int friendType) { + super(); + this.playerUID = playerUID; + this.buildingUID = buildingUID; + this.guildUID = guildUID; + this.friendType = friendType; + } + + public int getPlayerUID() { + return playerUID; + } + public int getGuildUID() { + return guildUID; + } + public int getFriendType() { + return friendType; + } + +} diff --git a/src/engine/objects/BuildingLocation.java b/src/engine/objects/BuildingLocation.java new file mode 100644 index 00000000..e703a65b --- /dev/null +++ b/src/engine/objects/BuildingLocation.java @@ -0,0 +1,143 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.gameManager.DbManager; +import engine.math.Vector3fImmutable; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; + + +public class BuildingLocation extends AbstractGameObject { + + private final int buildingUUID; + private final int type; + private final int slot; + private final int unknown; + private final Vector3fImmutable loc; + private final Vector3fImmutable rot; + private final float w; + + + /** + * ResultSet Constructor + */ + public BuildingLocation(ResultSet rs) throws SQLException { + super(rs); + this.buildingUUID = rs.getInt("BuildingID"); + this.type = rs.getInt("type"); + this.slot = rs.getInt("slot"); + this.unknown = rs.getInt("unknown"); + this.loc = new Vector3fImmutable(rs.getFloat("locX"), rs.getFloat("locY"), rs.getFloat("locZ")); + this.rot = new Vector3fImmutable(rs.getFloat("rotX"), rs.getFloat("rotY"), rs.getFloat("rotZ")); + this.w = rs.getFloat("w"); + } + + /* + * Getters + */ + + public int getBuildingUUID() { + return this.buildingUUID; + } + + public Vector3fImmutable rotatedLoc() { + Vector3fImmutable convertLoc = null; + + + double rotY = 2.0 * Math.asin(this.rot.y); + + + // handle building rotation + + convertLoc = new Vector3fImmutable( + (float) ((loc.z * Math.sin(rotY)) + (loc.x * Math.cos(rotY))), + loc.y, + (float) ((loc.z * Math.cos(rotY)) - (loc.x * Math.sin(rotY)))); + + return convertLoc; + + } + + public int getType() { + return this.type; + } + + public int getSlot() { + return this.slot; + } + + public int getUnknown() { + return this.unknown; + } + + public float getLocX() { + return this.loc.x; + } + + public float getLocY() { + return this.loc.y; + } + + public float getLocZ() { + return this.loc.z; + } + + public float getRotX() { + return this.rot.x; + } + + public float getRotY() { + return this.rot.y; + } + + public float getRotZ() { + return this.rot.z; + } + + public float getW() { + return this.w; + } + + public Vector3fImmutable getLoc() { + return this.loc; + } + + public Vector3fImmutable getRot() { + return this.rot; + } + + + @Override + public void updateDatabase() { + } + + + public static void loadAllLocations() { + ArrayList bls = DbManager.BuildingLocationQueries.LOAD_ALL_BUILDING_LOCATIONS(); + ConcurrentHashMap mbs = BuildingModelBase.getModelBases(); + for (BuildingLocation bl : bls) { + int modelID = bl.buildingUUID; + BuildingModelBase mb = null; + if (!mbs.containsKey(modelID)) { + mb = new BuildingModelBase(modelID); + mbs.put(modelID, mb); + } else + mb = mbs.get(modelID); + mb.addLocation(bl); + + if (bl.type == 6) + mb.addSlotLocation(bl); + } + } +} diff --git a/src/engine/objects/BuildingModelBase.java b/src/engine/objects/BuildingModelBase.java new file mode 100644 index 00000000..ac3e2ced --- /dev/null +++ b/src/engine/objects/BuildingModelBase.java @@ -0,0 +1,87 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.server.MBServerStatics; + +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; + +public class BuildingModelBase extends AbstractGameObject { + + private ArrayList locations = new ArrayList<>(); + private static ConcurrentHashMap modelBases = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private final int buildingBaseID; + + private ArrayList slotLocations = new ArrayList<>(); + + public BuildingModelBase(int buildingBaseID) { + super(); + this.buildingBaseID = buildingBaseID; + } + + public void addLocation(BuildingLocation bl) { + this.locations.add(bl); + } + + public void addSlotLocation(BuildingLocation bl) { + this.slotLocations.add(bl); + } + + public ArrayList getLocations() { + return this.locations; + } + + public BuildingLocation getNPCLocation(int slot) { + for (BuildingLocation bl : this.locations) { + if (bl.getType() == 6 && bl.getSlot() == slot) + return bl; + } + return null; //not found + } + + public BuildingLocation getStuckLocation() { + + for (BuildingLocation bl : this.locations) { + if (bl.getType() == 8) + return bl; + } + return null; //not found + } + + public BuildingLocation getSlotLocation(int slot) { + + try{ + return this.slotLocations.get(slot - 1); + }catch(Exception e){ + return null; + } + } + + + @Override + public void updateDatabase() { + } + + public int getBuildingBaseID() { + return this.buildingBaseID; + } + + public static ConcurrentHashMap getModelBases() { + return BuildingModelBase.modelBases; + } + + public static BuildingModelBase getModelBase(int ID) { + if (!BuildingModelBase.modelBases.containsKey(ID)) + BuildingModelBase.modelBases.put(ID, new BuildingModelBase(ID)); + return BuildingModelBase.modelBases.get(ID); + } + +} diff --git a/src/engine/objects/BuildingRegions.java b/src/engine/objects/BuildingRegions.java new file mode 100644 index 00000000..0cd8fddd --- /dev/null +++ b/src/engine/objects/BuildingRegions.java @@ -0,0 +1,388 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.gameManager.DbManager; +import engine.math.Vector3f; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; + +public class BuildingRegions { + + + private int buildingID; + private int level; + private int numVertex ; + private float vertex1X ; + private float vertex1Y ; + private float vertex1Z ; + private float vertex2X ; + private float vertex2Y ; + private float vertex2Z ; + private float vertex3X ; + private float vertex3Y ; + private float vertex3Z ; + private float vertex4X ; + private float vertex4Y ; + private float vertex4Z ; + private byte ground1; + private byte ground2; + private byte ground3; + private byte ground4; + + private short contentBehavior; + private boolean outside; + private float centerX; + private float centerZ; + private int room = 0; + public static HashMap> _staticRegions = new HashMap<>(); + private final boolean exitRegion; + private final boolean stairs; + + private ArrayList regionPoints = new ArrayList<>(); + public Vector3f center; + + /** + * ResultSet Constructor + */ + + public BuildingRegions(ResultSet rs) throws SQLException { + + buildingID = rs.getInt("buildingID"); + level = rs.getInt("level"); + room = rs.getInt("room"); + numVertex = rs.getInt("numVertex"); + vertex1X = rs.getFloat("vertex1X"); + vertex1Y = rs.getFloat("vertex1Y"); + vertex1Z = rs.getFloat("vertex1Z"); + vertex2X = rs.getFloat("vertex2X"); + vertex2Y = rs.getFloat("vertex2Y"); + vertex2Z = rs.getFloat("vertex2Z"); + vertex3X = rs.getFloat("vertex3X"); + vertex3Y = rs.getFloat("vertex3Y"); + vertex3Z = rs.getFloat("vertex3Z"); + vertex4X = rs.getFloat("vertex4X"); + vertex4Y = rs.getFloat("vertex4Y"); + vertex4Z = rs.getFloat("vertex4Z"); + + regionPoints.add(new Vector3f(vertex1X,vertex1Y,vertex1Z)); + regionPoints.add(new Vector3f(vertex2X,vertex2Y,vertex2Z)); + regionPoints.add(new Vector3f(vertex3X,vertex3Y,vertex3Z)); + + + if(numVertex ==4) + regionPoints.add(new Vector3f(vertex4X,vertex4Y,vertex4Z)); + + + this.contentBehavior = (rs.getShort("unknown_Order1")); + short state = rs.getShort("unknown_Order2"); + + if (state == 2) + this.outside = (true); + else + this.outside = (false); + + this.exitRegion = rs.getBoolean("colOrder1"); + this.stairs = rs.getBoolean("colOrder2"); + + + + ground1 = rs.getByte("colOrder1"); + ground2 = rs.getByte("colOrder2"); + ground3 = rs.getByte("colOrder3"); + ground4 = rs.getByte("colOrder4"); + + float centerY = rs.getFloat("unknown_VectorY"); + centerX = rs.getFloat("unknown_VectorX"); + centerZ = rs.getFloat("unknown_VectorZ"); + + this.center = new Vector3f(centerX,centerY,centerZ); + + + } + + + + public int getBuildingID() { + return buildingID; + } + + + + public void setBuildingID(int buildingID) { + this.buildingID = buildingID; + } + + + + public int getLevel() { + return level; + } + + + + public void setLevel(int level) { + this.level = level; + } + + + + public int getNumVertex() { + return numVertex; + } + + + + public void setNumVertex(int numVertex) { + this.numVertex = numVertex; + } + + + + public float getVertex1X() { + return vertex1X; + } + + + + public void setVertex1X(float vertex1x) { + vertex1X = vertex1x; + } + + + + public float getVertex1Y() { + return vertex1Y; + } + + + + public void setVertex1Y(float vertex1y) { + vertex1Y = vertex1y; + } + + + + public float getVertex1Z() { + return vertex1Z; + } + + + + public void setVertex1Z(float vertex1z) { + vertex1Z = vertex1z; + } + + + + public float getVertex2X() { + return vertex2X; + } + + + + public void setVertex2X(float vertex2x) { + vertex2X = vertex2x; + } + + + + public float getVertex2Y() { + return vertex2Y; + } + + + + public void setVertex2Y(float vertex2y) { + vertex2Y = vertex2y; + } + + + + public float getVertex2Z() { + return vertex2Z; + } + + + + public void setVertex2Z(float vertex2z) { + vertex2Z = vertex2z; + } + + + + public float getVertex3X() { + return vertex3X; + } + + + + public void setVertex3X(float vertex3x) { + vertex3X = vertex3x; + } + + + + public float getVertex3Y() { + return vertex3Y; + } + + + + public void setVertex3Y(float vertex3y) { + vertex3Y = vertex3y; + } + + + + public float getVertex3Z() { + return vertex3Z; + } + + + + public void setVertex3Z(float vertex3z) { + vertex3Z = vertex3z; + } + + + + public float getVertex4X() { + return vertex4X; + } + + + + public void setVertex4X(float vertex4x) { + vertex4X = vertex4x; + } + + + + public float getVertex4Y() { + return vertex4Y; + } + + + + public void setVertex4Y(float vertex4y) { + vertex4Y = vertex4y; + } + + + + public float getVertex4Z() { + return vertex4Z; + } + + + + public void setVertex4Z(float vertex4z) { + vertex4Z = vertex4z; + } + + + + public static HashMap> get_staticRegions() { + return _staticRegions; + } + + + + public static void set_staticRegions(HashMap> _staticRegions) { + BuildingRegions._staticRegions = _staticRegions; + } + + + + public static void loadAllStaticColliders(){ + _staticRegions = DbManager.BuildingQueries.LOAD_BUILDING_REGIONS(); + } + + public static ArrayList GetStaticCollidersForMeshID(int meshID) { + return _staticRegions.get(meshID); + } + + public boolean isGroundLevel(){ + if (this.level > 0) + return false; + + if (this.ground1 == 0) + return true; + if (this.ground2 == 0) + return true; + if (this.ground3 == 0) + return true; + return this.ground4 == 0; + + } + + + + public float getCenterX() { + return centerX; + } + + + + public void setCenterX(float centerX) { + this.centerX = centerX; + } + + + + public float getCenterY() { + return centerZ; + } + + + + public void setCenterY(float centerY) { + this.centerZ = centerY; + } + + + + public boolean isOutside() { + return outside; + } + + public short getContentBehavior() { + return contentBehavior; + } + + + + public int getRoom() { + return room; + } + + + + public ArrayList getRegionPoints() { + return regionPoints; + } + + + + public boolean isExitRegion() { + return exitRegion; + } + + + + public boolean isStairs() { + return stairs; + } + +} diff --git a/src/engine/objects/CharacterItemManager.java b/src/engine/objects/CharacterItemManager.java new file mode 100644 index 00000000..bc51e3f8 --- /dev/null +++ b/src/engine/objects/CharacterItemManager.java @@ -0,0 +1,2630 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.Enum.ItemType; +import engine.gameManager.BuildingManager; +import engine.gameManager.ChatManager; +import engine.gameManager.ConfigManager; +import engine.gameManager.DbManager; +import engine.math.Vector3fImmutable; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.ClientMessagePump; +import engine.net.client.msg.*; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; + +import static engine.math.FastMath.sqr; +import static engine.net.client.msg.ErrorPopupMsg.sendErrorPopup; + + + +public class CharacterItemManager { + + private final AbstractCharacter absCharacter; + private Account account; + + // Mapping of all the items associated with this Manager + private final ConcurrentHashMap itemIDtoType = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + + // Mapping of all items equipped in this Manager + // Key = Item Slot + private final ConcurrentHashMap equipped = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + + private final HashSet inventory = new HashSet<>(); + private final HashSet bank = new HashSet<>(); + private final HashSet vault = new HashSet<>(); + + private Item goldInventory; + private Item goldBank; + public Item goldVault; + + private boolean bankOpened; + private boolean vaultOpened; + + private short bankWeight; + private short inventoryWeight; + private short equipWeight; + private short vaultWeight; + + private ClientConnection tradingWith; + private byte tradeCommitted; + private boolean tradeSuccess; + private HashSet trading; + private int goldTradingAmount; + private int tradeID = 0; + private final HashSet equipOrder = new HashSet<>(); + + /* + * Item Manager Version data + */ + private byte equipVer = (byte) 0; + private static final byte inventoryVer = (byte) 0; + private static final byte bankVer = (byte) 0; + private static final byte vaultVer = (byte) 0; + + public CharacterItemManager(AbstractCharacter ac) { + super(); + this.absCharacter = ac; + } + + public void load() { + loadForGeneric(); + + if (this.absCharacter .getObjectType().equals(GameObjectType.PlayerCharacter)) + loadForPlayerCharacter(); + else if (this.absCharacter.getObjectType().equals(GameObjectType.NPC)) + loadForNPC(); + + } + + public void loadGoldItems() { + + if (ConfigManager.serverType.equals(Enum.ServerType.LOGINSERVER)) { + //other server, just make generic + this.goldInventory = new MobLoot(this.absCharacter, 0); + this.goldBank = new MobLoot(this.absCharacter, 0); + this.goldVault = new MobLoot(this.absCharacter, 0); + return; + } + + //create inventory gold if needed + if (this.goldInventory == null) + if (this.absCharacter != null && (this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter) || this.absCharacter.getObjectType().equals(GameObjectType.NPC))) + this.goldInventory = Item.newGoldItem(this.absCharacter, ItemBase.getItemBase(7), Enum.ItemContainerType.INVENTORY); + else + this.goldInventory = new MobLoot(this.absCharacter, 0); + + //create bank gold if needed + if (this.goldBank == null) + if (this.absCharacter != null && this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter)) + this.goldBank = Item.newGoldItem(this.absCharacter, ItemBase.getItemBase(7), Enum.ItemContainerType.BANK); + else + this.goldBank = new MobLoot(this.absCharacter, 0); + + //create vault gold if needed + if (this.goldVault == null) + if (this.absCharacter != null && this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter)){ + this.goldVault = this.account.vaultGold; + } + + else + this.goldVault = new MobLoot(this.absCharacter, 0); + + this.itemIDtoType.put(this.goldInventory.getObjectUUID(), this.goldInventory.getObjectType().ordinal()); + this.itemIDtoType.put(this.goldBank.getObjectUUID(), this.goldBank.getObjectType().ordinal()); + this.itemIDtoType.put(this.goldVault.getObjectUUID(), this.goldVault.getObjectType().ordinal()); + + } + + private void loadForPlayerCharacter() { + ArrayList al = null; + + // TODO Verify this is an actual account. + this.account = ((PlayerCharacter) this.absCharacter).getAccount(); + + // Get Items for player and vault + if (ConfigManager.serverType.equals(Enum.ServerType.LOGINSERVER)) //login, only need equipped items + al = DbManager.ItemQueries.GET_EQUIPPED_ITEMS(this.absCharacter.getObjectUUID()); + else + al = DbManager.ItemQueries.GET_ITEMS_FOR_PC(this.absCharacter.getObjectUUID()); + + for (Item i : al) { + + i.validateItemContainer(); + this.itemIDtoType.put(i.getObjectUUID(), i.getObjectType().ordinal()); + + switch (i.containerType) { + case EQUIPPED: + if (this.equipped.containsValue(i) == false) { + this.equipped.put((int) i.getEquipSlot(), i); + addEquipOrder((int) i.getEquipSlot()); + } + break; + case BANK: + if (i.getItemBase().getType().equals(ItemType.GOLD)) + this.goldBank = i; + else if (this.bank.contains(i) == false) + this.bank.add(i); + break; + case INVENTORY: + if (i.getItemBase().getType().equals(ItemType.GOLD)) + this.goldInventory = i; + else if (this.inventory.contains(i) == false) + this.inventory.add(i); + break; + case VAULT: + if (i.getItemBase().getType().equals(ItemType.GOLD)) + this.goldVault = i; + else if (this.vault.contains(i) == false) + this.vault.add(i); + break; + default: + i.junk(); + break; + } + + } + + this.goldVault = this.account.vaultGold; + + //check all gold is created + //loadGoldItems(); + calculateWeights(); + } + + private void loadForNPC() { + ArrayList al = null; + + // Get all items related to this NPC: + al = DbManager.ItemQueries.GET_ITEMS_FOR_NPC(this.absCharacter.getObjectUUID()); + + for (Item i : al) { + i.validateItemContainer(); + this.itemIDtoType.put(i.getObjectUUID(), i.getObjectType().ordinal()); + + switch (i.containerType) { + case EQUIPPED: + if (this.equipped.containsValue(i) == false) + this.equipped.put((int) i.getEquipSlot(), i); + break; + case BANK: + if (i.getItemBase().getType().equals(ItemType.GOLD)) + this.goldBank = i; + else if (this.bank.contains(i) == false) + this.bank.add(i); + break; + case INVENTORY: + if (i.getItemBase().getType().equals(ItemType.GOLD)) + this.goldInventory = i; + else if (this.inventory.contains(i) == false) + this.inventory.add(i); + break; + default: + i.junk(); + break; + } + } + + //check all gold is created + //loadGoldItems(); + } + + private void loadForGeneric() { + this.bankWeight = 0; + this.inventoryWeight = 0; + this.equipWeight = 0; + this.vaultWeight = 0; + + //check all gold is created + //loadGoldItems(); + // Always initialize with bank and vault closed + bankOpened = false; + vaultOpened = false; + } + + //Positve Amount = TO BUILDING; Negative Amount = FROM BUILDING. flip signs for Player inventory. + public synchronized boolean transferGoldToFromBuilding(int amount, AbstractWorldObject object){ + if (this.absCharacter.getObjectType() != GameObjectType.PlayerCharacter) + return false; + + PlayerCharacter player = (PlayerCharacter) this.absCharacter; + + switch (object.getObjectType()){ + case Building: + Building building = (Building)object; + + if (!this.getGoldInventory().validForInventory(player.getClientConnection(), player, this)) + return false; + + if (amount <0 && amount > building.getStrongboxValue()) + return false; + + // Not enough gold in inventory to transfer to tree + + if ((amount > 0) && + (this.getGoldInventory().getNumOfItems() - amount < 0)) { + sendErrorPopup(player, 28); + return false; + } + + if (this.getGoldInventory().getNumOfItems() - amount > MBServerStatics.PLAYER_GOLD_LIMIT){ + ErrorPopupMsg.sendErrorPopup(player, 202); + return false; + } + + + + // Not enough gold to transfer to inventory from tree + + if ((amount < 0) && + (building.getStrongboxValue() + amount < 0)) { + sendErrorPopup(player, 127); + return false; + } + + if (amount < 0) + if (!building.hasFunds(-amount)) + return false; + + //Verify player can access building to transfer goldItem + + if (!BuildingManager.playerCanManage(player, building)) + return false; + + if (building.getStrongboxValue() + amount > building.getMaxGold()){ + ErrorPopupMsg.sendErrorPopup(player, 201); + return false; + } + + if (this.getOwner().getCharItemManager().getGoldTrading() > 0){ + if (this.getOwner().getObjectType().equals(GameObjectType.PlayerCharacter)) + ErrorPopupMsg.sendErrorPopup((PlayerCharacter)this.getOwner(), 195); + return false; + } + + if (!this.modifyInventoryGold(-amount)){ + + Logger.error(player.getName() + " transfer amount = " + amount +" ; Gold Inventory = " + this.getGoldInventory().getNumOfItems()); + + // ChatManager.chatSystemError(player, "You do not have this Gold."); + return false; + } + + if (!building.transferGold(amount,false)){ + + Logger.error(player.getName() + " transfer amount = " + amount +" ; Gold Inventory = " + this.getGoldInventory().getNumOfItems() + "; Building Strongbox = " + building.getStrongboxValue()); + + //ChatManager.chatSystemError(player, "Something went terribly wrong. Contact CCR."); + return false; + } + + break; + case Warehouse: + + Warehouse warehouse = (Warehouse)object; + + if (amount < 0){ + if (!warehouse.deposit((PlayerCharacter) this.absCharacter, this.getGoldInventory(), amount*-1, true,true)){ + + ErrorPopupMsg.sendErrorPopup((PlayerCharacter) this.absCharacter, 203); + return false; + } + }else{ + if (!warehouse.withdraw((PlayerCharacter) this.absCharacter, this.getGoldInventory().getItemBase(), amount*-1, true,true)){ + + ErrorPopupMsg.sendErrorPopup((PlayerCharacter) this.absCharacter, 203); + return false; + } + + } + + break; + + } + return true; + } + + /* + * Item Controls + */ + public synchronized boolean modifyInventoryGold(int modifyValue) { + + Item goldItem; + PlayerCharacter player; + boolean success = false; + + goldItem = getGoldInventory(); + + if (goldItem == null) { + Logger.error("ModifyInventoryGold", "Could not create gold item"); + return success; + } + + if (this.getGoldInventory().getNumOfItems() + modifyValue > MBServerStatics.PLAYER_GOLD_LIMIT){ + return false; + } + + if (this.getGoldInventory().getNumOfItems() + modifyValue < 0) + return false; + + // No database update for npc's gold values so we use the player object + // for flow control later on. + + if (this.absCharacter.getObjectType() == GameObjectType.PlayerCharacter) + player = (PlayerCharacter) this.absCharacter; + else + player = null; + + // If this is an update for a player character update the database + + if (player != null) + try { + if (!DbManager.ItemQueries.UPDATE_GOLD(this.getGoldInventory(), this.goldInventory.getNumOfItems() + modifyValue)){ + return false; + } + + + success = true; + } catch (Exception e) { + Logger.error("ModifyInventoryGold", "Error writing to database"); + } + + // Update in-game gold values for character + goldItem.setNumOfItems(goldItem.getNumOfItems() + modifyValue); + UpdateGoldMsg ugm = new UpdateGoldMsg(this.absCharacter); + ugm.configure(); + + Dispatch dispatch = Dispatch.borrow(player, ugm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + return success; + } + + public synchronized boolean tradeRequest(TradeRequestMsg msg) { + + PlayerCharacter source = (PlayerCharacter) this.getOwner(); + PlayerCharacter target = PlayerCharacter.getFromCache(msg.getPlayerID()); + Dispatch dispatch; + + if (!canTrade(source, target)) { + ChatManager.chatSystemError(source, "Can't currently trade with target player"); + return false; + } + + // TODO uncomment this block after we determine when we + // setBankOpen(false) and setVaultOpen(false) + CharacterItemManager cim1 = source.getCharItemManager(); + CharacterItemManager cim2 = target.getCharItemManager(); + + if (cim1 == null) + return false; + + if (cim2 == null) + return false; + + dispatch = Dispatch.borrow(target, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return true; + + } + + public synchronized boolean invalidTradeRequest(InvalidTradeRequestMsg msg) { + PlayerCharacter requester = PlayerCharacter.getFromCache(msg.getRequesterID()); + Dispatch dispatch; + + dispatch = Dispatch.borrow(requester, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return true; + + } + + public synchronized boolean canTrade(PlayerCharacter playerA, PlayerCharacter playerB) { + + if (playerA == null || playerB == null) + return false; + + //make sure both are alive + if (!playerA.isAlive() || !playerB.isAlive()) + return false; + + //distance check + Vector3fImmutable aLoc = playerA.getLoc(); + Vector3fImmutable bLoc = playerB.getLoc(); + + if (aLoc.distanceSquared2D(bLoc) > sqr(MBServerStatics.TRADE_RANGE)) + return false; + + //visibility check + if (!playerA.canSee(playerB) || !playerB.canSee(playerA)) + return false; + + if (playerA.lastBuildingAccessed != 0) { + ManageCityAssetsMsg mca = new ManageCityAssetsMsg(); + mca.actionType = 4; + mca.setTargetType(Enum.GameObjectType.Building.ordinal()); + mca.setTargetID(playerA.lastBuildingAccessed); + Dispatch dispatch = Dispatch.borrow(playerA, mca); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + playerA.lastBuildingAccessed = 0; + } + + return true; + } + + public synchronized boolean acceptTradeRequest(AcceptTradeRequestMsg msg) { + + PlayerCharacter source = (PlayerCharacter)this.getOwner(); + PlayerCharacter target = PlayerCharacter.getFromCache(msg.getTargetID()); + + Dispatch dispatch; + + if (source == null || !source.isAlive()) + return false; + + if (target == null || !target.isAlive()) + return false; + + if (this.tradingWith != null) + return false; + + if (!canTrade(source, target)) + return false; + + // verify characterTarget is in range + if (source.getLoc().distanceSquared2D(target.getLoc()) > sqr(MBServerStatics.TRADE_RANGE)) + return false; + + // TODO uncomment this block after we determine when we + // setBankOpen(false) and setVaultOpen(false) + /* + * CharacterItemManager cim1 = source.getCharItemManager(); + * CharacterItemManager cim2 = characterTarget.getCharItemManager(); if (cim1 == + * null) return false; if (cim2 == null) return false; if (cim1.isBankOpen()) + * return false; if (cim2.isVaultOpen()) return false; + */ + ClientConnection sourceConn = source.getClientConnection(); + ClientConnection targetConn = target.getClientConnection(); + + if (sourceConn == null) + return false; + + if (targetConn == null) + return false; + + + CharacterItemManager toTradeWith = target.getCharItemManager(); + + if (toTradeWith == null) + return false; + + Account sourceAccount = source.getAccount(); + Account targetAccount = target.getAccount(); + + UpdateVaultMsg uvmSource = new UpdateVaultMsg(sourceAccount); + UpdateVaultMsg uvmTarget = new UpdateVaultMsg(targetAccount); + + dispatch = Dispatch.borrow(source, uvmSource); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); + + dispatch = Dispatch.borrow(target, uvmTarget); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); + + this.setVaultOpen(false); + toTradeWith.setVaultOpen(false); + this.setBankOpen(false); + toTradeWith.setBankOpen(false); + + OpenTradeWindowMsg otwm = new OpenTradeWindowMsg(msg.getUnknown01(), source, target); + + // Only start trade if both players aren't already trading with + // someone + + if (this.getTradingWith() != null || toTradeWith.getTradingWith() != null) + return false; + + this.initializeTrade(); + toTradeWith.initializeTrade(); + this.setTradingWith(targetConn); + toTradeWith.setTradingWith(sourceConn); + this.tradeID = msg.getUnknown01(); + toTradeWith.tradeID = msg.getUnknown01(); + + dispatch = Dispatch.borrow(source, otwm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); + + dispatch = Dispatch.borrow(target, otwm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); + + return true; + } + + public synchronized boolean addItemToTradeWindow(AddItemToTradeWindowMsg msg) { + PlayerCharacter source =(PlayerCharacter)this.getOwner(); + Dispatch dispatch; + + if (source == null || !source.isAlive()) + return false; + + + + + ClientConnection ccOther = this.getTradingWith(); + + if (ccOther == null) + return false; + + PlayerCharacter other = ccOther.getPlayerCharacter(); + + if (other == null || !other.isAlive()) + return false; + + CharacterItemManager tradingWith = other.getCharItemManager(); + + if (tradingWith == null) + return false; + + if (!canTrade(source, other)) + return false; + + Item i = Item.getFromCache(msg.getItemID()); + + if (i == null) + return false; + + if (!this.doesCharOwnThisItem(i.getObjectUUID())) + return false; + + //can't add item to trade window twice + if (this.tradingContains(i)) + return false; + + //dupe check + if (!i.validForInventory(source.getClientConnection(), source, this)) + return false; + + if (!tradingWith.hasRoomTrade(i.getItemBase().getWeight())) { + dispatch = Dispatch.borrow(source, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); + return false; + } + + UpdateTradeWindowMsg utwm = new UpdateTradeWindowMsg(source, other); + + this.setTradeCommitted((byte) 0); + tradingWith.setTradeCommitted((byte) 0); + + this.addItemToTrade(i); + + dispatch = Dispatch.borrow(other, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); + + modifyCommitToTrade(); + + dispatch = Dispatch.borrow(other, utwm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); + return true; + } + + public synchronized boolean addGoldToTradeWindow(AddGoldToTradeWindowMsg msg) { + + PlayerCharacter source = (PlayerCharacter) this.getOwner(); + Dispatch dispatch; + + if (source == null || !source.isAlive()) + return false; + + + + + ClientConnection ccOther = this.getTradingWith(); + + if (ccOther == null) + return false; + + PlayerCharacter other = ccOther.getPlayerCharacter(); + + if (other == null || !other.isAlive()) + return false; + + CharacterItemManager tradingWith = other.getCharItemManager(); + + if (tradingWith == null) + return false; + + UpdateTradeWindowMsg utwm = new UpdateTradeWindowMsg(other, source); + UpdateTradeWindowMsg utwmOther = new UpdateTradeWindowMsg(source, other); + + if (!canTrade(source, other)) + return false; + + this.setTradeCommitted((byte) 0); + tradingWith.setTradeCommitted((byte) 0); + + int amt = msg.getAmount(); + + if (amt <= 0){ + Logger.info( source.getFirstName() + " added negative gold to trade window. Dupe attempt FAILED!"); + return false; + } + + if (amt > MBServerStatics.PLAYER_GOLD_LIMIT) + return false; + + if (this.getGoldInventory().getNumOfItems() - amt < 0) + return false; + + this.addGoldToTrade(amt); + + // BONUS CODE BELOW: Thanks some unknown retard! + // sourceItemMan.updateInventory(sourceItemMan.getInventory(), true); + + UpdateGoldMsg ugm = new UpdateGoldMsg(source); + ugm.configure(); + + modifyCommitToTrade(); + + dispatch = Dispatch.borrow(source, utwm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + dispatch = Dispatch.borrow(source, ugm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + dispatch = Dispatch.borrow(other, utwmOther); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return true; + + } + + + public synchronized boolean uncommitToTrade(UncommitToTradeMsg msg) { + + PlayerCharacter source = (PlayerCharacter) this.getOwner(); + + if (source == null || !source.isAlive()) + return false; + + CharacterItemManager sourceItemMan = source.getCharItemManager(); + + if (sourceItemMan == null) + return false; + + sourceItemMan.setTradeCommitted((byte) 0); + + ClientConnection ccOther = sourceItemMan.getTradingWith(); + + if (ccOther == null) + return false; + + PlayerCharacter other = ccOther.getPlayerCharacter(); + + if (other == null) + return false; + + if (!canTrade(source, other)) + return false; + + return modifyCommitToTrade(); + } + + public synchronized boolean commitToTrade(CommitToTradeMsg msg) { + + PlayerCharacter source = (PlayerCharacter)this.getOwner(); + + if (source == null || !source.isAlive()) + return false; + + + + + this.setTradeCommitted((byte) 1); + + ClientConnection ccOther = this.getTradingWith(); + + if (ccOther == null) + return false; + + PlayerCharacter other = ccOther.getPlayerCharacter(); + + if (other == null || !other.isAlive()) + return false; + + CharacterItemManager tradingWith = other.getCharItemManager(); + + if (tradingWith == null) + return false; + + if (!canTrade(source, other)) + return false; + + modifyCommitToTrade(); + + if (this.getTradeCommitted() == (byte) 1 && tradingWith.getTradeCommitted() == (byte) 1) { + int tradeID = this.tradeID; + CloseTradeWindowMsg ctwm1 = new CloseTradeWindowMsg(source, tradeID); + CloseTradeWindowMsg ctwm2 = new CloseTradeWindowMsg(other, tradeID); + this.commitTrade(); + this.closeTradeWindow(ctwm1, false); + other.getCharItemManager().closeTradeWindow(ctwm2, false); + } + return true; + } + + private synchronized boolean modifyCommitToTrade() { + CharacterItemManager man1 = this; + + if (this.getTradingWith() == null) + return false; + + if (this.getTradingWith().getPlayerCharacter() == null) + return false; + CharacterItemManager man2 = this.getTradingWith().getPlayerCharacter().getCharItemManager(); + Dispatch dispatch; + + if (man1 == null || man2 == null) + return false; + + ModifyCommitToTradeMsg modify = new ModifyCommitToTradeMsg(this.getOwner(), man2.getOwner(), man1.getTradeCommitted(), + man2.getTradeCommitted()); + + dispatch = Dispatch.borrow((PlayerCharacter) this.getOwner(), modify); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + dispatch = Dispatch.borrow((PlayerCharacter) man2.getOwner(), modify); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + return true; + + } + + public synchronized boolean closeTradeWindow(CloseTradeWindowMsg msg, boolean sourceTrade) { + + + Dispatch dispatch; + + PlayerCharacter source = (PlayerCharacter) this.getOwner(); + if (source == null) + return false; + + CharacterItemManager sourceItemMan = source.getCharItemManager(); + + if (sourceItemMan == null) + return false; + + int tradeID = this.tradeID; + CloseTradeWindowMsg closeMsg = new CloseTradeWindowMsg(source,tradeID); + + dispatch = Dispatch.borrow(source, closeMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + if (!sourceTrade){ + sourceItemMan.endTrade(); + return true; + } + + ClientConnection cc2 = sourceItemMan.getTradingWith(); + + if (cc2 == null || cc2.getPlayerCharacter() == null){ + sourceItemMan.endTrade(); + return true; + } + + sourceItemMan.endTrade(); + + + + cc2.getPlayerCharacter().getCharItemManager().closeTradeWindow(msg, false); + + + + + return true; + } + + public Item getGoldInventory() { + if (this.goldInventory == null) + loadGoldItems(); + return this.goldInventory; + } + + public Item getGoldBank() { + if (this.goldBank == null) + loadGoldItems(); + return this.goldBank; + } + + public Item getGoldVault() { + if (this.goldVault == null) + loadGoldItems(); + return this.goldVault; + } + + public void addEquipOrder(int slot) { + synchronized (this.equipOrder) { + Integer iSlot = slot; + if (this.equipOrder.contains(iSlot)) + this.equipOrder.remove(iSlot); + this.equipOrder.add(slot); + } + } + + public synchronized boolean doesCharOwnThisItem(int itemID) { + return this.itemIDtoType.containsKey(itemID); + } + + public synchronized boolean junk(Item i) { + return junk(i, true); + } + + public synchronized boolean recycle(Item i) { + if (i.getObjectType() == GameObjectType.Item) + return junk(i, false); + else{ + if(this.removeItemFromInventory(i) == false) + return false; + ((MobLoot)i).recycle((NPC)this.absCharacter); + calculateInventoryWeight(); + return true; + } + } + + // The DeleteItemMsg takes care of updating inventory, so we don't want to do it separately + public synchronized boolean delete(Item i) { + return junk(i, false); + } + + //cleanup an item from CharacterItemManager if it doesn't belong here + public synchronized boolean cleanupDupe(Item i) { + if (i == null) + return false; + + if(i.getItemBase().getType().equals(ItemType.GOLD)){ + if (this.getGoldInventory() != null){ + if (i.getObjectUUID() == this.getGoldInventory().getObjectUUID()) + this.goldInventory = null; + }else if (this.getGoldBank() != null){ + if (i.getObjectUUID() == this.getGoldBank().getObjectUUID()) + this.goldBank = null; + } + return true; + } + + byte slot = i.getEquipSlot(); + + if (this.doesCharOwnThisItem(i.getObjectUUID()) == false) + return false; + + // remove it from other lists: + this.remItemFromLists(i, slot); + this.itemIDtoType.remove(i.getObjectUUID()); + + calculateWeights(); + return true; + } + + public synchronized boolean consume(Item i) { + i.decrementChargesRemaining(); + if (i.getChargesRemaining() > 0) + return true; + return junk(i, true); + } + + private synchronized boolean junk(Item i, boolean updateInventory) { + if (i.getItemBase().getType().equals(ItemType.GOLD)) { + if (this.getGoldInventory().getObjectUUID() == i.getObjectUUID()) + if (DbManager.ItemQueries.UPDATE_GOLD(i, 0)) { + this.getGoldInventory().setNumOfItems(0); + if (updateInventory) + updateInventory(); + return true; + }else{ + return false; + } + if (!(this.absCharacter.getObjectType().equals(GameObjectType.Mob))) + return false; + } + + byte slot = i.getEquipSlot(); + + if (this.doesCharOwnThisItem(i.getObjectUUID()) == false && this.absCharacter.getObjectType() != GameObjectType.Mob && (i.containerType != Enum.ItemContainerType.FORGE)) + return false; + + // remove it from other lists: + this.remItemFromLists(i, slot); + this.itemIDtoType.remove(i.getObjectUUID()); + + i.junk(); + + //Why are we adding junked items?! + + // if (i.getObjectType() != GameObjectType.MobLoot) + // CharacterItemManager.junkedItems.add(i); + + + calculateWeights(); + + if (updateInventory) + // Send the new inventory + //updateInventory(i, false); this line was causing entire inventory to disappear + updateInventory(this.getInventory(), true); + + return true; + } + + public synchronized boolean moveItemToInventory(Item i) { + + boolean fromEquip = false; + synchronized (this) { + byte slot = i.getEquipSlot(); + + //Skip if NOT in vault. + if (i.containerType != Enum.ItemContainerType.VAULT) + if (this.doesCharOwnThisItem(i.getObjectUUID()) == false) + return false; + + // Only valid from bank, equip and vault + if (!bankContains(i) && !equippedContains(i) && !vaultContains(i)) + return false; + + if (equippedContains(i)) { + fromEquip = true; + ItemBase ib = i.getItemBase(); + if (ib != null && ib.getType().equals(ItemType.GOLD)) + this.absCharacter.cancelOnUnEquip(); + } + + // check to see what type of AbstractCharacter subclass we have stored + if (this.absCharacter.getClass() == PlayerCharacter.class) { + if (!i.moveItemToInventory((PlayerCharacter) this.absCharacter)) + return false; + } else if (!i.moveItemToInventory((NPC) this.absCharacter)) + return false; + + // remove it from other lists: + this.remItemFromLists(i, slot); + + // add to Inventory + this.inventory.add(i); + i.addToCache(); + this.itemIDtoType.put(i.getObjectUUID(), i.getObjectType().ordinal()); + + calculateWeights(); + } + + //Apply bonuses if from equip + if (fromEquip && this.absCharacter != null) { + this.absCharacter.applyBonuses(); + if (this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter)) + this.absCharacter.incVer(); + } + + return true; + } + + public synchronized boolean moveItemToBank(Item i) { + byte slot = i.getEquipSlot(); + + if (this.doesCharOwnThisItem(i.getObjectUUID()) == false) + return false; + + // Item must be in inventory to move to bank + if (!this.inventory.contains(i)) + return false; + + // check to see what type of AbstractCharacter subclass we have stored + if (this.absCharacter.getClass() == PlayerCharacter.class) { + if (!i.moveItemToBank((PlayerCharacter) this.absCharacter)) + return false; + } else if (!i.moveItemToBank((NPC) this.absCharacter)) + return false; + + // remove it from other lists: + this.remItemFromLists(i, slot); + + // add to Bank + this.bank.add(i); + i.addToCache(); + + calculateWeights(); + + return true; + } + + public synchronized boolean moveGoldToBank(Item from, int amt) { + if (from == null) + return false; + if (from.getNumOfItems() - amt < 0) + return false; + if (this.goldBank.getNumOfItems() + amt > MBServerStatics.BANK_GOLD_LIMIT){ + if (this.absCharacter.getObjectType() == GameObjectType.PlayerCharacter){ + PlayerCharacter pc = (PlayerCharacter)this.absCharacter; + if (pc.getClientConnection() != null) + ErrorPopupMsg.sendErrorPopup(pc, 202); + return false; + } + } + + if (!DbManager.ItemQueries.MOVE_GOLD(from, this.getGoldBank(), amt)) + return false; + from.setNumOfItems(from.getNumOfItems() - amt); + this.goldBank.setNumOfItems(this.goldBank.getNumOfItems() + amt); + return true; + } + + public synchronized boolean moveGoldToVault(Item from, int amt) { + if (from == null) + return false; + if (from.getNumOfItems() - amt < 0) + return false; + if (!DbManager.ItemQueries.MOVE_GOLD(from, this.account.vaultGold, amt)) + return false; + from.setNumOfItems(from.getNumOfItems() - amt); + this.account.vaultGold.setNumOfItems(this.goldVault.getNumOfItems() + amt); + return true; + } + + public synchronized boolean moveGoldToInventory(Item from, int amt) { + if (from == null) + return false; + if (from.getNumOfItems() - amt < 0 || amt < 1) + return false; + + if (this.goldInventory.getNumOfItems() + amt > MBServerStatics.PLAYER_GOLD_LIMIT){ + if (this.absCharacter.getObjectType() == GameObjectType.PlayerCharacter){ + PlayerCharacter pc = (PlayerCharacter)this.absCharacter; + if (pc.getClientConnection() != null) + ErrorPopupMsg.sendErrorPopup(pc, 202); + return false; + } + } + + if (from instanceof MobLoot) { + if (!DbManager.ItemQueries.UPDATE_GOLD(this.getGoldInventory(), + this.goldInventory.getNumOfItems() + amt)) + return false; + } else if (!DbManager.ItemQueries.MOVE_GOLD(from, this.goldInventory, amt)) + return false; + from.setNumOfItems(from.getNumOfItems() - amt); + this.goldInventory.setNumOfItems(this.goldInventory.getNumOfItems() + amt); + return true; + } + + //This is called by the addGold devCmd. + public synchronized boolean addGoldToInventory(int amt, boolean fromDevCmd) { + + if (this.absCharacter == null || (!(this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter)))) + return false; + + if (this.getGoldInventory().getNumOfItems() + amt > MBServerStatics.PLAYER_GOLD_LIMIT){ + return false; + } + + if (this.getGoldInventory().getNumOfItems() + amt < 0) + return false; + + + boolean worked = DbManager.ItemQueries.UPDATE_GOLD(this.getGoldInventory(), this.goldInventory.getNumOfItems() + amt); + if (worked) { + //log this since it's technically a dupe. Only use on test server! + if (fromDevCmd) { + String logString = this.absCharacter.getName() + " added " + amt + " gold to their inventory"; + Logger.info(logString); + } + this.goldInventory.setNumOfItems(this.goldInventory.getNumOfItems() + amt); + } + return worked; + } + + //Used to trainsfer gold from one inventory to another, for steal, etc. + public boolean transferGoldToMyInventory(AbstractCharacter tar, int amount) { + if (tar == null) + return false; + + CharacterItemManager tarCim = tar.getCharItemManager(); + if (tarCim == null) + return false; + + if (this.getGoldInventory().getNumOfItems() + amount < 0) + return false; + + if (this.getGoldInventory().getNumOfItems() + amount > MBServerStatics.PLAYER_GOLD_LIMIT) + return false; + + if (tarCim.getGoldInventory().getNumOfItems() -amount < 0) + return false; + + if (tarCim.getGoldInventory().getNumOfItems() - amount > MBServerStatics.PLAYER_GOLD_LIMIT) + return false; + + synchronized (this) { + synchronized (tarCim) { + if (!tarCim.addGoldToInventory(0 - amount, false)) //remove gold from target + return false; + if (!addGoldToInventory(amount, false)) //add to this inventory + return false; + } + } + return true; + } + + public synchronized boolean moveItemToVault(Item i) { + byte slot = i.getEquipSlot(); + + // if (this.doesCharOwnThisItem(i.getObjectUUID()) == false) + // return false; + + // Item must be in inventory to move to vault + if (!this.inventory.contains(i)) + return false; + + // check to see what type of AbstractCharacter subclass we have stored + if (this.absCharacter.getClass() == PlayerCharacter.class) { + if (!i.moveItemToVault(this.account)) + return false; + } else + return false; // NPC's dont have vaults! + + // remove it from other lists: + this.remItemFromLists(i, slot); + + // add to Vault + i.addToCache(); + + calculateWeights(); + + return true; + } + + // This removes ingame item from inventory for loot. + private synchronized boolean removeItemFromInventory(Item i) { + if (i.getItemBase().getType().equals(ItemType.GOLD)) { + if (i.getObjectUUID() != this.getGoldInventory().getObjectUUID()) + return false; + if (!DbManager.ItemQueries.UPDATE_GOLD(this.goldInventory, 0)){ + return false; + } + + } else { + if (this.doesCharOwnThisItem(i.getObjectUUID()) == false) + return false; + if (this.inventory.contains(i)) { + this.inventory.remove(i); + this.itemIDtoType.remove(i.getObjectUUID()); + return true; + } + } + // tell client we're removing item + updateInventory(i, false); + return false; + } + + // This adds item to inventory for loot. Validity checks already handled + public synchronized boolean addItemToInventory(Item i) { + if (i.getItemBase().getType().equals(ItemType.GOLD)) + if (this.absCharacter.getObjectType() == GameObjectType.Mob) { + if (this.goldInventory == null) + loadGoldItems(); + this.goldInventory.setNumOfItems(this.goldInventory.getNumOfItems() + i.getNumOfItems()); + } else { + int amt = i.getNumOfItems(); + if (DbManager.ItemQueries.UPDATE_GOLD(this.goldInventory, this.goldInventory.getNumOfItems() + amt)) { + updateInventory(); + return true; + } + + return false; + } + + this.inventory.add(i); + this.itemIDtoType.put(i.getObjectUUID(), i.getObjectType().ordinal()); + + ItemBase ib = i.getItemBase(); + if (ib != null) + this.inventoryWeight += ib.getWeight(); + return true; + } + + + + //called for adding gold of a specified amount + public synchronized boolean addItemToInventory(Item i, int amount) { + if (i.getItemBase().getType().equals(ItemType.GOLD)) + return DbManager.ItemQueries.UPDATE_GOLD(this.getGoldInventory(), this.goldInventory.getNumOfItems() + amount); + return false; + } + + public boolean equipItem(Item i, byte slot) { + + synchronized (this) { + byte curSlot = i.getEquipSlot(); // Should be 0 + + if (this.doesCharOwnThisItem(i.getObjectUUID()) == false && this.absCharacter.getObjectType() != GameObjectType.Mob) { + Logger.error("Doesnt own item"); + return false; + } + + // Item must be in inventory to equip + if (!this.inventory.contains(i) && this.absCharacter.getObjectType() != GameObjectType.Mob) + return false; + + // make sure player can equip item + if (i.getItemBase() == null) + return false; + if (!i.getItemBase().canEquip(slot, this, absCharacter, i) && this.absCharacter.getObjectType() != GameObjectType.Mob) + return false; + + // check to see if item is already there. + Item old = this.equipped.get((int) slot); + if (old != null) { + Logger.error( "already equipped"); + return false; + } + + // check to see what type of AbstractCharacter subclass we have stored + if (this.absCharacter.getClass() == PlayerCharacter.class) { + if (!i.equipItem((PlayerCharacter) this.absCharacter, slot)) + return false; + } else if (this.absCharacter.getObjectType() == GameObjectType.Mob) { + if (!i.equipItem((Mob) this.absCharacter, slot)) { + Logger.error("Failed to set Equip"); + return false; + } + + } else if (!i.equipItem((NPC) this.absCharacter, slot)) + return false; + + // remove it from other lists: + this.remItemFromLists(i, slot); + + // add to Equipped + this.equipped.put((int) slot, i); + i.addToCache(); + + addEquipOrder(i.getEquipSlot()); + + //calculateWeights(); + } + + //Apply Bonuses and update player + if (this.absCharacter != null) { + this.absCharacter.applyBonuses(); + if (this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter)) + this.absCharacter.incVer(); + } + + return true; + } + + //Used for buying MobEquipment from NPC + //Handles the gold transfer aspect + + public synchronized boolean buyFromNPC(Building vendorBuilding, int cost,int buildingDeposit) { + + Item gold = this.getGoldInventory(); + + if (cost <= 0 || (gold.getNumOfItems() - cost) < 0) + return false; + + + if (this.getOwner() != null && this.getOwner().getObjectType().equals(GameObjectType.PlayerCharacter)){ + if (this.goldTradingAmount > 0){ + ErrorPopupMsg.sendErrorPopup((PlayerCharacter)this.getOwner(), 195); + return false; + } + } + + // Create gold from screatch instead of building strongbox + // if the NPC is not slotted. + + if (vendorBuilding == null) { + + return this.modifyInventoryGold(-cost); + } + + + if (vendorBuilding.getStrongboxValue() + cost > vendorBuilding.getMaxGold()){ + + if (this.absCharacter.getObjectType() == GameObjectType.PlayerCharacter){ + PlayerCharacter pc = (PlayerCharacter)this.absCharacter; + if (pc.getClientConnection() != null) + ErrorPopupMsg.sendErrorPopup(pc, 206); + } + + return false; + } + + + // Update strongbox and inventory gold + if (!this.modifyInventoryGold(-cost)) + return false; + + City buildingCity = vendorBuilding.getCity(); + + if (buildingCity != null){ + buildingCity.transactionLock.writeLock().lock(); + try{ + if (!vendorBuilding.transferGold(buildingDeposit, true)) + return false; + }catch(Exception e){ + Logger.error(e); + return false; + }finally{ + buildingCity.transactionLock.writeLock().unlock(); + } + }else + if (!vendorBuilding.transferGold(buildingDeposit, true)) + return false; + + return true; + } + + //Used for selling items to NPC + public synchronized boolean sellToNPC(Building building, int cost, Item item) { + + // Create gold from screatch instead of building strongbox + // if the NPC is not slotted. + + if (this.getGoldInventory().getNumOfItems() + cost < 0) + return false; + + if (this.getGoldInventory().getNumOfItems() + cost > MBServerStatics.PLAYER_GOLD_LIMIT) + return false; + + + if (this.getOwner().getCharItemManager().getGoldTrading() > 0){ + if (this.getOwner().getObjectType().equals(GameObjectType.PlayerCharacter)) + ErrorPopupMsg.sendErrorPopup((PlayerCharacter)this.getOwner(), 195); + return false; + } + + + if (building == null) { + return this.modifyInventoryGold(cost); + } + + //make sure strongbox can afford gold. + + if (!building.hasFunds(cost)) + return false; + + if ((building.getStrongboxValue() - cost) < 0) + return false; + + // Update strongbox and inventory gold + + if (!building.transferGold(-cost,false)) + return false; + + return this.modifyInventoryGold(cost); + } + + /** + * This sells an item to an npc + * + * @return True on success + */ + public synchronized boolean sellToNPC(Item itemToSell, NPC npc) { + + CharacterItemManager itemMan; + + if (itemToSell == null || npc == null) + return false; + + itemMan = npc.getCharItemManager(); + + if (itemMan == null) + return false; + + //test npc inventory is not full + + synchronized (this) { + synchronized (itemMan) { + if (!this.doesCharOwnThisItem(itemToSell.getObjectUUID())) + return false; + // attempt to transfer item in db + + boolean sdrMerchant = false; + + if (npc.getContractID() >= 1900 && npc.getContractID() <= 1906) + sdrMerchant = true; + + if (sdrMerchant){ + this.delete(itemToSell); + this.updateInventory(); + + }else + if (!itemToSell.moveItemToInventory(npc)) + return false; + + // db transfer successfull, remove from this character + // skip this check if this is a mobLoot item (which is not in any inventory) + if (!sdrMerchant) + if (!removeItemFromInventory(itemToSell)) + return false; + + // add item to looter. + if(!sdrMerchant) + if (!itemMan.addItemToInventory(itemToSell)) + return false; + } + } + + // calculate new weights + calculateInventoryWeight(); + itemMan.calculateInventoryWeight(); + return true; + } + + /** + * This buys an item from an npc + * Handles transfer of item. + * + * @return True on success + */ + public synchronized boolean buyFromNPC(Item purchasedItem, NPC npc) { + + CharacterItemManager itemMan; + ItemBase itemBase; + + if (purchasedItem == null || npc == null) + return false; + + itemMan = npc.getCharItemManager(); + + if (itemMan == null) + return false; + + + + synchronized (this) { + synchronized (itemMan) { + itemBase = purchasedItem.getItemBase(); + + if (itemBase == null) + return false; + + //test inventory is not full + + if (!hasRoomInventory(itemBase.getWeight())) + return false; + + if (!itemMan.inventory.contains(purchasedItem)) + return false; + // attempt to transfer item in db + + if (purchasedItem.getObjectType() == GameObjectType.MobLoot){ + + Item newItem = ((MobLoot) purchasedItem).promoteToItem((PlayerCharacter)this.absCharacter); + if (newItem == null) + return false; + + if (!itemMan.removeItemFromInventory(purchasedItem)) + return false; + + if (!addItemToInventory(newItem)) + return false; + //Item was created and still a mobloot item, remove from npc production list in db. + + DbManager.NPCQueries.REMOVE_FROM_PRODUCTION_LIST(purchasedItem.getObjectUUID(),npc.getObjectUUID()); + + + }else{ + if (!purchasedItem.moveItemToInventory((PlayerCharacter) this.absCharacter)) + return false; + + if (purchasedItem.getValue() != purchasedItem.getMagicValue()){ + DbManager.ItemQueries.UPDATE_VALUE(purchasedItem,0); + purchasedItem.setValue(0); + } + + // db transfer successfull, remove from this character + // skip this check if this is a mobLoot item (which is not in any inventory) + if (!itemMan.removeItemFromInventory(purchasedItem)) + return false; + + // add item to looter. + + if (!addItemToInventory(purchasedItem)) + return false; + } + + } + } + + // calculate new weights + calculateInventoryWeight(); + itemMan.calculateInventoryWeight(); + return true; + } + + /** + * Loot an item from an AbstractCharacter. Call this function on + * the CharacterItemManager of the current item owner, not the looter. + * This method will verify that the looter can receive the item + * (e.g. inventory isn't full). + * + * @param i Item being looted + * @param looter Player looting the item + * @param origin ClientConnection + * @return True on success + */ + public synchronized Item lootItemFromMe(Item i, PlayerCharacter looter, ClientConnection origin) { + return lootItemFromMe(i, looter, origin, false, -1); + } + + //This function is used for both looting and stealing + public synchronized Item lootItemFromMe(Item lootItem, PlayerCharacter lootingPlayer, ClientConnection origin, boolean fromSteal, int amount) { + + //TODO this function should have more logging + // make sure lootingPlayer exists + if (lootingPlayer == null) + return null; + + // get looters item manager + CharacterItemManager looterItems = lootingPlayer.getCharItemManager(); + + if (looterItems == null) + return null; + + if (fromSteal) { + if (!this.absCharacter.isAlive()) + return null; + } else if (!this.absCharacter.canBeLooted()) + return null; + + MobLoot mobLoot = null; + if (lootItem instanceof MobLoot) { + mobLoot = (MobLoot) lootItem; + if (mobLoot.isDeleted()) + return null; + } + + //Lock both ItemManagers; lower ID first + CharacterItemManager lockFirst; + CharacterItemManager lockSecond; + if (this.absCharacter.getObjectUUID() + < looterItems.absCharacter.getObjectUUID()) { + lockFirst = this; + lockSecond = looterItems; + } else { + lockFirst = looterItems; + lockSecond = this; + } + + synchronized (lockFirst) { + synchronized (lockSecond) { + // make sure current player has item in inventory + if (lootItem.getItemBase().getType().equals(ItemType.GOLD) && lootItem.getObjectUUID() != this.getGoldInventory().getObjectUUID() && !(this.absCharacter.getObjectType().equals(GameObjectType.Mob))) + return null; + else if (!this.inventory.contains(lootItem) && !this.getEquippedList().contains(lootItem) && !lootItem.getItemBase().getType().equals(ItemType.GOLD)) + return null; + + // get weight of item + ItemBase ib = lootItem.getItemBase(); + if (ib == null) + return null; + short weight = ib.getWeight(); + + // make sure lootingPlayer has room for item + if (!lootItem.getItemBase().getType().equals(ItemType.GOLD) && !looterItems.hasRoomInventory(weight)) + return null; + + if (lootItem.getItemBase().getType().equals(ItemType.GOLD)) + if (amount != -1) { //from steal + int total = lootItem.getNumOfItems(); + amount = (amount > total) ? total : amount; + if (!looterItems.moveGoldToInventory(lootItem, amount)) + return null; + if (mobLoot != null && amount == total) + this.delete(mobLoot); + } else { //from loot + if (!looterItems.moveGoldToInventory(lootItem, lootItem.getNumOfItems())) + return null; + if (mobLoot != null) // delete mobloot after it has been looted + this.delete(mobLoot); + } + else { //not Gold item + boolean created = false; + if (mobLoot != null) { + lootItem = mobLoot.promoteToItem(lootingPlayer); + + // delete mobloot after it has been looted + this.delete(mobLoot); + if (lootItem == null) + return null; + + created = true; + } + + // attempt to transfer item in db + + if (!lootItem.moveItemToInventory(lootingPlayer)) + return null; + + // db transfer successfull, remove from this character + // skip this check if this is a mobLoot item (which is not in any inventory) + if (mobLoot == null) + if (!removeItemFromInventory(lootItem)) + return null; + + // add item to lootingPlayer. + if (!looterItems.addItemToInventory(lootItem)) + return null; + } + } + } + + // calculate new weights + calculateInventoryWeight(); + looterItems.calculateInventoryWeight(); + + return lootItem; + } + + private synchronized void remItemFromLists(Item i, byte slot) { + + this.equipped.remove((int) slot); + this.vault.remove(i); + this.bank.remove(i); + this.inventory.remove(i); + } + + /* + * Delegates + */ + public synchronized boolean bankContains(Item i) { + if (i.getItemBase().getType().equals(ItemType.GOLD)) + return (this.getGoldBank() != null && this.goldBank.getObjectUUID() == i.getObjectUUID()); + return bank.contains(i); + } + + + public synchronized boolean inventoryContains(Item i) { + if (i.getItemBase().getType().equals(ItemType.GOLD)) + return (this.getGoldInventory() != null && this.goldInventory.getObjectUUID() == i.getObjectUUID()); + return inventory.contains(i); + } + + public synchronized boolean forgeContains(Item i,NPC vendor) { + if (i.getItemBase().getType().equals(ItemType.GOLD)) + return (this.getGoldInventory() != null && this.goldInventory.getObjectUUID() == i.getObjectUUID()); + return vendor.getRolling().contains(i); + } + + + + public synchronized boolean vaultContains(Item i) { + if (i.getItemBase().getType().equals(ItemType.GOLD)) + return (this.getGoldVault() != null && this.goldVault.getObjectUUID() == i.getObjectUUID()); + return this.account.getVault().contains(i); + } + + public synchronized boolean vaultContainsType(ItemBase ib) { + if (ib.getUUID() == 7) + return (this.getGoldVault() != null); + for (Item i : vault) { + if (i.getItemBase().getUUID() == ib.getUUID()) + return true; + } + return false; + } + + //for calling from devCmd fill vault. Already synchronized + public boolean vaultContainsTypeA(ItemBase ib) { + if (ib.getUUID() == 7) + return (this.getGoldVault() != null); + for (Item i : vault) { + if (i.getItemBase().getUUID() == ib.getUUID()) + return true; + } + return false; + } + + + public synchronized boolean equippedContains(Item i) { + return equipped.containsValue(i); + } + + public synchronized Item getItemFromEquipped(int slot) { + return equipped.get(slot); + } + + public synchronized Item getItemByUUID(int objectUUID) { + if (this.itemIDtoType.containsKey(objectUUID)){ + + Integer integer = this.itemIDtoType.get(objectUUID); + if (integer == GameObjectType.Item.ordinal()) { + return Item.getFromCache(objectUUID); + } else if (integer == GameObjectType.MobLoot.ordinal()) { + return MobLoot.getFromCache(objectUUID); + } + + } + + if (this.getGoldInventory() != null && this.goldInventory.getObjectUUID() == objectUUID) + return this.goldInventory; + if (this.getGoldBank() != null && this.goldBank.getObjectUUID() == objectUUID) + return this.goldBank; + if (this.getGoldVault() != null && this.goldVault.getObjectUUID() == objectUUID) + return this.goldVault; + return null; + } + + public boolean tradingContains(Item i) { + if (this.trading == null || i == null) + return false; + return this.trading.contains(i.getObjectUUID()); + } + + public boolean isBankOpen() { + return this.bankOpened; + } + + public synchronized void setBankOpen(boolean bankOpened) { + this.bankOpened = bankOpened; + } + + public boolean isVaultOpen() { + return this.vaultOpened; + } + + public synchronized void setVaultOpen(boolean vaultOpened) { + this.vaultOpened = vaultOpened; + } + + public ClientConnection getTradingWith() { + return tradingWith; + } + + public synchronized void setTradingWith(ClientConnection tradingWith) { + this.tradingWith = tradingWith; + } + + public synchronized void clearTradingWith() { + this.tradingWith = null; + } + + public int getGoldTrading() { + return goldTradingAmount; + } + + public synchronized void setTradeCommitted(byte tradeCommitted) { + this.tradeCommitted = tradeCommitted; + } + + public byte getTradeCommitted() { + return tradeCommitted; + } + + public HashSet getTrading() { + return trading; + } + + + public synchronized void addItemToTrade(Item i) { + this.trading.add(i.getObjectUUID()); + } + + + public synchronized void setTradeSuccess(boolean tradeSuccess) { + this.tradeSuccess = tradeSuccess; + } + + public boolean getTradeSuccess() { + return tradeSuccess; + } + + + public synchronized boolean RemoveEquipmentFromLackOfSkill(PlayerCharacter pc, boolean initialized) { + + + if (pc == null) + return false; + + if (this.equipped == null) + return false; + + + for (int slot : this.equipped.keySet()) { + + if (slot == MBServerStatics.SLOT_HAIRSTYLE || slot == MBServerStatics.SLOT_BEARDSTYLE) + continue; + + Item item = this.equipped.get(slot); + + if (item == null){ + this.equipped.remove(slot); + pc.applyBonuses(); + continue; + } + + if (!item.getItemBase().validForSkills(pc.getSkills())){ + this.forceToInventory(slot, item, pc, initialized); + pc.applyBonuses(); + } + } + + return true; + } + + /* + * List Copiers + */ + /** + * Note that this method returns a copy of the internally stored + * list. + * + * @return the equipped + */ + public ConcurrentHashMap getEquipped() { + synchronized (this.equipped) { + return new ConcurrentHashMap<>(this.equipped); + } + } + + public ArrayList getEquippedList() { + ArrayList ret = new ArrayList<>(); + synchronized (this.equipOrder) { + synchronized (this.equipped) { + for (int slot : this.equipOrder) { + if (this.equipped.containsKey(slot)) + ret.add(this.equipped.get(slot)); + } + if (ret.size() != this.equipped.size()) + //missed adding some items, figure out what. + for (int slot : this.equipped.keySet()) { + if (!(this.equipOrder.contains(slot))) { + this.equipOrder.add(slot); + ret.add(this.equipped.get(slot)); + } + } + } + } + return ret; + } + + public Item getEquipped(int slot) { + synchronized (this.equipped) { + return this.equipped.get(slot); + } + } + + /** + * Note that this method returns a copy of the internally stored + * list. + * + * @return the inventory + */ + public ArrayList getInventory() { + return getInventory(false); + } + + public ArrayList getInventory(boolean sendGold) { + synchronized (this.inventory) { + ArrayList ret = new ArrayList<>(this.inventory); + if (sendGold && this.getGoldInventory() != null && this.goldInventory.getNumOfItems() > 0) + ret.add(this.goldInventory); + return ret; + } + } + + public int getInventoryCount() { + synchronized (this.inventory) { + return this.inventory.size(); + } + } + + /** + * Clears ownership of items. Called when player dies, but before + * respawning. + * + * @return the inventory + */ + public synchronized void orphanInventory() { + PlayerCharacter pc = null; + if (this.absCharacter != null && this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter)) + pc = (PlayerCharacter) this.absCharacter; + synchronized (this.inventory) { + //dupe check, validate player properly owns all items + if (pc != null) { + Iterator iter = this.inventory.iterator(); + while (iter.hasNext()) { + Item item = iter.next(); + //this call may remove the item from this.inventory + if (!item.validForInventory(pc.getClientConnection(), pc, this)) { + } + } + } + + if (this.inventory.size() > 0) + DbManager.ItemQueries.ORPHAN_INVENTORY(this.inventory); + //make a copy of gold inventory for looting + //so we don't remove the goldInventory + if (this.getGoldInventory().getNumOfItems() > 0) { + int amt = this.goldInventory.getNumOfItems(); + if (DbManager.ItemQueries.UPDATE_GOLD(this.goldInventory, 0)) { + this.goldInventory.setNumOfItems(0); + MobLoot gold = new MobLoot(this.absCharacter, amt); + this.inventory.add(gold); + } + } + } + } + + /** + * This transfers the entire inventory to another list For populating + * corpse' inventory when player dies + * + * @return the inventory + */ + public synchronized void transferEntireInventory( + ArrayList newInventory, Corpse corpse, boolean enterWorld) { + + PlayerCharacter pc = null; + if (this.absCharacter != null && this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter)) + pc = (PlayerCharacter) this.absCharacter; + + if (this.getGoldInventory().getNumOfItems() > 0) { + int amt = this.goldInventory.getNumOfItems(); + if (DbManager.ItemQueries.UPDATE_GOLD(this.goldInventory, 0)) { + this.goldInventory.setNumOfItems(0); + MobLoot gold = new MobLoot(this.absCharacter, amt); + newInventory.add(gold); + } + } + + for (Item item : this.inventory) { + if (item != null) + if (item instanceof MobLoot) { + + //MobLoot + item.zeroItem(); + item.containerType = Enum.ItemContainerType.INVENTORY; + + if (item.getItemBase().getType().equals(ItemType.GOLD)) + //only add gold item once + if (!corpse.hasGold()) + corpse.setHasGold(true); + newInventory.add(item); + } else //item + if (item.getItemBase().getType().equals(ItemType.GOLD)) { + int amt = item.getNumOfItems(); + item.setNumOfItems(0); + MobLoot ml = new MobLoot(this.absCharacter, amt); + ml.zeroItem(); + ml.containerType = Enum.ItemContainerType.INVENTORY; + if (!corpse.hasGold()) { + corpse.setHasGold(true); + newInventory.add(ml); + } + } else { + boolean transferred = item.moveItemToInventory(corpse); + if (!transferred) + Logger.error( + "CharItemManager.transferEntireInvetory", + "DB Error, Failed to transfer item " + + item.getObjectUUID() + " to new owner " + + corpse.getObjectUUID()); + newInventory.add(item); + + } + } + + // tell client we're clearing inventory + + + // clear the inventory. + this.inventory.clear(); + + //re-calculate inventory weight + calculateInventoryWeight(); + if (!enterWorld) + updateInventory(this.getInventory(), false); + } + + public synchronized void purgeInventory() { + + if (!this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter)) + return; + + if (this.goldInventory != null) + if (this.getGoldInventory().getNumOfItems() > 0) { + if (DbManager.ItemQueries.UPDATE_GOLD(this.goldInventory, 0)) { + this.goldInventory.setNumOfItems(0); + } + } + + if (this.inventory.size() > 0) + DbManager.ItemQueries.ORPHAN_INVENTORY(this.inventory); + + // clear the inventory. + this.inventory.clear(); + //re-calculate inventory weight + calculateInventoryWeight(); + } + + /** + * Note that this method returns a copy of the internally stored + * list. + * + * @return the bank + */ + public ArrayList getBank() { + synchronized (this.bank) { + ArrayList ret = new ArrayList<>(this.bank); + if (this.getGoldBank() != null && this.goldBank.getNumOfItems() > 0) + ret.add(this.goldBank); + return ret; + } + } + + /** + * Note that this method returns a copy of the internally stored + * list. + * + * @return the vault + */ + public ArrayList getVault() { + synchronized (this.vault) { + ArrayList ret = new ArrayList<>(this.vault); + if (this.getGoldVault() != null && this.goldVault.getNumOfItems() > 0) + ret.add(this.goldVault); + return ret; + } + } + + public boolean hasRoomInventory(short weight) { + if (this.absCharacter == null) + return false; + if (this.absCharacter.getObjectType() == GameObjectType.PlayerCharacter) { + PlayerCharacter pc = (PlayerCharacter) this.absCharacter; + int newWeight = this.getCarriedWeight() + weight; + return newWeight <= (int) pc.statStrBase * 3; + } else if (this.absCharacter.getObjectType() == GameObjectType.NPC){ + int newWeight = this.getCarriedWeight() + weight; + return newWeight <= 1900 + (this.absCharacter.getLevel() * 3); + }else + return true; // npc's need checked + } + + public boolean hasRoomTrade(short itemWeight) { + + PlayerCharacter playerCharacter; + PlayerCharacter tradeCharacter; + + int tradeWeight; + + if (this.absCharacter == null) + return false; + + if (this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter) == false) + return false; + + playerCharacter = (PlayerCharacter) this.absCharacter; + + if ((this.tradingWith == null) || + (this.tradingWith.isConnected() == false)) + return false; + + tradeCharacter = this.tradingWith.getPlayerCharacter(); + + tradeWeight = this.getCarriedWeight() + itemWeight; + tradeWeight = tradeWeight + tradeCharacter.getCharItemManager().getTradingWeight(); + tradeWeight = tradeWeight - this.getTradingWeight(); + + return tradeWeight <= (int) playerCharacter.statStrBase * 3; + } + + public boolean hasRoomBank(short weight) { + if (this.absCharacter == null) + return false; + return weight <= this.absCharacter.getBankCapacityRemaining(); + } + + public boolean hasRoomVault(short weight) { + if (this.absCharacter == null) + return false; + return weight <= this.absCharacter.getVaultCapacityRemaining(); + } + + public int getCarriedWeight() { + return getInventoryWeight() + getEquipWeight(); + } + + public int getInventoryWeight() { + return this.inventoryWeight; + } + + public int getBankWeight() { + return this.bankWeight; + } + + public int getEquipWeight() { + return this.equipWeight; + } + + public int getVaultWeight() { + return this.vaultWeight; + } + + public int getTradingForWeight() { + return calculateTradingForWeight(); + } + + public int getTradingWeight() { + + int weight = 0; + Item item; + + for (int i : this.trading) { + item = Item.getFromCache(i); + + if (item == null) + continue; + + ItemBase ib = item.getItemBase(); + weight += ib.getWeight(); + } + return weight; + } + + public AbstractCharacter getOwner() { + return this.absCharacter; + } + + public void calculateWeights() { + calculateBankWeight(); + calculateInventoryWeight(); + calculateEquipWeight(); + calculateVaultWeight(); + } + + public void calculateBankWeight() { + this.bankWeight = 0; + for (Item i : this.bank) { + ItemBase ib = i.getItemBase(); + if (ib != null) + this.bankWeight += ib.getWeight(); + } + } + + public void calculateEquipWeight() { + this.equipWeight = 0; + Collection c = this.equipped.values(); + Iterator it = c.iterator(); + while (it.hasNext()) { + Item i = it.next(); + ItemBase ib = i.getItemBase(); + if (ib != null) + this.equipWeight += ib.getWeight(); + } + } + + public void calculateInventoryWeight() { + this.inventoryWeight = 0; + for (Item i : this.inventory) { + ItemBase ib = i.getItemBase(); + if (ib != null) + this.inventoryWeight += ib.getWeight(); + } + } + + public void calculateVaultWeight() { + this.vaultWeight = 0; + for (Item i : this.vault) { + ItemBase ib = i.getItemBase(); + if (ib != null) + this.vaultWeight += ib.getWeight(); + } + } + + private int calculateTradingForWeight() { + int tradingForWeight = 0; + + return tradingForWeight; + } + + + public void updateInventory(Item item, boolean add) { + ArrayList list = new ArrayList<>(); + list.add(item); + updateInventory(list, add); + } + + private void updateInventory(ArrayList inventory, boolean add) { + + if (this.absCharacter == null) + return; + + if (this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter) == false) + return; + + PlayerCharacter pc = (PlayerCharacter) this.absCharacter; + + UpdateInventoryMsg updateInventoryMsg = new UpdateInventoryMsg(inventory, this.getBank(), this.getGoldInventory(), add); + Dispatch dispatch = Dispatch.borrow(pc, updateInventoryMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + } + + public void forceToInventory(int slot, Item item, PlayerCharacter pc, boolean initialized) { + if (item == null || pc == null) + return; + + if (!item.moveItemToInventory(pc)) { + //TODO well why did this fail? clean it up + } + + // remove it from other lists: + this.remItemFromLists(item, (byte) slot); + + // add to Inventory + this.inventory.add(item); + item.addToCache(); + + calculateWeights(); + + //Update players with unequipped item + if (initialized) { + TransferItemFromEquipToInventoryMsg back = new TransferItemFromEquipToInventoryMsg(pc, slot); + DispatchMessage.dispatchMsgToInterestArea(pc, back, engine.Enum.DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + } + + } + + /** + * Update the player's inventory window by resending the entire contents. + */ + public void updateInventory() { + this.updateInventory(this.getInventory(), true); + } + + public synchronized void initializeTrade() { + this.trading = new HashSet<>(); + } + + public synchronized boolean commitTrade() { + int goldFrom1 = 0; + int goldFrom2 = 0; + + if (this.getTradingWith() == null || this.getTradingWith().isConnected() == false + || this.getTradingWith().getPlayerCharacter() == null){ + this.endTrade(); + return false; + } + + + CharacterItemManager tradingWith = this.getTradingWith().getPlayerCharacter().getCharItemManager(); + + if (tradingWith == null) + return false; + + if (this.goldTradingAmount != 0) { + + if (tradingWith.goldInventory == null){ + Logger.error("Null Gold for player " + this.getOwner().getObjectUUID()); + return false; + } + goldFrom1 = this.goldTradingAmount; + } + if (tradingWith.goldTradingAmount != 0) { + + if (this.getGoldInventory() == null){ + Logger.error("Null Gold for player " + this.getOwner().getObjectUUID()); + return false; + } + goldFrom2 = tradingWith.goldTradingAmount; + } + + + if (this.getGoldInventory().getNumOfItems() + goldFrom2 > 10000000){ + PlayerCharacter pc = (PlayerCharacter)this.absCharacter; + if (pc.getClientConnection() != null) + ErrorPopupMsg.sendErrorPopup(pc, 202); + return false; + } + + + if (tradingWith.getGoldInventory().getNumOfItems() + goldFrom1 > 10000000){ + PlayerCharacter pc = (PlayerCharacter)tradingWith.absCharacter; + if (pc.getClientConnection() != null) + ErrorPopupMsg.sendErrorPopup(pc, 202); + return false; + } + + if (this.trading.size() > 0 || tradingWith.trading.size() > 0 || goldFrom1 > 0 || goldFrom2 > 0) { + if (!DbManager.ItemQueries.DO_TRADE(this.trading, tradingWith.trading, this, tradingWith, + this.goldInventory, tradingWith.goldInventory, goldFrom1, goldFrom2)) + return false; + } else + return true; + + for (int i : this.trading) { + Item item = Item.getFromCache(i); + if (item == null) + continue; + this.trade(item); + tradingWith.tradeForItem(item); + } + for (int i : tradingWith.trading) { + Item item = Item.getFromCache(i); + if (item == null) + continue; + tradingWith.trade(item); + this.tradeForItem(item); + } + + //subtract gold your trading from your inventory. + if (this.goldTradingAmount > 0) + this.getGoldInventory().setNumOfItems(this.getGoldInventory().getNumOfItems() - this.goldTradingAmount); + //subtract gold your trading from your inventory. + if (tradingWith.goldTradingAmount > 0) + tradingWith.getGoldInventory().setNumOfItems(tradingWith.getGoldInventory().getNumOfItems() - tradingWith.goldTradingAmount); + + if (tradingWith.goldTradingAmount > 0) + this.getGoldInventory().setNumOfItems(this.goldInventory.getNumOfItems() + + tradingWith.goldTradingAmount); + if (this.goldTradingAmount > 0) + tradingWith.getGoldInventory().setNumOfItems(tradingWith.goldInventory.getNumOfItems() + + this.goldTradingAmount); + + this.tradeSuccess = true; + tradingWith.tradeSuccess = true; + + return true; + + } + + public synchronized void endTrade() { + updateInventory(this.getInventory(), true); + this.tradeCommitted = (byte) 0; + this.tradeSuccess = false; + this.tradingWith = null; + this.trading = null; + this.goldTradingAmount = 0; + this.tradeID = 0; + } + + public synchronized void endTrade(boolean fromDeath) { + this.tradeCommitted = (byte) 0; + this.tradeSuccess = false; + this.tradingWith = null; + this.trading = null; + this.goldTradingAmount = 0; + } + + // Remove item from your possession + private synchronized boolean trade(Item i) { + if (this.doesCharOwnThisItem(i.getObjectUUID()) == false) + return false; + + // Only valid from inventory + if (!inventoryContains(i)) + return false; + + // remove from Inventory + this.inventory.remove(i); + this.itemIDtoType.remove(i.getObjectUUID()); + i.setOwnerID(0); + + calculateWeights(); + + return true; + } + + //Damage an equipped item a specified amount + public void damageItem(Item item, int amount) { + if (item == null || amount < 1 || amount > 5) + return; + + //verify the item is equipped by this player + int slot = item.getEquipSlot(); + if (!this.equipped.containsKey(slot)) + return; + Item verify = this.equipped.get(slot); + if (verify == null || item.getObjectUUID() != verify.getObjectUUID()) + return; + + //don't damage noob gear, hair or beards. + if (item.getDurabilityMax() == 0) + return; + + if (!item.isCanDestroy()) + return; + + int dur = (int) item.getDurabilityCurrent(); + if (dur - amount <= 0) { + //destroy the item + junk(item); + + //TODO remove item from the client + //This may not be correct + dur = 0; + } else { + dur -= amount; + if (!DbManager.ItemQueries.SET_DURABILITY(item, dur)) + return; + item.setDurabilityCurrent((short) dur); + + } + + if (this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter) == false) + return; + + //send damage item msg to client + PlayerCharacter pc = (PlayerCharacter) this.absCharacter; + + ItemHealthUpdateMsg itemHealthUpdateMsg = new ItemHealthUpdateMsg(slot, (float) dur); + Dispatch dispatch = Dispatch.borrow(pc, itemHealthUpdateMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + } + + //Damage a random piece of armor a specified amount + public void damageRandomArmor(int amount) { + ArrayList armor = new ArrayList<>(); + if (this.equipped.containsKey(MBServerStatics.SLOT_OFFHAND)) { + Item item = this.equipped.get(MBServerStatics.SLOT_OFFHAND); + ItemBase ib = item.getItemBase(); + if (ib.isShield()) + armor.add(item); + } + if (this.equipped.containsKey(MBServerStatics.SLOT_HELMET)) + armor.add(this.equipped.get(MBServerStatics.SLOT_HELMET)); + if (this.equipped.containsKey(MBServerStatics.SLOT_CHEST)) + armor.add(this.equipped.get(MBServerStatics.SLOT_CHEST)); + if (this.equipped.containsKey(MBServerStatics.SLOT_ARMS)) + armor.add(this.equipped.get(MBServerStatics.SLOT_ARMS)); + if (this.equipped.containsKey(MBServerStatics.SLOT_GLOVES)) + armor.add(this.equipped.get(MBServerStatics.SLOT_GLOVES)); + if (this.equipped.containsKey(MBServerStatics.SLOT_GLOVES)) + armor.add(this.equipped.get(MBServerStatics.SLOT_GLOVES)); + if (this.equipped.containsKey(MBServerStatics.SLOT_LEGGINGS)) + armor.add(this.equipped.get(MBServerStatics.SLOT_LEGGINGS)); + if (this.equipped.containsKey(MBServerStatics.SLOT_FEET)) + armor.add(this.equipped.get(MBServerStatics.SLOT_FEET)); + + if (armor.isEmpty()) + return; //nothing to damage + + int roll = ThreadLocalRandom.current().nextInt(armor.size()); + damageItem(armor.get(roll), amount); + } + + //Damage all equipped gear a random amount between 1 and 5 + public void damageAllGear() { + for (Item gear : this.equipped.values()) { + damageItem(gear, (ThreadLocalRandom.current().nextInt(5) + 1)); + } + } + + // Add item to your possession + public synchronized boolean tradeForItem(Item i) { + // add to Inventory + this.inventory.add(i); + this.itemIDtoType.put(i.getObjectUUID(), i.getObjectType().ordinal()); + i.setOwnerID(this.absCharacter.getObjectUUID()); + + calculateWeights(); + + return true; + } + + public synchronized boolean addGoldToTrade(int amount) { + + if (this.goldTradingAmount + amount > MBServerStatics.PLAYER_GOLD_LIMIT) + return false; + + this.goldTradingAmount += amount; + + return true; + } + + /** + * Completely empties inventory, deleting any items. Use with caution! + */ + public synchronized void clearInventory() { + this.getGoldInventory().setNumOfItems(0); + Iterator ii = this.inventory.iterator(); + while (ii.hasNext()) { + Item itm = ii.next(); + ii.remove(); + this.delete(itm); + } + } + + public synchronized void clearEquip() { + + ArrayList equipCopy = new ArrayList<>(this.getEquippedList()); + Iterator ii = equipCopy.iterator(); + while (ii.hasNext()) { + Item itm = ii.next(); + this.getEquippedList().remove(itm); + this.delete(itm); + } + } + + public byte getEquipVer() { + return this.equipVer; + } + + public static byte getInventoryVer() { + return inventoryVer; + } + + public static byte getBankVer() { + return bankVer; + } + + public static byte getVaultVer() { + return vaultVer; + } + + public void incEquipVer() { + this.equipVer++; + } + + public void incInventoryVer() { + this.equipVer++; + } + + public void incBankVer() { + this.equipVer++; + } + + public void incVaultVer() { + this.equipVer++; + } + + public static void takeFromNPC(NPC npc, PlayerCharacter pc, Item take, ClientMessagePump clientMessagePump) { + ItemBase ib = take.getItemBase(); + if (ib == null) + return; + CharacterItemManager itemMan = pc.getCharItemManager(); + if (itemMan == null) + return; + CharacterItemManager npcCim = npc.getCharItemManager(); + if (npcCim == null) + return; + if (!npcCim.inventoryContains(take)) { + return; + } + + if (!itemMan.hasRoomInventory(ib.getWeight())) + return; + if (take != null) { + itemMan.buyFromNPC(take, npc); + itemMan.updateInventory(); + } + } + + public int getTradeID() { + return tradeID; + } + + public synchronized boolean closeTradeWindow(){ + if (this.getTradingWith() != null || this.getTradeID() != 0) + this.closeTradeWindow(new CloseTradeWindowMsg(this.getOwner(), this.getTradeID()), true); + return true; + + } + +} diff --git a/src/engine/objects/CharacterPower.java b/src/engine/objects/CharacterPower.java new file mode 100644 index 00000000..7ef1a3d7 --- /dev/null +++ b/src/engine/objects/CharacterPower.java @@ -0,0 +1,628 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.gameManager.DbManager; +import engine.gameManager.PowersManager; +import engine.net.ByteBufferWriter; +import engine.net.client.msg.ErrorPopupMsg; +import engine.powers.PowersBase; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + + +public class CharacterPower extends AbstractGameObject { + + private final PowersBase power; + private AtomicInteger trains = new AtomicInteger(); + private short grantedTrains; + private int ownerUID; + private boolean trained = false; + private int requiredLevel = 0; + + + + + /** + * No Table ID Constructor + */ + public CharacterPower(PowersBase power, PlayerCharacter pc) { + super(); + this.power = power; + this.trains.set(0); + this.grantedTrains = this.grantedTrains; + this.ownerUID = pc.getObjectUUID(); + + } + + /** + * Normal Constructor + */ + public CharacterPower(PowersBase power, PlayerCharacter pc, int newUUID) { + super(newUUID); + this.power = power; + this.trains.set(0); + this.grantedTrains = this.grantedTrains; + this.ownerUID = pc.getObjectUUID(); + + + } + + + /** + * ResultSet Constructor + */ + public CharacterPower(ResultSet rs, PlayerCharacter pc) throws SQLException { + super(rs); + int powersBaseToken = rs.getInt("PowersBaseToken"); + this.power = PowersManager.getPowerByToken(powersBaseToken); + + if (this.power != null && this.power.isWeaponPower()) + this.trains.set(0); + else + this.trains.set(rs.getInt("trains")); + this.grantedTrains = this.grantedTrains; + this.ownerUID = pc.getObjectUUID(); + + } + + public CharacterPower(ResultSet rs) throws SQLException { + super(rs); + int powersBaseToken = rs.getInt("PowersBaseToken"); + this.power = PowersManager.getPowerByToken(powersBaseToken); + this.trains.set(rs.getInt("trains")); + this.grantedTrains = this.grantedTrains; + this.ownerUID = rs.getInt("CharacterID"); + + // this.owner = DbManager.PlayerCharacterQueries.GET_PLAYER_CHARACTER(rs.getInt("CharacterID")); + } + + private short getGrantedTrains(PlayerCharacter pc) { + if (this.power != null && pc != null) { + // if (this.power.isWeaponPower()) { + // SkillsBase sb = null; + // try { + // sb = SkillsBase.getSkillsBaseByName(this.power.getSkillName()); + // } catch (SQLException e) {} + // if (sb != null) { + // return pc.getBonuses().getByte("gt." + sb.getToken()); + // } else + // return pc.getBonuses().getByte("gt." + this.power.getToken()); + // } else + // return pc.getBonuses().getByte("gt." + this.power.getToken()); + return PowerGrant.getGrantedTrains(this.power.getToken(), pc); + } + else + return 0; + } + + /* + * Getters + */ + public PowersBase getPower() { + return power; + } + + public int getPowerID() { + return power.getUUID(); + } + + public boolean isTrained() { + return trained; + } + + public static PlayerCharacter getOwner(CharacterPower cp) { + return PlayerCharacter.getFromCache(cp.ownerUID); + } + + public void setTrained(boolean b) { + trained = b; + } + + public int getTrains() { + return this.trains.get(); + } + + public short getGrantedTrains() { + return this.grantedTrains; + } + + public int getTotalTrains() { + return (this.trains.get() + this.grantedTrains); + } + + public float getTrainingCost(PlayerCharacter pc, NPC trainer){ + int charLevel = pc.getLevel(); + int skillRank = this.trains.get() -1 + this.requiredLevel; + + + float baseCost = 50 * this.requiredLevel ; //TODO GET BASE COSTS OF SKILLS. + + + + float sellPercent = -4f; //NOT SELL PERCENT! + float cost; + float const5; + int const2 = 1; + float const3 = 50; + float const4 = const3 + const2; + if (charLevel > 50) + const5 = 50 / const4; + else + const5 = charLevel/const4; + + const5 = 1-const5; + const5 = (float) (Math.log(const5) / Math.log(2) * .75f); + float rounded5 = Math.round(const5); + const5 = rounded5 - const5; + + const5 *= -1; + + const5 = (float) (Math.pow(2, const5) - 1); + + const5 +=1; + const5 = Math.scalb(const5, (int) rounded5); + const5 *= (charLevel - skillRank); + const5 *= sellPercent; + + const5 = (float) (Math.log(const5) / Math.log(2) * 3); + rounded5 = Math.round(const5); + const5 = rounded5 - const5; + const5 *= -1; + const5 = (float) (Math.pow(2, const5) - 1); + const5 +=1; + + + const5 = Math.scalb(const5, (int) rounded5); + const5 += 1; + cost = const5 * baseCost; + + + if (Float.isNaN(cost)) + cost = baseCost; + return cost; + } + + public synchronized boolean train(PlayerCharacter pc) { + if (pc == null || this.power == null) + return false; + + //see if any prereqs to train this power is met + if (!canTrain(pc)) + return false; + + boolean succeeded=true; + int oldTrains = this.trains.get(); + int tr = oldTrains + this.grantedTrains; + if (pc.getTrainsAvailable() <= 0) + return false; + if (tr == this.power.getMaxTrains()) //at max, stop here + return false; + else if (tr > this.power.getMaxTrains()) //catch incase we somehow go over + this.trains.set((this.power.getMaxTrains() - this.grantedTrains)); + else //add the train + succeeded = this.trains.compareAndSet(oldTrains, oldTrains+1); + + if (this.trains.get() > this.power.getMaxTrains()) { //double check not over max trains + this.trains.set(this.power.getMaxTrains()); + succeeded = false; + } + + if (succeeded) { + this.trained = true; + + //update database + pc.addDatabaseJob("Skills", MBServerStatics.THIRTY_SECONDS); + + //subtract from trains available + pc.modifyTrainsAvailable(-1); + + pc.calculateSkills(); + return true; + } else + return false; + } + + public boolean reset(PlayerCharacter pc) { + if (pc == null || this.power == null) + return false; + + //see if any prereqs to refine this power is met + + boolean succeeded=true; + int oldTrains = this.trains.get(); + int tr = oldTrains + this.grantedTrains; + if (oldTrains < 1) + return false; + else //subtract the train + succeeded = this.trains.compareAndSet(oldTrains, 0); + if (succeeded) { + this.trained = true; + + //update database + pc.addDatabaseJob("Skills", MBServerStatics.THIRTY_SECONDS); + + //subtract from trains available + pc.modifyTrainsAvailable(oldTrains); + + pc.calculateSkills(); + return true; + } else + return false; + } + + public boolean refine(PlayerCharacter pc) { + if (pc == null || this.power == null) + return false; + + //see if any prereqs to refine this power is met + if (!canRefine(pc)) + return false; + + boolean succeeded=true; + int oldTrains = this.trains.get(); + int tr = oldTrains + this.grantedTrains; + if (oldTrains < 1) + return false; + else //subtract the train + succeeded = this.trains.compareAndSet(oldTrains, oldTrains-1); + if (succeeded) { + this.trained = true; + + //update database + pc.addDatabaseJob("Skills", MBServerStatics.THIRTY_SECONDS); + + //subtract from trains available + pc.modifyTrainsAvailable(1); + + pc.calculateSkills(); + return true; + } else + return false; + } + + + /* + * Utils + */ + + /* + * This iterates through players runes and adds and removes powers as needed + * Don't Call this directly. Instead call pc.calculateSkills(). + */ + public static void calculatePowers(PlayerCharacter pc) { + if (pc == null) + return; + + // First add powers that don't exist + ConcurrentHashMap powers = pc.getPowers(); + // ArrayList genericPowers = PowerReq.getPowerReqsForAll(); + // CharacterPower.grantPowers(genericPowers, powers, pc); + Race race = pc.getRace(); + if (race != null) { + CharacterPower.grantPowers(race.getPowersGranted(), powers, pc); + } else + Logger.error( "Failed to find Race for player " + pc.getObjectUUID()); + BaseClass bc = pc.getBaseClass(); + if (bc != null) { + CharacterPower.grantPowers(bc.getPowersGranted(), powers, pc); + } else + Logger.error( "Failed to find BaseClass for player " + pc.getObjectUUID()); + PromotionClass promo = pc.getPromotionClass(); + if (promo != null) + CharacterPower.grantPowers(promo.getPowersGranted(), powers, pc); + ArrayList runes = pc.getRunes(); + if (runes != null) { + for (CharacterRune rune : runes) { + CharacterPower.grantPowers(rune.getPowersGranted(), powers, pc); + } + } else + Logger.error("Failed to find Runes list for player " + pc.getObjectUUID()); + + // next remove any skills that no longer belong + Iterator it = powers.keySet().iterator(); + while (it.hasNext()) { + Integer token = it.next(); + boolean valid = false; + // if (CharacterPower.powerAllowed(token, genericPowers, pc)) + // continue; + if (CharacterPower.powerAllowed(token, race.getPowersGranted(), pc)) + continue; + if (CharacterPower.powerAllowed(token, bc.getPowersGranted(), pc)) + continue; + if (promo != null) + if (CharacterPower.powerAllowed(token, promo.getPowersGranted(), pc)) + continue; + for (CharacterRune rune : runes) { + if (CharacterPower.powerAllowed(token, rune.getPowersGranted(), pc)) { + valid = true; + continue; + } + } + + // if power doesn't belong to any runes or skill, then remove it + if (!valid) { + CharacterPower cp = powers.get(token); + DbManager.CharacterPowerQueries.DELETE_CHARACTER_POWER(cp.getObjectUUID()); + it.remove(); + } + } + } + + /* + * This grants powers for specific runes + */ + private static void grantPowers(ArrayList powersGranted, ConcurrentHashMap powers, PlayerCharacter pc) { + ConcurrentHashMap skills = pc.getSkills(); + + for (PowerReq powerreq : powersGranted) { + PowersBase powersBase = powerreq.getPowersBase(); + + if (powersBase == null) + continue; + // skip if player already has power + if (powers.containsKey(powerreq.getToken())){ + CharacterPower cp = powers.get(powersBase.getToken()); + if (cp != null) + if (cp.requiredLevel == 0) { + cp.requiredLevel = (int) powerreq.getLevel(); + } + + continue; + } + + // If player not high enough level for power, then skip + if (pc.getLevel() < powerreq.getLevel()) + continue; + + // See if any prereq powers needed + boolean valid = true; + ConcurrentHashMap preqs = powerreq.getPowerReqs(); + for (Integer tok : preqs.keySet()) { + if (!powers.containsKey(tok)) + valid = false; + else { + CharacterPower cpp = powers.get(tok); + if ((cpp.getTrains() + cpp.grantedTrains) < preqs.get(tok)) + valid = false; + } + } + if (!valid) + continue; + + // See if any prereq skills needed + preqs = powerreq.getSkillReqs(); + for (Integer tok : preqs.keySet()) { + if (tok == 0) + continue; + CharacterSkill found = null; + for (CharacterSkill sk : skills.values()) { + if (sk.getToken() == tok) { + found = sk; + continue; + } + } + if (found != null) { + if (found.getModifiedAmountBeforeMods() < preqs.get(tok)) + valid = false; + } else + valid = false; + } + if (!valid) + continue; + + + if (!powers.containsKey(powersBase.getToken())) { + CharacterPower newPower = new CharacterPower(powersBase, pc); + CharacterPower cp = null; + try { + cp = DbManager.CharacterPowerQueries.ADD_CHARACTER_POWER(newPower); + } catch (Exception e) { + cp = null; + } + if (cp != null){ + cp.requiredLevel = (int) powerreq.getLevel(); + powers.put(powersBase.getToken(), cp); + } + + else + Logger.error("Failed to add CharacterPower to player " + pc.getObjectUUID()); + }else{ + CharacterPower cp = powers.get(powersBase.getToken()); + if (cp != null) + if (cp.requiredLevel == 0) { + cp.requiredLevel = (int) powerreq.getLevel(); + } + } + } + } + + public static void grantTrains(PlayerCharacter pc) { + if (pc == null) + return; + ConcurrentHashMap powers = pc.getPowers(); + for (CharacterPower cp : powers.values()) { + cp.grantedTrains = cp.getGrantedTrains(pc); + } + } + + /* + * This verifies if a power is valid for a players rune + */ + private static boolean powerAllowed(Integer token, ArrayList powersGranted, PlayerCharacter pc) { + ConcurrentHashMap skills = pc.getSkills(); + ConcurrentHashMap powers = pc.getPowers(); + if (skills == null || powers == null) + return false; + for (PowerReq powerreq : powersGranted) { + PowersBase pb = powerreq.getPowersBase(); + if (pb != null) { + if (pb.getToken() == token) { + + //test level requirements + if (powerreq.getLevel() > pc.getLevel()) { + return false; + } + + //test skill requirements are met + ConcurrentHashMap skillReqs = powerreq.getSkillReqs(); + for (int tok : skillReqs.keySet()) { + boolean valid = false; + if (tok == 0) + continue; + for (CharacterSkill skill : skills.values()) { + if (skill.getToken() == tok) { + if (skill.getModifiedAmountBeforeMods() < skillReqs.get(tok)) + return false; + valid = true; + break; + } + } + if (!valid) + return false; + } + + //test power prerequisites are met + ConcurrentHashMap powerReqs = powerreq.getPowerReqs(); + for (int tok : powerReqs.keySet()) { + if (!powers.containsKey(tok)) + return false; + CharacterPower cp = powers.get(tok); + if (cp.getTotalTrains() < powerReqs.get(tok)) + return false; + } + + //everything passed. power is valid + return true; + } + } + } + return false; + } + + //This verifies the power is not blocked from refining by prereqs on other powers. + private boolean canRefine(PlayerCharacter pc) { + if (this.power == null || pc == null) + return false; + + ConcurrentHashMap powers = pc.getPowers(); + Race race = pc.getRace(); + if (race != null) { + if (!canRefine(race.getPowersGranted(), powers, pc)) + return false; + } else + return false; + BaseClass bc = pc.getBaseClass(); + if (bc != null) { + if (!canRefine(bc.getPowersGranted(), powers, pc)) + return false; + } else + return false; + PromotionClass promo = pc.getPromotionClass(); + if (promo != null) + if (!canRefine(promo.getPowersGranted(), powers, pc)) + return false; + ArrayList runes = pc.getRunes(); + if (runes != null) { + for (CharacterRune rune : runes) { + if (!canRefine(rune.getPowersGranted(), powers, pc)) + return false; + } + } + + //all tests passed. Can refine + return true; + } + + private boolean canRefine(ArrayList powersGranted, ConcurrentHashMap powers, PlayerCharacter pc) { + for (PowerReq pr : powersGranted) { + ConcurrentHashMap powerReqs = pr.getPowerReqs(); + for (int token : powerReqs.keySet()) { + if (token == this.power.getToken()) { + //this is a prereq, find the power and make sure it has enough trains + int trainsReq = (int)powerReqs.get(token); + for (CharacterPower cp : powers.values()) { + if (cp.power.getToken() == pr.getToken()) { + if (this.getTotalTrains() <= trainsReq && cp.getTrains() > 0) { + ErrorPopupMsg.sendErrorMsg(pc, "You must refine " + cp.power.getName() + " to 0 before refining any more from this power."); + return false; + } + } + } + } + } + } + return true; + } + + private boolean canTrain(PlayerCharacter pc) { + if (this.power == null || pc == null) + return false; + int token = this.power.getToken(); + boolean valid = false; + Race race = pc.getRace(); + if (race != null) { + if (CharacterPower.powerAllowed(token, race.getPowersGranted(), pc)) + return true; + } else + return false; + BaseClass bc = pc.getBaseClass(); + if (bc != null) { + if (CharacterPower.powerAllowed(token, bc.getPowersGranted(), pc)) + return true; + } else + return false; + PromotionClass promo = pc.getPromotionClass(); + if (promo != null) + if (CharacterPower.powerAllowed(token, promo.getPowersGranted(), pc)) + return true; + ArrayList runes = pc.getRunes(); + for (CharacterRune rune : runes) + if (CharacterPower.powerAllowed(token, rune.getPowersGranted(), pc)) + return true; + return false; + } + + /* + * Serializing + */ + + public static void serializeForClientMsg(CharacterPower characterPower, ByteBufferWriter writer) { + if (characterPower.power != null) + writer.putInt(characterPower.power.getToken()); + else + writer.putInt(0); + writer.putInt(characterPower.getTrains()); + } + + public static CharacterPower getPower(int tableId) { + return DbManager.CharacterPowerQueries.GET_CHARACTER_POWER(tableId); + } + + @Override + public void updateDatabase() { + DbManager.CharacterPowerQueries.updateDatabase(this); + } + + public int getRequiredLevel() { + return requiredLevel; + } + + public void setRequiredLevel(int requiredLevel) { + this.requiredLevel = requiredLevel; + } +} diff --git a/src/engine/objects/CharacterRune.java b/src/engine/objects/CharacterRune.java new file mode 100644 index 00000000..0eb40073 --- /dev/null +++ b/src/engine/objects/CharacterRune.java @@ -0,0 +1,211 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum.DispatchChannel; +import engine.gameManager.DbManager; +import engine.net.ByteBufferWriter; +import engine.net.DispatchMessage; +import engine.net.client.msg.ApplyRuneMsg; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; + +public class CharacterRune extends AbstractGameObject { + + private final RuneBase runeBase; + private final int player; + private final ArrayList skillsGranted; + private final ArrayList powersGranted; + private final ArrayList effectsGranted; + + /** + * No Table ID Constructor + */ + public CharacterRune(RuneBase runeBase, int characterID) { + super(); + this.runeBase = runeBase; + this.player = characterID; + if (this.runeBase != null) { + this.skillsGranted = DbManager.SkillReqQueries.GET_REQS_FOR_RUNE(this.runeBase.getObjectUUID()); + this.powersGranted = PowerReq.getPowerReqsForRune(this.runeBase.getObjectUUID()); + } else { + this.skillsGranted = new ArrayList<>(); + this.powersGranted = new ArrayList<>(); + } + if (this.runeBase != null) + this.effectsGranted = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE(this.runeBase.getObjectUUID()); + else + this.effectsGranted = new ArrayList<>(); + } + + /** + * Normal Constructor + */ + public CharacterRune(RuneBase runeBase, int characterID, int newUUID) { + super(newUUID); + this.runeBase = runeBase; + this.player = characterID; + if (this.runeBase != null) { + this.skillsGranted = DbManager.SkillReqQueries.GET_REQS_FOR_RUNE(this.runeBase.getObjectUUID()); + this.powersGranted = PowerReq.getPowerReqsForRune(this.runeBase.getObjectUUID()); + } else { + this.skillsGranted = new ArrayList<>(); + this.powersGranted = new ArrayList<>(); + } + if (this.runeBase != null) + this.effectsGranted = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE(this.runeBase.getObjectUUID()); + else + this.effectsGranted = new ArrayList<>(); + } + + /** + * ResultSet Constructor + */ + public CharacterRune(ResultSet rs) throws SQLException { + super(rs); + + this.runeBase = RuneBase.getRuneBase(rs.getInt("RuneBaseID")); + this.player = rs.getInt("CharacterID"); + if (this.runeBase != null) { + this.skillsGranted = DbManager.SkillReqQueries.GET_REQS_FOR_RUNE(this.runeBase.getObjectUUID()); + this.powersGranted = PowerReq.getPowerReqsForRune(this.runeBase.getObjectUUID()); + this.effectsGranted = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE(this.runeBase.getObjectUUID()); + } else { + Logger.error("Failed to find RuneBase for CharacterRune " + this.getObjectUUID()); + this.skillsGranted = new ArrayList<>(); + this.powersGranted = new ArrayList<>(); + this.effectsGranted = new ArrayList<>(); + } + } + + /* + * Getters + */ + public RuneBase getRuneBase() { + return this.runeBase; + } + + public int getRuneBaseID() { + if (this.runeBase != null) + return this.runeBase.getObjectUUID(); + return 0; + } + + public int getPlayerID() { + return this.player; + } + + public ArrayList getSkillsGranted() { + return this.skillsGranted; + } + + public ArrayList getPowersGranted() { + return this.powersGranted; + } + + public ArrayList getEffectsGranted() { + return this.effectsGranted; + } + + /* + * Serializing + */ + + public static void serializeForClientMsg(CharacterRune characterRune, ByteBufferWriter writer) { + if (characterRune.runeBase != null) { + int idd = characterRune.runeBase.getObjectUUID(); + if (idd > 3000 && idd < 3050) + writer.putInt(4); + else + writer.putInt(5); + // writer.putInt(this.runeBase.getMessageType()); + writer.putInt(0); + writer.putInt(characterRune.runeBase.getObjectUUID()); + writer.putInt(characterRune.getObjectType().ordinal()); + writer.putInt(characterRune.getObjectUUID()); + } else { + for (int i = 0; i < 5; i++) + writer.putInt(0); + } + } + + public static boolean grantRune(PlayerCharacter pc, int runeID) { + //Verify not too many runes + ArrayList runes = pc.getRunes(); + boolean worked = false; + synchronized (runes) { + if (runes == null || runes.size() > 12) + return false; + + //Verify player doesn't already have rune + for (CharacterRune rune : runes) { + RuneBase rb = rune.runeBase; + if (rb == null || rb.getObjectUUID() == runeID) + return false; + } + + RuneBase rb = RuneBase.getRuneBase(runeID); + if (rb == null) + return false; + + //Attempt to add rune to database + CharacterRune toAdd = new CharacterRune(rb, pc.getObjectUUID()); + CharacterRune rune = null; + try { + rune = DbManager.CharacterRuneQueries.ADD_CHARACTER_RUNE(toAdd); + } catch (Exception e) { + return false; + } + if (rune == null) + return false; + + //attempt add rune to player + worked = pc.addRune(rune); + + //worked, send ApplyRuneMsg + if (worked) { + ApplyRuneMsg arm = new ApplyRuneMsg(pc.getObjectType().ordinal(), pc.getObjectUUID(), runeID, rune.getObjectType().ordinal(), rune.getObjectUUID(), true); + DispatchMessage.dispatchMsgToInterestArea(pc, arm, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + CharacterSkill.calculateSkills(pc); + pc.applyBonuses(); + return true; + } else + return false; + } + + } + + public static boolean removeRune(PlayerCharacter pc, int runeID) { + ArrayList runes = pc.getRunes(); + synchronized (runes) { + for (CharacterRune rune : runes) { + RuneBase rb = rune.runeBase; + if (rb == null) + continue; + if (rb.getObjectUUID() == runeID && DbManager.CharacterRuneQueries.DELETE_CHARACTER_RUNE(rune)) { + runes.remove(runes.indexOf(rune)); + CharacterSkill.calculateSkills(pc); + pc.applyBonuses(); + return true; + } + } + return false; + } + } + + @Override + public void updateDatabase() { + DbManager.CharacterRuneQueries.updateDatabase(this); + } +} diff --git a/src/engine/objects/CharacterSkill.java b/src/engine/objects/CharacterSkill.java new file mode 100644 index 00000000..e2179392 --- /dev/null +++ b/src/engine/objects/CharacterSkill.java @@ -0,0 +1,1248 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.objects; + +import engine.Enum; +import engine.Enum.CharacterSkills; +import engine.Enum.ModType; +import engine.Enum.SourceType; +import engine.gameManager.DbManager; +import engine.net.ByteBufferWriter; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +public class CharacterSkill extends AbstractGameObject { + + private static final int[] maxTrains = { + 29, 29, 29, 29, 29, //0 to 4 + 29, 32, 34, 36, 38, //5 to 9 + 40, 42, 43, 45, 47, //10 to 14 + 48, 49, 51, 52, 53, //15 to 19 + 55, 56, 57, 58, 59, //20 to 24 + 60, 62, 63, 64, 65, //25 to 29 + 66, 67, 68, 68, 69, //30 to 34 + 70, 71, 72, 73, 74, //35 to 39 + 75, 76, 76, 77, 78, //40 to 44 + 79, 80, 80, 81, 82, //45 to 49 + 83, 83, 84, 85, 85, //50 to 54 + 86, 87, 88, 88, 89, //55 to 59 + 90, 90, 91, 92, 92, //60 to 64 + 93, 94, 94, 95, 95, //65 to 69 + 96, 97, 97, 98, 99, //70 to 74 + 99, 100, 100, 101, 101, //75 to 79 + 102, 103, 103, 104, 104, //80 to 84 + 105, 105, 106, 106, 107, //85 to 89 + 108, 109, 109, 110, 110, //90 to 94 + 111, 112, 112, 113, 113, //95 to 99 + 114, 115, 115, 116, 116, //100 to 104 + 117, 118, 118, 119, 119, //105 to 109 + 120, 121, 121, 122, 122, //110 to 114 + 123, 124, 124, 125, 125, //115 to 119 + 126, 127, 127, 128, 128, //120 to 124 + 129, 130, 130, 131, 131, //125 to 129 + 132, 133, 133, 134, 134, //130 to 134 + 135, 136, 136, 137, 137, //135 to 139 + 138, 139, 139, 140, 140, //140 to 144 + 141, 142, 142, 143, 143, //145 to 149 + 144, 145, 145, 146, 146, //150 to 154 + 147, 148, 148, 149, 149, //155 to 159 + 150, 151, 151, 152, 152, //160 to 164 + 153, 154, 154, 155, 155, //165 to 169 + 156, 157, 157, 158, 158, //170 to 174 + 159, 160, 160, 161, 161, //175 to 179 + 162, 163, 163, 164, 164, //180 to 184 + 165, 166, 166, 167, 167, //185 to 189 + 168}; //190 + + private static final float[] baseSkillValues = { + 0.0f, 0.0f, 0.2f, 0.4f, 0.6f, //0 to 4 + 0.8f, 1.0f, 1.1666666f, 1.3333334f, 1.5f, //5 to 9 + 1.6666667f, 1.8333334f, 2.0f, 2.2f, 2.4f, //10 to 14 + 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, //15 to 19 + 3.6f, 3.8f, 4.0f, 4.2f, 4.4f, //20 to 24 + 4.6f, 4.8f, 5.0f, 5.25f, 5.5f, //25 to 29 + 5.75f, 6.0f, 6.2f, 6.4f, 6.6f, //30 to 34 + 6.8f, 7.0f, 7.25f, 7.5f, 7.75f, //35 to 39 + 8.0f, 8.2f, 8.4f, 8.6f, 8.8f, //40 to 44 + 9.0f, 9.25f, 9.5f, 9.75f, 10.0f, //45 to 49 + 10.25f, 10.5f, 10.75f, 11.0f, 11.2f, //50 to 54 + 11.4f, 11.6f, 11.8f, 12.0f, 12.25f, //55 to 59 + 12.5f, 12.75f, 13.0f, 13.25f, 13.5f, //60 to 64 + 13.75f, 14.0f, 14.25f, 14.5f, 14.75f, //65 to 69 + 15.0f, 15.333333f, 15.666667f, 16.0f, 16.25f, //70 to 74 + 16.5f, 16.75f, 17.0f, 17.25f, 17.5f, //75 to 79 + 17.75f, 18.0f, 18.25f, 18.5f, 18.75f, //80 to 84 + 19.0f, 19.333334f, 19.666666f, 20.0f, 20.25f, //85 to 89 + 20.5f, 20.75f, 21.0f, 21.25f, 21.5f, //90 to 94 + 21.75f, 22.0f, 22.333334f, 22.666666f, 23.0f, //95 to 99 + 23.25f, 23.5f, 23.75f, 24.0f, 24.333334f, //100 to 104 + 24.666666f, 25.0f, 25.25f, 25.5f, 25.75f, //105 to 109 + 26.0f, 26.333334f, 26.666666f, 27.0f, 27.333334f, //110 to 114 + 27.666666f, 28.0f, 28.25f, 28.5f, 28.75f, //115 to 119 + 29.0f, 29.333334f, 29.666666f, 30.0f, 30.333334f, //120 to 124 + 30.666666f, 31.0f, 31.25f, 31.5f, 31.75f, //125 to 129 + 32.0f, 32.333332f, 32.666668f, 33.0f, 33.333332f, //130 to 134 + 33.666668f, 34.0f, 34.333332f, 34.666668f, 35.0f, //135 to 139 + 35.333332f, 35.666668f, 36.0f, 36.333332f, 36.666668f, //140 to 144 + 37.0f, 37.25f, 37.5f, 37.75f, 38.0f, //145 to 149 + 38.333332f, 38.666668f, 39.0f, 39.333332f, 39.666668f, //150 to 154 + 40.0f, 40.333332f, 40.666668f, 41.0f, 41.333332f, //155 to 159 + 41.666668f, 42.0f, 42.333332f, 42.666668f, 43.0f, //160 to 164 + 43.333332f, 43.666668f, 44.0f, 44.333332f, 44.666668f, //165 to 169 + 45.0f, 45.5f, 46.0f, 46.333332f, 46.666668f, //170 to 174 + 47.0f, 47.333332f, 47.666668f, 48.0f, 48.333332f, //175 to 179 + 48.666668f, 49.0f, 49.333332f, 49.666668f, 50.0f, //180 to 184 + 50.333332f, 50.666668f, 51.0f, 51.333332f, 51.666668f, //185 to 189 + 52.0f, 52.5f, 53.0f, 53.333332f, 53.666668f, //190 to 194 + 54.0f, 54.333332f, 54.666668f, 55.0f, 55.333332f, //195 to 199 + 55.666668f, 56.0f, 56.333332f, 56.666668f, 57.0f, //200 to 204 + 57.5f, 58.0f, 58.333332f, 58.666668f, 59.0f, //205 to 209 + 59.333332f, 59.666668f, 60.0f, 60.5f, 61.0f, //210 to 214 + 61.333332f, 61.666668f, 62.0f, 62.333332f, 62.666668f, //215 to 219 + 63.0f, 63.5f, 64.0f, 64.333336f, 64.666664f, //220 to 224 + 65.0f, 65.333336f, 65.666664f, 66.0f, 66.5f, //225 to 229 + 67.0f, 67.333336f, 67.666664f, 68.0f, 68.5f, //230 to 234 + 69.0f, 69.333336f, 69.666664f, 70.0f, 70.333336f, //235 to 239 + 70.666664f, 71.0f, 71.5f, 72.0f, 72.5f, //240 to 244 + 73.0f, 73.333336f, 73.666664f, 74.0f, 74.333336f, //245 to 249 + 74.666664f, 75.0f, 75.5f, 76.0f, 76.333336f, //250 to 254 + 76.666664f, 77.0f, 77.5f, 78.0f, 78.333336f, //255 to 259 + 78.666664f, 79.0f, 79.5f, 80.0f, 80.333336f, //260 to 264 + 80.666664f, 81.0f, 81.5f, 82.0f, 82.333336f, //265 to 269 + 82.666664f, 83.0f, 83.5f, 84.0f, 84.333336f, //270 to 274 + 84.666664f, 85.0f, 85.5f, 86.0f, 86.5f, //275 to 279 + 87.0f, 87.333336f, 87.666664f, 88.0f, 88.5f, //280 to 284 + 89.0f, 89.333336f, 89.666664f, 90.0f, 90.5f, //285 to 289 + 91.0f, 91.5f, 92.0f, 92.333336f, 92.666664f, //290 to 294 + 93.0f, 93.5f, 94.0f, 94.5f, 95.0f, //295 to 299 + 95.333336f, 95.666664f, 96.0f, 96.5f, 97.0f, //300 to 304 + 97.5f, 98.0f, 98.333336f, 98.666664f, 99.0f, //305 to 309 + 99.5f, 100.0f, 100.5f, 101.0f, 101.5f, //310 to 314 + 102.0f, 102.5f, 103.0f, 103.333336f, 103.666664f, //315 to 319 + 104.0f, 104.333336f, 104.666664f, 105.0f, 105.5f, //320 to 324 + 106.0f, 106.5f, 107.0f, 108.0f, 108.333336f, //325 to 329 + 108.666664f, 109.0f, 109.333336f, 109.666664f, 110.0f, //330 to 334 + 110.5f, 111.0f, 111.5f, 112.0f, 112.5f, //335 to 339 + 113.0f, 113.333336f, 113.666664f, 114.0f, 114.5f, //340 to 344 + 115.0f, 115.5f, 116.0f, 116.5f, 117.0f, //345 to 349 + 117.5f, 118.0f, 118.333336f, 118.666664f, 119.0f, //350 to 354 + 119.5f, 120.0f, 120.5f, 121.0f, 121.5f, //355 to 359 + 122.0f, 122.5f, 123.0f, 123.333336f, 123.666664f, //360 to 364 + 124.0f, 124.5f, 125.0f, 125.5f, 126.0f, //365 to 369 + 126.5f, 127.0f, 127.5f, 128.0f, 128.5f, //370 to 374 + 129.0f, 129.5f, 130.0f, 130.33333f, 130.66667f, //375 to 379 + 131.0f, 131.5f, 132.0f, 132.5f, 133.0f, //380 to 384 + 133.5f, 134.0f, 134.5f, 135.0f, 135.5f, //385 to 389 + 136.0f, 136.5f, 137.0f, 137.5f, 138.0f, //390 to 394 + 138.5f, 139.0f, 139.5f, 140.0f, 140.33333f, //395 to 399 + 140.66667f, 141.0f, 141.5f, 142.0f, 142.5f, //400 to 404 + 143.0f, 143.5f, 144.0f, 144.5f, 145.0f, //405 to 409 + 145.5f, 146.0f, 146.5f, 147.0f, 147.5f, //410 to 414 + 148.0f, 148.5f, 149.0f, 149.5f, 150.0f, //415 to 419 + 150.5f, 151.0f, 151.5f, 152.0f, 152.5f, //420 to 424 + 153.0f, 153.5f, 154.0f, 154.5f, 155.0f, //425 to 429 + 155.5f, 156.0f, 156.5f, 157.0f, 157.5f, //430 to 434 + 158.0f, 158.5f, 159.0f, 159.5f, 160.0f, //435 to 439 + 160.5f, 161.0f, 161.5f, 162.0f, 162.5f, //440 to 444 + 163.0f, 163.5f, 164.0f, 164.5f, 165.0f, //445 to 449 + 165.5f, 166.0f, 166.5f, 167.0f, 167.5f, //450 to 454 + 168.0f, 168.5f, 169.0f, 169.5f, 170.0f, //455 to 459 + 170.5f, 171.0f, 171.5f, 172.0f, 172.5f, //460 to 464 + 173.0f, 173.5f, 174.0f, 174.5f, 175.0f, //465 to 469 + 176.0f, 176.5f, 177.0f, 177.5f, 178.0f, //470 to 474 + 178.5f, 179.0f, 179.5f, 180.0f, 180.5f, //475 to 479 + 181.0f, 181.5f, 182.0f, 182.5f, 183.0f, //480 to 484 + 183.5f, 184.0f, 184.5f, 185.0f, 185.5f, //485 to 489 + 186.0f, 187.0f, 187.5f, 188.0f, 188.5f, //490 to 494 + 189.0f, 189.5f, 190.0f, 190.5f, 191.0f, //495 to 499 + 191.5f, 192.0f, 192.5f, 193.0f, 193.5f, //500 to 504 + 194.0f, 194.5f, 195.0f, 196.0f, 196.5f, //505 to 509 + 197.0f, 197.5f, 198.0f, 198.5f, 199.0f, //510 to 514 + 199.5f, 200.0f, 200.5f, 201.0f, 201.5f, //515 to 519 + 202.0f, 203.0f, 203.5f, 204.0f, 204.5f, //520 to 524 + 205.0f, 205.5f, 206.0f, 206.5f, 207.0f, //525 to 529 + 207.5f, 208.0f, 209.0f, 209.5f, 210.0f, //530 to 534 + 210.5f, 211.0f, 211.5f, 212.0f, 212.5f, //535 to 539 + 213.0f, 214.0f, 214.5f, 215.0f, 215.5f, //540 to 544 + 216.0f, 216.5f, 217.0f, 217.5f, 218.0f, //545 to 549 + 218.5f, 219.0f, 220.0f, 220.5f, 221.0f, //550 to 554 + 221.5f, 222.0f, 222.5f, 223.0f, 224.0f, //555 to 559 + 224.5f, 225.0f, 225.5f, 226.0f, 226.5f, //560 to 564 + 227.0f, 227.5f, 228.0f, 229.0f, 229.5f, //565 to 569 + 230.0f, 230.5f, 231.0f, 231.5f, 232.0f, //570 to 574 + 233.0f, 233.5f, 234.0f, 234.5f, 235.0f, //575 to 579 + 235.5f, 236.0f, 237.0f, 237.5f, 238.0f, //580 to 584 + 238.5f, 239.0f, 239.5f, 240.0f, 241.0f, //585 to 589 + 241.5f, 242.0f, 242.5f, 243.0f, 243.5f, //590 to 594 + 244.0f, 245.0f, 245.5f, 246.0f, 246.5f, //595 to 599 + 247.0f}; //600 + + + private SkillsBase skillsBase; + private AtomicInteger numTrains = new AtomicInteger(); + + private CharacterSkills skillType; + + //Skill% before trains and before any effects or item bonuses + private float baseAmountBeforeMods; + + //Skill% after trains but before any effects or item bonuses + private float modifiedAmountBeforeMods; + private boolean isMobOwner = false; + + //Skill% before trains but after any effects or item bonuses + private float baseAmount; + + //Skill% after trains and after any effects or item bonuses + private float modifiedAmount; + + private int ownerUID; + private boolean trained = false; + private int requiredLevel = 0; + + /** + * No Table ID Constructor + */ + public CharacterSkill(SkillsBase skillsBase, PlayerCharacter pc) { + super(); + this.skillsBase = skillsBase; + this.numTrains.set(0); + this.ownerUID = pc.getObjectUUID(); + calculateBaseAmount(); + calculateModifiedAmount(); + this.skillType = CharacterSkills.GetCharacterSkillByToken(this.skillsBase.getToken()); + } + + /** + * Normal Constructor + */ + public CharacterSkill(SkillsBase skillsBase, PlayerCharacter pc, int newUUID) { + + super(newUUID); + this.skillsBase = skillsBase; + this.numTrains.set(0); + this.ownerUID = pc.getObjectUUID(); + this.trained = true; + calculateBaseAmount(); + calculateModifiedAmount(); + this.skillType = CharacterSkills.GetCharacterSkillByToken(this.skillsBase.getToken()); + + } + + /** + * ResultSet Constructor + */ + public CharacterSkill(ResultSet rs, PlayerCharacter pc) throws SQLException { + super(rs); + + int skillsBaseID = rs.getInt("SkillsBaseID"); + this.skillsBase = DbManager.SkillsBaseQueries.GET_BASE(skillsBaseID); + this.numTrains.set(rs.getShort("trains")); + this.ownerUID = pc.getObjectUUID(); + calculateBaseAmount(); + calculateModifiedAmount(); + this.skillType = CharacterSkills.GetCharacterSkillByToken(this.skillsBase.getToken()); + } + + public CharacterSkill(SkillsBase sb, Mob mob, int trains) { + super(); + this.skillsBase = sb; + this.numTrains.set(trains); + this.ownerUID = mob.getObjectUUID(); + this.isMobOwner = true; + calculateMobBaseAmount(); + calculateModifiedAmount(); + this.skillType = CharacterSkills.GetCharacterSkillByToken(this.skillsBase.getToken()); + } + + public CharacterSkill(ResultSet rs) throws SQLException { + super(rs); + int skillsBaseID = rs.getInt("SkillsBaseID"); + this.skillsBase = DbManager.SkillsBaseQueries.GET_BASE(skillsBaseID); + this.numTrains.set(rs.getShort("trains")); + this.ownerUID = rs.getInt("CharacterID"); + // this.owner = DbManager.PlayerCharacterQueries.GET_PLAYER_CHARACTER(rs.getInt("CharacterID")); + calculateBaseAmount(); + calculateModifiedAmount(); + this.skillType = CharacterSkills.GetCharacterSkillByToken(this.skillsBase.getToken()); + } + + public static AbstractCharacter GetOwner(CharacterSkill cs){ + if (cs.ownerUID == 0) + return null; + if (cs.isMobOwner) + return Mob.getFromCache(cs.ownerUID); + else + return PlayerCharacter.getFromCache(cs.ownerUID); + } + + /* + * Getters + */ + + public static float getATR(AbstractCharacter ac, String name) { + if (ac == null) + return 0f; + float atr; + ConcurrentHashMap skills = ac.getSkills(); + CharacterSkill skill = skills.get(name); + if (skill != null) + atr = skill.getATR(ac); + else { + float mast = CharacterSkill.getQuickMastery(ac, name); + atr = (((int)mast * 7) + (ac.getStatDexCurrent() / 2)); + } + //apply effect mods + PlayerBonuses bonus = ac.getBonuses(); + if (bonus == null) + return atr; + atr += bonus.getFloat(ModType.OCV, SourceType.None); + float pos_Bonus = bonus.getFloatPercentPositive(ModType.OCV, SourceType.None); + atr *= (1 + pos_Bonus); + //rUNES will already be applied + // atr *= (1 + ((float)bonus.getShort("rune.Attack") / 100)); //precise + float neg_Bonus = bonus.getFloatPercentNegative(ModType.OCV, SourceType.None); + atr *= (1 +neg_Bonus); + return atr; + } + + private float getATR(AbstractCharacter ac) { + return (((int)this.modifiedAmount * 7) + (ac.getStatDexCurrent() / 2)); + } + + public synchronized boolean train(PlayerCharacter pc) { + if (pc == null || this.skillsBase == null) + return false; + + boolean running = false; + + //trying out a table lookup + int intt = (int) pc.statIntBase; + int maxTrains = 0; + if (intt > 0 && intt < 191) + maxTrains = CharacterSkill.maxTrains[intt]; + else + maxTrains = (int)(33 + 1.25 * (int) pc.statIntBase - 0.005 * Math.pow((int) pc.statIntBase, 2)); + + + int oldTrains = this.numTrains.get(); + boolean succeeded = true; + if (pc.getTrainsAvailable() <= 0) + return false; + if (oldTrains == maxTrains) //at gold, stop here + return false; + else if (oldTrains > maxTrains) //catch incase we somehow go over + this.numTrains.set(maxTrains); + else //add the train + succeeded = this.numTrains.compareAndSet(oldTrains, oldTrains+1); + + if (this.numTrains.get() > maxTrains) { //double check not over max trains + this.numTrains.set(maxTrains); + succeeded = false; + } + + if (succeeded) { + this.trained = true; + + //subtract from trains available + pc.modifyTrainsAvailable(-1); + + //update database + pc.addDatabaseJob("Skills", MBServerStatics.THIRTY_SECONDS); + + //recalculate this skill + calculateBaseAmount(); + calculateModifiedAmount(); + + //see if any new skills or powers granted + pc.calculateSkills(); + + //reapply all bonuses + pc.applyBonuses(); + + //update cache if running trains change + if (running) + pc.incVer(); + + return true; + } else + return false; + } + + public float getTrainingCost(PlayerCharacter pc, NPC trainer){ + int charLevel = pc.getLevel(); + int skillRank = this.getNumTrains() - 1 + this.requiredLevel; + float baseCost = 15 * this.requiredLevel; //TODO GET BASE COSTS OF SKILLS. + + + + float sellPercent = -.20f; + float cost; + float const5; + int const2 = 1; + float const3 = 50; + float const4 = const3 + const2; + + if (charLevel > 50) + const5 = 50 / const4; + else + const5 = charLevel/const4; + + const5 = 1-const5; + const5 = (float) (Math.log(const5) / Math.log(2) * .75f); + float rounded5 = Math.round(const5); + const5 = rounded5 - const5; + + const5 *= -1; + + const5 = (float) (Math.pow(2, const5) - 1); + + const5 +=1; + const5 = Math.scalb(const5, (int) rounded5); + const5 *= (charLevel - skillRank); + const5 *= sellPercent; + const5 = (float) (Math.log(const5) / Math.log(2) * 3); + rounded5 = Math.round(const5); + const5 = rounded5 - const5; + const5 *= -1; + const5 = (float) (Math.pow(2, const5) - 1); + const5 +=1; + + + const5 = Math.scalb(const5, (int) rounded5); + const5 += 1; + cost = const5 * (baseCost); + + + if (Float.isNaN(cost)) + cost = baseCost; + return cost; + } + + //Call this to refine skills and recalculate everything for pc. + public boolean refine(PlayerCharacter pc) { + return refine(pc, true); + } + + public boolean refine(PlayerCharacter pc, boolean recalculate) { + if (pc == null || this.skillsBase == null) + return false; + + int oldTrains = this.numTrains.get(); + boolean succeeded = true; + if (this.getNumTrains() < 1) + return false; + succeeded = this.numTrains.compareAndSet(oldTrains, oldTrains-1); + if (succeeded) { + this.trained = true; + + //add to trains available + pc.modifyTrainsAvailable(1); + + //update database + pc.addDatabaseJob("Skills", MBServerStatics.THIRTY_SECONDS); + + if (recalculate) { + //recalculate this skill + calculateBaseAmount(); + calculateModifiedAmount(); + + //see if any skills or powers removed + pc.calculateSkills(); + + //reapply all bonuses + pc.applyBonuses(); + } + + return true; + } else + return false; + } + + + public boolean reset(PlayerCharacter pc, boolean recalculate) { + if (pc == null || this.skillsBase == null) + return false; + + int oldTrains = this.numTrains.get(); + boolean succeeded = true; + if (this.getNumTrains() < 1) + return false; + succeeded = this.numTrains.compareAndSet(oldTrains, 0); + if (succeeded) { + this.trained = true; + + //add to trains available + pc.modifyTrainsAvailable(oldTrains); + + //update database + pc.addDatabaseJob("Skills", MBServerStatics.THIRTY_SECONDS); + + if (recalculate) { + //recalculate this skill + calculateBaseAmount(); + calculateModifiedAmount(); + + //see if any skills or powers removed + pc.calculateSkills(); + + //reapply all bonuses + pc.applyBonuses(); + } + + return true; + } else + return false; + } + + + /* + * Returns Skill Base for skill + */ + public SkillsBase getSkillsBase() { + return this.skillsBase; + } + + /* + * Returns number of trains in skill + */ + public int getNumTrains() { + return this.numTrains.get(); + } + + + + /* + * Returns Skill% before trains added + */ + public float getBaseAmount() { + return this.baseAmount; + } + + /* + * Returns Skill% after trains added + */ + public float getModifiedAmount() { + return this.modifiedAmount; + } + + /* + * Returns Skill% before trains added, minus bonus from equip and effects + */ + public float getBaseAmountBeforeMods() { + return this.baseAmountBeforeMods; + } + + /* + * Returns Skill% after trains added, minus bonus from equip and effects + */ + public float getModifiedAmountBeforeMods() { + return this.modifiedAmountBeforeMods; + } + + public String getName() { + if (this.skillsBase != null) + return this.skillsBase.getName(); + return ""; + } + + public int getToken() { + if (this.skillsBase != null) + return this.skillsBase.getToken(); + return 0; + } + + public boolean isTrained() { + return trained; + } + + public void syncTrains() { + this.trained = false; + } + + /* + * Serializing + */ + + public static void serializeForClientMsg(CharacterSkill characterSkill, ByteBufferWriter writer) { + if (characterSkill.skillsBase == null) { + Logger.error( "SkillsBase not found for skill " + characterSkill.getObjectUUID()); + writer.putInt(0); + writer.putInt(0); + } else { + writer.putInt(characterSkill.skillsBase.getToken()); + writer.putInt(characterSkill.numTrains.get()); + } + } + + /** + * @ This updates all Base Skill Amouts for a player + * Convienence method + */ + public static void updateAllBaseAmounts(PlayerCharacter pc) { + if (pc == null) + return; + ConcurrentHashMap skills = pc.getSkills(); + Iterator it = skills.keySet().iterator(); + while(it.hasNext()) { + String name = it.next(); + CharacterSkill cs = skills.get(name); + if (cs != null) + cs.calculateBaseAmount(); + // Logger.info("CharacterSkill", pc.getName() + ", skill: " + + // cs.getSkillsBase().getName() + ", trains: " + cs.numTrains + + // ", base: " + cs.baseAmount + ", mod: " + cs.modifiedAmount); + } + + //Recalculate ATR, damage and defense + //pc.calculateAtrDefenseDamage(); + + //recalculate movement bonus + //pc.calculateSpeedMod(); + } + + /** + * @ This updates all Modified skill Amounts for a player + * Convienence method + */ + public static void updateAllModifiedAmounts(PlayerCharacter pc) { + if (pc == null) + return; + ConcurrentHashMap skills = pc.getSkills(); + Iterator it = skills.keySet().iterator(); + while(it.hasNext()) { + String name = it.next(); + CharacterSkill cs = skills.get(name); + if (cs != null) + cs.calculateModifiedAmount(); + + } + + //Recalculate ATR, damage and defense + //pc.calculateAtrDefenseDamage(); + } + + /** + * @ Calculates Base Skill Percentage + * Call this when stats change for a player + */ + public void calculateBaseAmount() { + if (CharacterSkill.GetOwner(this) == null) { + Logger.error("owner not found for owner uuid : " + this.ownerUID); + this.baseAmount = 1; + this.modifiedAmount = 1; + return; + } + + if (this.skillsBase == null) { + Logger.error("SkillsBase not found for skill " + this.getObjectUUID()); + this.baseAmount = 1; + this.modifiedAmount = 1; + return; + } + + //Get any rune bonus + float bonus = 0f; + //runes will already be calculated + if (CharacterSkill.GetOwner(this).getBonuses() != null) { + //Get bonuses from runes + bonus = CharacterSkill.GetOwner(this).getBonuses().getSkillBonus(this.skillsBase.sourceType); + } + + //Get Base skill for unmodified stats + float base = 7f; + float statMod = 0.5f; + if (this.skillsBase.getStrMod() > 0) + statMod += (float)this.skillsBase.getStrMod() * (float) (int) ((PlayerCharacter) CharacterSkill.GetOwner(this)).statStrBase / 100f; + if (this.skillsBase.getDexMod() > 0) + statMod += (float)this.skillsBase.getDexMod() * (float) (int) ((PlayerCharacter) CharacterSkill.GetOwner(this)).statDexBase / 100f; + if (this.skillsBase.getConMod() > 0) + statMod += (float)this.skillsBase.getConMod() * (float) (int) ((PlayerCharacter) CharacterSkill.GetOwner(this)).statConBase / 100f; + if (this.skillsBase.getIntMod() > 0) + statMod += (float)this.skillsBase.getIntMod() * (float) (int) ((PlayerCharacter) CharacterSkill.GetOwner(this)).statIntBase / 100f; + if (this.skillsBase.getSpiMod() > 0) + statMod += (float)this.skillsBase.getSpiMod() * (float) (int) ((PlayerCharacter) CharacterSkill.GetOwner(this)).statSpiBase / 100f; + if (statMod < 1) + statMod = 1f; + else if (statMod > 600) + statMod = 600f; + base += CharacterSkill.baseSkillValues[(int)statMod]; + + if (base + bonus < 1f) + this.baseAmountBeforeMods = 1f; + else + this.baseAmountBeforeMods = base + bonus; + this.modifiedAmountBeforeMods = Math.round(this.baseAmountBeforeMods + calculateAmountAfterTrains()); + } + + public void calculateMobBaseAmount() { + if (CharacterSkill.GetOwner(this) == null) { + Logger.error("owner not found for owner uuid : " + this.ownerUID); + this.baseAmount = 1; + this.modifiedAmount = 1; + return; + } + + if (this.skillsBase == null) { + Logger.error("SkillsBase not found for skill " + this.getObjectUUID()); + this.baseAmount = 1; + this.modifiedAmount = 1; + return; + } + + //Get any rune bonus + float bonus = 0f; + //TODO SKILLS RUNES + + if (CharacterSkill.GetOwner(this).getBonuses() != null) { + //Get bonuses from runes + bonus = CharacterSkill.GetOwner(this).getBonuses().getSkillBonus(this.skillsBase.sourceType); + } + + //Get Base skill for unmodified stats + float base = 7f; + float statMod = 0.5f; + if (this.skillsBase.getStrMod() > 0) + statMod += (float)this.skillsBase.getStrMod() * (float)((Mob)CharacterSkill.GetOwner(this)).getMobBase().getMobBaseStats().getBaseStr() / 100f; + if (this.skillsBase.getDexMod() > 0) + statMod += (float)this.skillsBase.getDexMod() * (float)((Mob)CharacterSkill.GetOwner(this)).getMobBase().getMobBaseStats().getBaseDex() / 100f; + if (this.skillsBase.getConMod() > 0) + statMod += (float)this.skillsBase.getConMod() * (float)((Mob)CharacterSkill.GetOwner(this)).getMobBase().getMobBaseStats().getBaseCon() / 100f; + if (this.skillsBase.getIntMod() > 0) + statMod += (float)this.skillsBase.getIntMod() * (float)((Mob)CharacterSkill.GetOwner(this)).getMobBase().getMobBaseStats().getBaseInt() / 100f; + if (this.skillsBase.getSpiMod() > 0) + statMod += (float)this.skillsBase.getSpiMod() * (float)((Mob)CharacterSkill.GetOwner(this)).getMobBase().getMobBaseStats().getBaseSpi() / 100f; + if (statMod < 1) + statMod = 1f; + else if (statMod > 600) + statMod = 600f; + base += CharacterSkill.baseSkillValues[(int)statMod]; + + if (base + bonus < 1f) + this.baseAmountBeforeMods = 1f; + else + this.baseAmountBeforeMods = base + bonus; + this.modifiedAmountBeforeMods = (int)(this.baseAmountBeforeMods + calculateAmountAfterTrains()); + } + + public void calculateModifiedAmount() { + if (CharacterSkill.GetOwner(this) == null || this.skillsBase == null) { + Logger.error( "owner or SkillsBase not found for skill " + this.getObjectUUID()); + this.baseAmount = 1; + this.modifiedAmount = 1; + return; + } + + //Get any rune bonus + float bonus = 0f; + if (CharacterSkill.GetOwner(this).getBonuses() != null) { + //Get bonuses from runes + bonus = CharacterSkill.GetOwner(this).getBonuses().getSkillBonus(this.skillsBase.sourceType); + } + + //Get Base skill for modified stats + //TODO this fomula needs verified + float base = 7f; + float statMod = 0.5f; + if (this.skillsBase.getStrMod() > 0) + statMod += (float)this.skillsBase.getStrMod() * (float)CharacterSkill.GetOwner(this).getStatStrCurrent() / 100f; + if (this.skillsBase.getDexMod() > 0) + statMod += (float)this.skillsBase.getDexMod() * (float)CharacterSkill.GetOwner(this).getStatDexCurrent() / 100f; + if (this.skillsBase.getConMod() > 0) + statMod += (float)this.skillsBase.getConMod() * (float)CharacterSkill.GetOwner(this).getStatConCurrent() / 100f; + if (this.skillsBase.getIntMod() > 0) + statMod += (float)this.skillsBase.getIntMod() * (float)CharacterSkill.GetOwner(this).getStatIntCurrent() / 100f; + if (this.skillsBase.getSpiMod() > 0) + statMod += (float)this.skillsBase.getSpiMod() * (float)CharacterSkill.GetOwner(this).getStatSpiCurrent() / 100f; + if (statMod < 1) + statMod = 1f; + else if (statMod > 600) + statMod = 600f; + base += CharacterSkill.baseSkillValues[(int)statMod]; + SourceType sourceType = SourceType.GetSourceType(this.skillsBase.getNameNoSpace()); + + //Get any rune, effect and item bonus + + if (CharacterSkill.GetOwner(this).getBonuses() != null) { + //add bonuses from effects/items and runes + base += bonus + CharacterSkill.GetOwner(this).getBonuses().getFloat(ModType.Skill, sourceType); + } + + if (base < 1f) + this.baseAmount = 1f; + else + this.baseAmount = base; + + float modAmount = this.baseAmount + calculateAmountAfterTrains(); + + if (CharacterSkill.GetOwner(this).getBonuses() != null) { + //Multiply any percent bonuses + modAmount *= (1 + CharacterSkill.GetOwner(this).getBonuses().getFloatPercentAll(ModType.Skill, sourceType)); + } + + this.modifiedAmount = (int)(modAmount); + } + + /** + * @ Calculates Modified Skill Percentage + * Call this when number of trains change for skill + */ + public float calculateAmountAfterTrains() { + if (this.skillsBase == null) { + Logger.error( "SkillsBase not found for skill " + this.getObjectUUID()); + this.modifiedAmount = this.baseAmount; + } + int amount; + + int trains = this.numTrains.get(); + if (trains < 10) + amount = (trains * 2); + else if (trains < 90) + amount = 10 + trains; + else if (trains < 134) + amount = 100 + ((trains-90) / 2); + else + amount = 122 + ((trains-134) / 3); + + return amount; + } + + public static int getTrainsAvailable(PlayerCharacter pc) { + + if (pc == null) + return 0; + if (pc.getRace() == null || pc.getBaseClass() == null) { + Logger.error("Race or BaseClass not found for player " + pc.getObjectUUID()); + return 0; + } + int raceBonus = 0; + int baseMod = 0; + int promoMod = 6; + int available = 0; + + //get racial bonus; + if (pc.getRace().getRaceType().equals(Enum.RaceType.HUMANMALE) || + pc.getRace().getRaceType().equals(Enum.RaceType.HUMANFEMALE) ) + raceBonus = 1; //Human racial bonus; + + //get base class trains + if (pc.getBaseClass().getObjectUUID() == 2500 || pc.getBaseClass().getObjectUUID() == 2502) { + baseMod = 4; //Fighter or Rogue + } else { + baseMod = 5; //Healer or Mage + } + + int level = pc.getLevel(); + if (level > 74) + available = 62 + (49 * (promoMod + baseMod + raceBonus)) + (9 * (baseMod + raceBonus)); + else if (level > 69) + available = ((level - 69) * 3) + 45 + (49 * (promoMod + baseMod + raceBonus)) + (9 * (baseMod + raceBonus)); + else if (level > 64) + available = ((level - 64) * 4) + 25 + (49 * (promoMod + baseMod + raceBonus)) + (9 * (baseMod + raceBonus)); + else if (level > 59) //Between 60 and 65 + available = ((level - 59) * 5) + (49 * (promoMod + baseMod + raceBonus)) + (9 * (baseMod + raceBonus)); + else if (level > 10) //Between 11 and 59 + available = ((level - 10) * (promoMod + baseMod + raceBonus)) + (9 * (baseMod + raceBonus)); + // available = (level - 59) + (49 * (promoMod + baseMod + raceBonus)) + (9 * (baseMod + raceBonus)); + else if (level == 10 && (pc.getPromotionClass() != null)) //10 but promoted + available = (promoMod + baseMod + raceBonus) + (9 * (baseMod + raceBonus)); + else //not promoted + available = (level-1) * (baseMod + raceBonus); + + //next subtract trains in any skills + ConcurrentHashMap skills = pc.getSkills(); + Iterator it = skills.keySet().iterator(); + while(it.hasNext()) { + String name = it.next(); + CharacterSkill cs = skills.get(name); + if (cs != null) + available -= cs.numTrains.get(); + } + + //TODO subtract any trains from powers + ConcurrentHashMap powers = pc.getPowers(); + for (CharacterPower power : powers.values()) { + if (power != null) + available -= power.getTrains(); + } + + if(MBServerStatics.BONUS_TRAINS_ENABLED) { + available += 1000; + } + + // if (available < 0) { + //TODO readd this error log after test + // Logger.error("CharacterSkill.getTrainsAvailable", "Number of trains available less then 0 for Player " + pc.getUUID()); + // available = 0; + // } + + //return what's left + return available; + } + + /** + * @ Returns mastery base when mastery not granted + * to player. For calculating damage correctly + */ + public static float getQuickMastery(AbstractCharacter pc, String mastery) { + SkillsBase sb = SkillsBase.getFromCache(mastery); + if (sb == null) { + sb = DbManager.SkillsBaseQueries.GET_BASE_BY_NAME(mastery); + if (sb == null) { + //Logger.error("CharacterSkill.getQuickMastery", "Unable to find skillsbase of name " + mastery); + return 0f; + } + } + + float bonus = 0f; + SourceType sourceType = SourceType.GetSourceType(sb.getNameNoSpace()); + if (pc.getBonuses() != null) { + //Get bonuses from runes + bonus = pc.getBonuses().getSkillBonus(sb.sourceType); + } + float base = 4.75f; + base += (0.0025f * sb.getStrMod() * pc.getStatStrCurrent()); + base += (0.0025f * sb.getDexMod() * pc.getStatDexCurrent()); + base += (0.0025f * sb.getConMod() * pc.getStatConCurrent()); + base += (0.0025f * sb.getIntMod() * pc.getStatIntCurrent()); + base += (0.0025f * sb.getSpiMod() * pc.getStatSpiCurrent()); + return base + bonus; + } + + /* + * This iterates through players runes and adds and removes skills as needed + * Don't Call this directly. Instead call pc.calculateSkills(). + */ + public static void calculateSkills(PlayerCharacter pc) { + if (pc == null) + return; + + ConcurrentHashMap skills = pc.getSkills(); + + //First add skills that don't exist + Race race = pc.getRace(); + if (race != null) { + CharacterSkill.grantSkills(race.getSkillsGranted(), pc); + } else + Logger.error( "Failed to find Race for player " + pc.getObjectUUID()); + BaseClass bc = pc.getBaseClass(); + if (bc != null) { + CharacterSkill.grantSkills(bc.getSkillsGranted(), pc); + } else + Logger.error( "Failed to find BaseClass for player " + pc.getObjectUUID()); + PromotionClass promo = pc.getPromotionClass(); + if (promo != null) + CharacterSkill.grantSkills(promo.getSkillsGranted(), pc); + ArrayList runes = pc.getRunes(); + if (runes != null) { + for (CharacterRune rune : runes) { + CharacterSkill.grantSkills(rune.getSkillsGranted(), pc); + } + } else + Logger.error("Failed to find Runes list for player " + pc.getObjectUUID()); + + //next remove any skills that no longer belong + Iterator it = skills.values().iterator(); + while(it.hasNext()) { + CharacterSkill cs = it.next(); + if (cs == null) + continue; + SkillsBase sb = cs.skillsBase; + if (sb == null) { + DbManager.CharacterSkillQueries.DELETE_SKILL(cs.getObjectUUID()); + it.remove(); + continue; + } + boolean valid = false; + if (CharacterSkill.skillAllowed(sb.getObjectUUID(), race.getSkillsGranted(), pc)) + continue; + if (CharacterSkill.skillAllowed(sb.getObjectUUID(), bc.getSkillsGranted(), pc)) + continue; + if (promo != null) + if (CharacterSkill.skillAllowed(sb.getObjectUUID(), promo.getSkillsGranted(), pc)) + continue; + for (CharacterRune rune : runes) { + if (CharacterSkill.skillAllowed(sb.getObjectUUID(), rune.getSkillsGranted(), pc)) { + valid = true; + continue; + } + } + //if skill doesn't belong to any runes, then remove it + if (!valid) { + DbManager.CharacterSkillQueries.DELETE_SKILL(cs.getObjectUUID()); + it.remove(); + } + } + CharacterSkill.updateAllBaseAmounts(pc); + CharacterSkill.updateAllModifiedAmounts(pc); + CharacterPower.calculatePowers(pc); + } + + /* + *This grants skills for specific runes + */ + private static void grantSkills(ArrayList skillsGranted, PlayerCharacter pc) { + ConcurrentHashMap skills = pc.getSkills(); + if (skills == null) + return; + + for (SkillReq skillreq : skillsGranted) { + SkillsBase skillsBase = skillreq.getSkillsBase(); + + //If player not high enough level for skill, then skip + if (pc.getLevel() < skillreq.getLevel()) + continue; + //If player doesn't have prereq skills high enough then skip + boolean valid = true; + for (byte prereqSkill : skillreq.getSkillReqs()) { + SkillsBase sb = null; + sb = DbManager.SkillsBaseQueries.GET_BASE(prereqSkill); + if (sb != null) { + if (skills.containsKey(sb.getName())) { + if (validForWarrior(pc, skills.get(sb.getName()), skillreq)) { + valid = true; + break; //add if any prereq skills met + } else if (skills.get(sb.getName()).modifiedAmountBeforeMods >= 80) { + valid = true; + break; //add if any prereq skills met + // allow blade masters to use blade master without training sword above 80.. + } else if (skillsBase.getObjectUUID() == 9){ + valid = true; + break; + } + + } + } else { + Logger.error("Failed to find SkillsBase of ID " + prereqSkill); + } + valid = false; + } + // Throwing does not need axe,dagger, or hammer at 80% + if (skillreq.getSkillID() == 43) + valid = true; + if (!valid) + continue; + + //Skill valid for player. Add if don't already have + if (skillsBase != null) { + if (!skills.containsKey(skillsBase.getName())) { + CharacterSkill newSkill = new CharacterSkill(skillsBase, pc); + CharacterSkill cs = null; + try { + cs = DbManager.CharacterSkillQueries.ADD_SKILL(newSkill); + } catch (Exception e) { + cs = null; + } + if (cs != null){ + cs.requiredLevel = (int) skillreq.getLevel(); + skills.put(skillsBase.getName(), cs); + } + + else + Logger.error("Failed to add CharacterSkill to player " + pc.getObjectUUID()); + } + else{ + CharacterSkill cs = skills.get(skillsBase.getName()); + if (cs != null && cs.requiredLevel == 0) { + cs.requiredLevel = (int) skillreq.getLevel(); + } + } + + } else + Logger.error( "Failed to find SkillsBase for SkillReq " + skillreq.getObjectUUID()); + } + } + + private static boolean validForWarrior(PlayerCharacter pc, CharacterSkill skill, SkillReq skillreq) { + if (pc.getPromotionClass() == null || pc.getPromotionClass().getObjectUUID() != 2518 || skill == null || skillreq == null) + return false; //not a warrior + int sID = (skill.skillsBase != null) ? skill.skillsBase.getObjectUUID() : 0; + switch (skillreq.getSkillID()) { + case 3: //Axe mastery + case 19: //Great axe mastery + return (sID == 4) ? (skill.modifiedAmountBeforeMods >= 50) : false; + case 15: //Dagger mastery + return (sID == 16) ? (skill.modifiedAmountBeforeMods >= 50) : false; + case 20: //Great hammer mastery + case 22: //Hammer mastery + return (sID == 23) ? (skill.modifiedAmountBeforeMods >= 50) : false; + case 28: //Polearm mastery + return (sID == 29) ? (skill.modifiedAmountBeforeMods >= 50) : false; + case 21: //Great sword mastery + case 39: //Sword mastery + return (sID == 40) ? (skill.modifiedAmountBeforeMods >= 50) : false; + case 34: //Spear mastery + return (sID == 35) ? (skill.modifiedAmountBeforeMods >= 50) : false; + case 36: //Staff mastery + return (sID == 37) ? (skill.modifiedAmountBeforeMods >= 50) : false; + case 45: //Unarmed combat mastery + return (sID == 46) ? (skill.modifiedAmountBeforeMods >= 50) : false; + case 40: + return true; + case 9: + return true; + default: + } + return false; + } + + /* + * This verifies if a skill is valid for a players rune + */ + private static boolean skillAllowed(int UUID, ArrayList skillsGranted, PlayerCharacter pc) { + ConcurrentHashMap skills = pc.getSkills(); + for (SkillReq skillreq : skillsGranted) { + SkillsBase sb = skillreq.getSkillsBase(); + if (sb != null) { + if (sb.getObjectUUID() == UUID) { + if (skillreq.getLevel() <= pc.getLevel()) { + SkillsBase sbp = null; + if (skillreq.getSkillReqs().size() == 0) + return true; + for (byte prereqSkill : skillreq.getSkillReqs()) { + sbp = DbManager.SkillsBaseQueries.GET_BASE(prereqSkill); + if (sbp != null && skills.containsKey(sbp.getName())) { + if (validForWarrior(pc, skills.get(sbp.getName()), skillreq)) { + return true; + } else if (skills.get(sbp.getName()).modifiedAmountBeforeMods >= 80) + return true; + } + } + + if (skillreq.getSkillID() == 43) + return true; + if (skillreq.getSkillID() == 9) + return true; + } + } + } else + Logger.error( "Failed to find SkillsBase for SkillReq " + skillreq.getObjectUUID()); + } + return false; + } + + //Print skills for player for debugging + public static void printSkills(PlayerCharacter pc) { + if (pc == null) + return; + ConcurrentHashMap skills = pc.getSkills(); + String out = "Player: " + pc.getObjectUUID() + ", SkillCount: " + skills.size(); + Iterator it = skills.keySet().iterator(); + while(it.hasNext()) { + String name = it.next(); + out += ", " + name; + } + Logger.info( out); + } + + public static int getMaxTrains(int intt) { + if (intt > 0 && intt < 191) + return CharacterSkill.maxTrains[intt]; + else + return (int)(33 + 1.25 * intt - 0.005 * Math.pow(intt, 2)); + } + + public int getSkillPercentFromAttributes(){ + AbstractCharacter ac = CharacterSkill.GetOwner(this); + + if (ac == null) + return 0; + + float statMod = 0; + + if (this.skillsBase.getStrMod() > 0){ + float strengthModPercent = (float)this.skillsBase.getStrMod() * .01f; + strengthModPercent *= ac.getStatStrCurrent() * .01f + .6f; + statMod += strengthModPercent; + } + + if (this.skillsBase.getDexMod() > 0){ + float dexModPercent = (float)this.skillsBase.getDexMod() * .01f; + dexModPercent *= ac.getStatDexCurrent() * .01f + .6f; + statMod += dexModPercent; + } + if (this.skillsBase.getConMod() > 0){ + float conModPercent = (float)this.skillsBase.getConMod() * .01f; + conModPercent *= ac.getStatConCurrent() * .01f + .6f; + statMod += conModPercent; + } + if (this.skillsBase.getIntMod() > 0){ + float intModPercent = (float)this.skillsBase.getIntMod() * .01f; + intModPercent *= ac.getStatIntCurrent() * .01f + .6f; + statMod += intModPercent; + } + if (this.skillsBase.getSpiMod() > 0){ + float spiModPercent = (float)this.skillsBase.getSpiMod() * .01f; + spiModPercent *= ac.getStatSpiCurrent() * .01f + .6f; + statMod += spiModPercent; + } + + statMod = (float) (Math.pow(statMod, 1.5f) * 15f); + if (statMod < 1) + statMod = 1f; + else if (statMod > 600) + statMod = 600f; + + + + + return (int) statMod; + } + + public int getSkillPercentFromTrains(){ + + int trains = this.numTrains.get(); + if ( trains <= 10 ) + return 2 * trains; + if ( trains <= 90 ) + return trains + 10; + if ( trains > 130 ) + return (int) (120 - ((trains - 130) * -0.33000001)); + return (int) (100 - ((trains - 90) * -0.5)); + + } + + public int getTotalSkillPercet(){ + + AbstractCharacter ac = CharacterSkill.GetOwner(this); + + if (ac == null) + return 0; + + float bonus = 0f; + SourceType sourceType = SourceType.GetSourceType(this.skillsBase.getNameNoSpace()); + if (CharacterSkill.GetOwner(this).getBonuses() != null) { + //Get bonuses from runes + bonus = CharacterSkill.GetOwner(this).getBonuses().getSkillBonus(this.skillsBase.sourceType); + } + return this.getSkillPercentFromTrains() + this.getSkillPercentFromAttributes(); + } + + @Override + public void updateDatabase() { + DbManager.CharacterSkillQueries.updateDatabase(this); + } + + public int getRequiredLevel() { + return requiredLevel; + } + + public void setRequiredLevel(int requiredLevel) { + this.requiredLevel = requiredLevel; + } +} diff --git a/src/engine/objects/CharacterTitle.java b/src/engine/objects/CharacterTitle.java new file mode 100644 index 00000000..1ca8a4e6 --- /dev/null +++ b/src/engine/objects/CharacterTitle.java @@ -0,0 +1,121 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.net.ByteBufferWriter; + +import java.nio.ByteBuffer; + +public enum CharacterTitle { + + NONE(0,0,0,""), + CSR_1(255,0,0,"CCR"), + CSR_2(255,0,0,"CCR"), + CSR_3(255,0,0,"CCR"), + CSR_4(251,181,13,"CCR"), + DEVELOPER(166,153,114,"Programmer"), + QA(88,250,244,"GIRLFRIEND"); + + CharacterTitle(int _r, int _g, int _b, String _prefix) { + char[] str_header = ("^\\c" + + (((_r < 100)?((_r < 10)?"00":"0"):"") + ((byte) _r & 0xFF)) + + (((_g < 100)?((_g < 10)?"00":"0"):"") + ((byte) _g & 0xFF)) + + (((_b < 100)?((_b < 10)?"00":"0"):"") + ((byte) _b & 0xFF)) + + '<' + _prefix + "> ").toCharArray(); + + char[] str_footer = ("^\\c255255255").toCharArray(); + + this.headerLength = str_header.length; + this.footerLength = str_footer.length; + + this.header = ByteBuffer.allocateDirect(headerLength << 1); + this.footer = ByteBuffer.allocateDirect(footerLength << 1); + + ByteBufferWriter headWriter= new ByteBufferWriter(header); + + for(char c : str_header) { + headWriter.putChar(c); + } + + ByteBufferWriter footWriter = new ByteBufferWriter(footer); + + for(char c : str_footer) { + footWriter.putChar(c); + } + } + + int headerLength, footerLength; + private ByteBuffer header; + private ByteBuffer footer; + + public void _serializeFirstName(ByteBufferWriter writer, String firstName) { + _serializeFirstName(writer, firstName, false); + } + + public void _serializeFirstName(ByteBufferWriter writer, String firstName, boolean smallString) { + if(this.ordinal() == 0) { + if (smallString) + writer.putSmallString(firstName); + else + writer.putString(firstName); + return; + } + + char[] chars = firstName.toCharArray(); + + if (smallString) + writer.put((byte)(chars.length + this.headerLength)); + else + writer.putInt(chars.length + this.headerLength); + writer.putBB(header); + + for(char c : chars) { + writer.putChar(c); + } + } + + public void _serializeLastName(ByteBufferWriter writer, String lastName, boolean haln, boolean asciiLastName) { + _serializeLastName(writer, lastName, haln, asciiLastName, false); + } + + public void _serializeLastName(ByteBufferWriter writer, String lastName, boolean haln, boolean asciiLastName, boolean smallString) { + if (!haln || asciiLastName) { + if(this.ordinal() == 0) { + if (smallString) + writer.putSmallString(lastName); + else + writer.putString(lastName); + return; + } + } + + if (!haln || asciiLastName) { + char[] chars = lastName.toCharArray(); + + if (smallString) + writer.put((byte)(chars.length + this.footerLength)); + else + writer.putInt(chars.length + this.footerLength); + + for(char c : chars) { + writer.putChar(c); + } + + writer.putBB(footer); + } else { + if (smallString) + writer.put((byte)this.footerLength); + else + writer.putInt(this.footerLength); + writer.putBB(footer); + } + + } +} diff --git a/src/engine/objects/City.java b/src/engine/objects/City.java new file mode 100644 index 00000000..f4ed1b52 --- /dev/null +++ b/src/engine/objects/City.java @@ -0,0 +1,1486 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.objects; + +import engine.Enum; +import engine.Enum.*; +import engine.InterestManagement.HeightMap; +import engine.InterestManagement.RealmMap; +import engine.InterestManagement.WorldGrid; +import engine.db.archive.CityRecord; +import engine.db.archive.DataWarehouse; +import engine.gameManager.*; +import engine.math.Bounds; +import engine.math.FastMath; +import engine.math.Vector2f; +import engine.math.Vector3fImmutable; +import engine.net.ByteBufferWriter; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.TaxResourcesMsg; +import engine.net.client.msg.ViewResourcesMessage; +import engine.powers.EffectsBase; +import engine.server.MBServerStatics; +import engine.workthreads.DestroyCityThread; +import engine.workthreads.TransferCityThread; +import org.pmw.tinylog.Logger; + +import java.net.UnknownHostException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +public class City extends AbstractWorldObject { + + private String cityName; + private String motto; + private String description; + public java.time.LocalDateTime established; + private int isNoobIsle; //1: noob, 0: not noob: -1: not noob, no teleport + private int population = 0; + private int siegesWithstood = 0; + private int realmID; + private int radiusType; + private float bindRadius; + private float statLat; + private float statAlt; + private float statLon; + private float bindX; + private float bindZ; + private byte isNpc; //aka Safehold + private byte isCapital = 0; + private byte isSafeHold; + private boolean forceRename = false; + public boolean hasBeenTransfered = false; + + private boolean noTeleport = false; //used by npc cities + private boolean noRepledge = false; //used by npc cities + private boolean isOpen = false; + + private int treeOfLifeID; + private Vector3fImmutable location = Vector3fImmutable.ZERO; + private Vector3fImmutable bindLoc; + protected Zone parentZone; + private int warehouseBuildingID = 0; + private boolean open = false; + private boolean reverseKOS = false; + public static long lastCityUpdate = 0; + public LocalDateTime realmTaxDate; + + public ReentrantReadWriteLock transactionLock = new ReentrantReadWriteLock(); + + // Players who have entered the city (used for adding and removing affects) + + private final HashSet _playerMemory = new HashSet<>(); + + public volatile boolean protectionEnforced = true; + private String hash; + + /** + * ResultSet Constructor + */ + + public City(ResultSet rs) throws SQLException, UnknownHostException { + super(rs); + try{ + this.cityName = rs.getString("name"); + this.motto = rs.getString("motto"); + this.isNpc = rs.getByte("isNpc"); + this.isSafeHold = (byte) ((this.isNpc == 1) ? 1 : 0); + this.description = ""; // TODO Implement this! + this.isNoobIsle = rs.getByte("isNoobIsle"); // Noob + this.gridObjectType = GridObjectType.STATIC; + // Island + // City(00000001), + // Otherwise(FFFFFFFF) + this.population = rs.getInt("population"); + this.siegesWithstood = rs.getInt("siegesWithstood"); + + java.sql.Timestamp establishedTimeStamp = rs.getTimestamp("established"); + + if (establishedTimeStamp != null) + this.established = java.time.LocalDateTime.ofInstant(establishedTimeStamp.toInstant(), ZoneId.systemDefault()); + + this.location = new Vector3fImmutable(rs.getFloat("xCoord"), rs.getFloat("yCoord"), rs.getFloat("zCoord")); + this.statLat = rs.getFloat("xCoord"); + this.statAlt = rs.getFloat("yCoord"); + this.statLon = rs.getFloat("zCoord"); + + java.sql.Timestamp realmTaxTimeStamp = rs.getTimestamp("realmTaxDate"); + + if (realmTaxTimeStamp != null) + this.realmTaxDate = realmTaxTimeStamp.toLocalDateTime(); + + if (this.realmTaxDate == null) + this.realmTaxDate = LocalDateTime.now(); + + this.treeOfLifeID = rs.getInt("treeOfLifeUUID"); + this.bindX = rs.getFloat("bindX"); + this.bindZ = rs.getFloat("bindZ"); + this.bindLoc = new Vector3fImmutable(this.location.getX() + this.bindX, + this.location.getY(), + this.location.getZ() + this.bindZ); + this.radiusType = rs.getInt("radiusType"); + float bindradiustemp = rs.getFloat("bindRadius"); + if (bindradiustemp > 2) + bindradiustemp -=2; + + this.bindRadius = bindradiustemp; + + this.forceRename = rs.getInt("forceRename") == 1; + this.open = rs.getInt("open") == 1; + + if (this.cityName.equals("Perdition") || this.cityName.equals("Bastion")) { + this.noTeleport = true; + this.noRepledge = true; + } else { + this.noTeleport = false; + this.noRepledge = false; + } + + this.hash = rs.getString("hash"); + + if (this.motto.isEmpty()){ + Guild guild = this.getGuild(); + + if (guild != null && guild.isErrant() == false) + this.motto = guild.getMotto(); + } + + + //Disabled till i finish. + // this.reverseKOS = rs.getInt("kos") == 1; + + + Zone zone = ZoneManager.getZoneByUUID(rs.getInt("parent")); + + if (zone != null) + setParent(zone); + + //npc cities without heightmaps except swampstone are specials. + + + + this.realmID = rs.getInt("realmID"); + + }catch(Exception e){ + Logger.error(e); + } + + // *** Refactor: Is this working? Intended to supress + // login server errors from attempting to + // load cities/realms along with players + + + + } + + /* + * Utils + */ + + public boolean renameCity(String cityName){ + if (!DbManager.CityQueries.renameCity(this, cityName)) + return false; + if (!DbManager.CityQueries.updateforceRename(this, false)) + return false; + + this.cityName = cityName; + this.forceRename = false; + return true; + } + + public boolean updateTOL(Building tol){ + if (tol == null) + return false; + if (!DbManager.CityQueries.updateTOL(this, tol.getObjectUUID())) + return false; + this.treeOfLifeID = tol.getObjectUUID(); + return true; + } + + public boolean renameCityForNewPlant(String cityName){ + if (!DbManager.CityQueries.renameCity(this, cityName)) + return false; + if (!DbManager.CityQueries.updateforceRename(this, true)) + return false; + + this.cityName = cityName; + this.forceRename = true; + return true; + } + + public void setForceRename(boolean forceRename) { + if (!DbManager.CityQueries.updateforceRename(this, forceRename)) + return; + this.forceRename = forceRename; + } + public String getCityName() { + + return cityName; + } + + public String getMotto() { + return motto; + } + + public String getDescription() { + return description; + } + + public Building getTOL() { + if (this.treeOfLifeID == 0) + return null; + + return BuildingManager.getBuildingFromCache(this.treeOfLifeID); + + } + + public int getIsNoobIsle() { + return isNoobIsle; + } + + public int getPopulation() { + return population; + } + + public int getSiegesWithstood() { + return siegesWithstood; + } + + public float getLatitude() { + return this.location.x; + } + + public float getLongitude() { + return this.location.z; + } + + public float getAltitude() { + return this.location.y; + } + + @Override + public Vector3fImmutable getLoc() { + return this.location; + } + + public byte getIsNpcOwned() { + return isNpc; + } + + public byte getIsSafeHold() { + return this.isSafeHold; + } + + public boolean isSafeHold() { + return (this.isSafeHold == (byte) 1); + } + + public byte getIsCapital() { + return isCapital; + } + + public void setIsCapital(boolean state) { + this.isCapital = (state) ? (byte) 1 : (byte) 0; + } + + public int getRadiusType() { + return this.radiusType; + } + + public float getBindRadius() { + return this.bindRadius; + } + + public int getRank() { + return (this.getTOL() == null) ? 0 : this.getTOL().getRank(); + } + + public Bane getBane() { + return Bane.getBane(this.getObjectUUID()); + } + + public void setParent(Zone zone) { + + try { + + + this.parentZone = zone; + this.location = new Vector3fImmutable(zone.absX + statLat, zone.absY + statAlt, zone.absZ + statLon); + this.bindLoc = new Vector3fImmutable(this.location.x + this.bindX, + this.location.y, + this.location.z + this.bindZ); + + // set city bounds + + Bounds cityBounds = Bounds.borrow(); + cityBounds.setBounds(new Vector2f(this.location.x + 64, this.location.z + 64), // location x and z are offset by 64 from the center of the city. + new Vector2f(Enum.CityBoundsType.GRID.extents, Enum.CityBoundsType.GRID.extents), + 0.0f); + this.setBounds(cityBounds); + + if (zone.getHeightMap() == null && this.isNpc == 1 && this.getObjectUUID() != 1213 ){ + HeightMap.GenerateCustomHeightMap(zone); + Logger.info(zone.getName() + " created custom heightmap"); + } + }catch(Exception e){ + Logger.error(e); + } + } + + public Zone getParent() { + return this.parentZone; + } + + public boolean isCityZone(Zone zone) { + + if (zone == null || this.parentZone == null) + return false; + + return zone.getObjectUUID() == this.parentZone.getObjectUUID(); + + } + + public AbstractCharacter getOwner() { + + if (this.getTOL() == null) + return null; + + int ownerID = this.getTOL().getOwnerUUID(); + + if (ownerID == 0) + return null; + + if (this.isNpc == 1) + return NPC.getNPC(ownerID); + else + return PlayerCharacter.getPlayerCharacter(ownerID); + } + + public Guild getGuild() { + + if (this.getTOL() == null) + return null; + + + + if (this.isNpc == 1) { + + if (this.getTOL().getOwner() == null) + return null; + return this.getTOL().getOwner().getGuild(); + } else { + if (this.getTOL().getOwner() == null) + return null; + return this.getTOL().getOwner().getGuild(); + } + } + + public boolean openCity(boolean open){ + if (!DbManager.CityQueries.updateOpenCity(this, open)) + return false; + this.open = open; + return true; + } + + + public static void _serializeForClientMsg(City city, ByteBufferWriter writer) { + City.serializeForClientMsg(city,writer); + } + + /* + * Serializing + */ + + + public static void serializeForClientMsg(City city, ByteBufferWriter writer) { + AbstractCharacter guildRuler; + Guild rulingGuild; + Guild rulingNation; + java.time.LocalDateTime dateTime1900; + + // Cities aren't a city without a TOL. Time to early exit. + // No need to spam the log here as non-existant TOL's are indicated + // during bootstrap routines. + + if (city.getTOL() == null){ + + Logger.error( "NULL TOL FOR " + city.cityName); + } + + + // Assign city owner + + if (city.getTOL() != null) + guildRuler = city.getTOL().getOwner(); + else guildRuler = null; + + // If is an errant tree, use errant guild for serialization. + // otherwise we serialize the soverign guild + + if (guildRuler == null) + rulingGuild = Guild.getErrantGuild(); + else + rulingGuild = guildRuler.getGuild(); + + rulingNation = rulingGuild.getNation(); + + // Begin Serialzing soverign guild data + writer.putInt(city.getObjectType().ordinal()); + writer.putInt(city.getObjectUUID()); + writer.putString(city.cityName); + writer.putInt(rulingGuild.getObjectType().ordinal()); + writer.putInt(rulingGuild.getObjectUUID()); + + writer.putString(rulingGuild.getName()); + writer.putString(city.motto); + writer.putString(rulingGuild.getLeadershipType()); + + // Serialize guild ruler's name + // If tree is abandoned blank out the name + // to allow them a rename. + + if (guildRuler == null) + writer.putString(""); + else + writer.putString(guildRuler.getFirstName() + ' ' + guildRuler.getLastName()); + + writer.putInt(rulingGuild.getCharter()); + writer.putInt(0); // always 00000000 + + writer.put(city.isSafeHold); + + writer.put((byte) 1); + writer.put((byte) 1); // *** Refactor: What are these flags? + writer.put((byte) 1); + writer.put((byte) 1); + writer.put((byte) 1); + + GuildTag._serializeForDisplay(rulingGuild.getGuildTag(),writer); + GuildTag._serializeForDisplay(rulingNation.getGuildTag(),writer); + + writer.putInt(0);// TODO Implement description text + + writer.put((byte) 1); + + if (city.isCapital > 0) + writer.put((byte) 1); + else + writer.put((byte) 0); + + writer.put((byte) 1); + + // Begin serializing nation guild info + + if (rulingNation.isErrant()){ + writer.putInt(rulingGuild.getObjectType().ordinal()); + writer.putInt(rulingGuild.getObjectUUID()); + } + + else{ + writer.putInt(rulingNation.getObjectType().ordinal()); + writer.putInt(rulingNation.getObjectUUID()); + } + + + // Serialize nation name + + if (rulingNation.isErrant()) + writer.putString("None"); + else + writer.putString(rulingNation.getName()); + + writer.putInt(city.getTOL().getRank()); + + if (city.isNoobIsle > 0) + writer.putInt(1); + else + writer.putInt(0xFFFFFFFF); + + writer.putInt(city.population); + + if (rulingNation.isErrant()) + writer.putString(" "); + else + writer.putString(Guild.GetGL(rulingNation).getFirstName() + ' ' + Guild.GetGL(rulingNation).getLastName()); + + + writer.putLocalDateTime(city.established); + +// writer.put((byte) city.established.getDayOfMonth()); +// writer.put((byte) city.established.minusMonths(1).getMonth().getValue()); +// writer.putInt((int) years); +// writer.put((byte) hours); +// writer.put((byte) minutes); +// writer.put((byte) seconds); + + writer.putFloat(city.location.x); + writer.putFloat(city.location.y); + writer.putFloat(city.location.z); + + writer.putInt(city.siegesWithstood); + + writer.put((byte) 1); + writer.put((byte) 0); + writer.putInt(0x64); + writer.put((byte) 0); + writer.put((byte) 0); + writer.put((byte) 0); + } + + public static Vector3fImmutable getBindLoc(int cityID) { + + City city; + + city = City.getCity(cityID); + + if (city == null) + return Enum.Ruins.getRandomRuin().getLocation(); + + return city.getBindLoc(); + } + + public Vector3fImmutable getBindLoc() { + Vector3fImmutable treeLoc = null; + + if (this.getTOL() != null && this.getTOL().getRank() == 8) + treeLoc = this.getTOL().getStuckLocation(); + + if (treeLoc != null) + return treeLoc; + + if (this.radiusType == 1 && this.bindRadius > 0f) { + //square radius + float x = this.bindLoc.getX(); + float z = this.bindLoc.getZ(); + float offset = ((ThreadLocalRandom.current().nextFloat() * 2) - 1) * this.bindRadius; + int direction = ThreadLocalRandom.current().nextInt(4); + + switch (direction) { + case 0: + x += this.bindRadius; + z += offset; + break; + case 1: + x += offset; + z -= this.bindRadius; + break; + case 2: + x -= this.bindRadius; + z += offset; + break; + case 3: + x += offset; + z += this.bindRadius; + break; + } + return new Vector3fImmutable(x, this.bindLoc.getY(), z); + } else if (this.radiusType == 2 && this.bindRadius > 0f) { + //circle radius + Vector3fImmutable dir = FastMath.randomVector2D(); + return this.bindLoc.scaleAdd(this.bindRadius, dir); + } else if (this.radiusType == 3 && this.bindRadius > 0f) { + //random inside square + float x = this.bindLoc.getX(); + x += ((ThreadLocalRandom.current().nextFloat() * 2) - 1) * this.bindRadius; + float z = this.bindLoc.getZ(); + z += ((ThreadLocalRandom.current().nextFloat() * 2) - 1) * this.bindRadius; + return new Vector3fImmutable(x, this.bindLoc.getY(), z); + } else if (this.radiusType == 4 && this.bindRadius > 0f) { + //random inside circle + Vector3fImmutable dir = FastMath.randomVector2D(); + return this.bindLoc.scaleAdd(ThreadLocalRandom.current().nextFloat() * this.bindRadius, dir); + } else + //spawn at bindLoc + //System.out.println("x: " + this.bindLoc.x + ", z: " + this.bindLoc.z); + return this.bindLoc; + } + + public static ArrayList getCitiesToTeleportTo(PlayerCharacter pc) { + + ArrayList cities = new ArrayList<>(); + + if (pc == null) + return cities; + + Guild pcG = pc.getGuild(); + + ConcurrentHashMap worldCities = DbManager.getMap(Enum.GameObjectType.City); + + //add npc cities + for (AbstractGameObject ago : worldCities.values()) { + + if (ago.getObjectType().equals(GameObjectType.City)) { + City city = (City) ago; + + if (city.noTeleport) + continue; + + if (city.parentZone != null && city.parentZone.isPlayerCity()) { + + if (pc.getAccount().status.equals(AccountStatus.ADMIN)) { + cities.add(city); + } else + //list Player cities + + //open city, just list + if (city.open && city.getTOL() != null && city.getTOL().getRank() > 4) { + + if (!BuildingManager.IsPlayerHostile(city.getTOL(), pc)) + cities.add(city); //verify nation or guild is same + } + + else if (Guild.sameNationExcludeErrant(city.getGuild(), pcG)) + cities.add(city); + + + } else if (city.isNpc == 1) { + //list NPC cities + Guild g = city.getGuild(); + if (g == null) { + if (city.isNpc == 1) + if (city.isNoobIsle == 1) { + if (pc.getLevel() < 21) + cities.add(city); + } else if (pc.getLevel() > 9) + cities.add(city); + + } else if (pc.getLevel() >= g.getTeleportMin() && pc.getLevel() <= g.getTeleportMax()){ + + + cities.add(city); + } + } + + + } + } + + return cities; + } + + public NPC getRuneMaster() { + NPC outNPC = null; + + if (this.getTOL() == null) + return outNPC; + + for (AbstractCharacter npc : getTOL().getHirelings().keySet()) { + if (npc.getObjectType() == GameObjectType.NPC) + if (((NPC)npc).getContract().isRuneMaster() == true) + outNPC = (NPC)npc; + } + + return outNPC; + } + + public static ArrayList getCitiesToRepledgeTo(PlayerCharacter pc) { + ArrayList cities = new ArrayList<>(); + if (pc == null) + return cities; + Guild pcG = pc.getGuild(); + + ConcurrentHashMap worldCities = DbManager.getMap(Enum.GameObjectType.City); + + //add npc cities + for (AbstractGameObject ago : worldCities.values()) { + if (ago.getObjectType().equals(GameObjectType.City)) { + City city = (City) ago; + if (city.noRepledge) + continue; + + if (city.parentZone != null && city.parentZone.isPlayerCity()) { + + //list Player cities + //open city, just list + if (pc.getAccount().status.equals(AccountStatus.ADMIN)) { + cities.add(city); + } else if (city.open && city.getTOL() != null && city.getTOL().getRank() > 4) { + + if (!BuildingManager.IsPlayerHostile(city.getTOL(), pc)) + cities.add(city); //verify nation or guild is same + } else if (Guild.sameNationExcludeErrant(city.getGuild(), pcG)) + cities.add(city); + + } else if (city.isNpc == 1) { + //list NPC cities + + Guild g = city.getGuild(); + if (g == null) { + if (city.isNpc == 1) + if (city.isNoobIsle == 1) { + if (pc.getLevel() < 21) + cities.add(city); + } else if (pc.getLevel() > 9) + cities.add(city); + } else if (pc.getLevel() >= g.getRepledgeMin() && pc.getLevel() <= g.getRepledgeMax()){ + + cities.add(city); + } + } + } + } + return cities; + } + + public boolean isOpen() { + return open; + } + + public static void loadCities(Zone zone) { + + ArrayList cities = DbManager.CityQueries.GET_CITIES_BY_ZONE(zone.getObjectUUID()); + + for (City city : cities) { + + city.setParent(zone); + city.setObjectTypeMask(MBServerStatics.MASK_CITY); + city.setLoc(city.location); + + //not player city, must be npc city.. + if (!zone.isPlayerCity()) + zone.setNPCCity(true); + + if ((ConfigManager.serverType.equals(ServerType.WORLDSERVER)) && (city.hash == null)) { + + city.setHash(); + + if (DataWarehouse.recordExists(Enum.DataRecordType.CITY, city.getObjectUUID()) == false) { + CityRecord cityRecord = CityRecord.borrow(city, Enum.RecordEventType.CREATE); + DataWarehouse.pushToWarehouse(cityRecord); + } + } + } + } + + + + @Override + public void updateDatabase() { + // TODO Create update logic. + } + + public static City getCity(int cityId) { + + if (cityId == 0) + return null; + + City city = (City) DbManager.getFromCache(Enum.GameObjectType.City, cityId); + if (city != null) + return city; + + return DbManager.CityQueries.GET_CITY(cityId); + + } + public static City GetCityFromCache(int cityId) { + + if (cityId == 0) + return null; + + return (City) DbManager.getFromCache(Enum.GameObjectType.City, cityId); + } + + @Override + public void runAfterLoad() { + + // Set city bounds + // *** Note: Moved to SetParent() + // for some undocumented reason + + // Set city motto to current guild motto + + if (BuildingManager.getBuilding(this.treeOfLifeID) == null) + Logger.info( "City UID " + this.getObjectUUID() + " Failed to Load Tree of Life with ID " + this.treeOfLifeID); + + if ((ConfigManager.serverType.equals(ServerType.WORLDSERVER)) + && (this.isNpc == (byte) 0)) { + + Realm wsr = Realm.getRealm(this.realmID); + + if (wsr != null) + wsr.addCity(this.getObjectUUID()); + else + Logger.error("Unable to find realm of ID " + realmID + " for city " + this.getObjectUUID()); + } + + if (this.getGuild() != null) { + this.motto = this.getGuild().getMotto(); + + // Determine if this city is a nation capitol + + if (this.getGuild().getGuildState() == GuildState.Nation) + for (Guild sub : this.getGuild().getSubGuildList()) { + + if ( (sub.getGuildState() == GuildState.Protectorate) || + (sub.getGuildState() == GuildState.Province)) + this.isCapital = 1; + } + + ArrayList guildList = Guild.GuildRoster(this.getGuild()); + + this.population = guildList.size(); + } + + // Banes are loaded for this city from the database at this point + + if (this.getBane() == null) + return; + + // if this city is baned, add the siege effect + + try { + this.getTOL().addEffectBit((1 << 16)); + this.getBane().getStone().addEffectBit((1 << 19));; + }catch(Exception e){ + Logger.info("Failed ao add bane effects on city." + e.getMessage()); + } + } + + public void addCityEffect(EffectsBase effectBase, int rank) { + + HashSet currentPlayers; + PlayerCharacter player; + + // Add this new effect to the current city effect collection. + // so any new player to the grid will have all effects applied + + this.addEffectNoTimer(Integer.toString(effectBase.getUUID()), effectBase, rank, false); + + // Any players currently in the zone will not be processed by the heartbeat + // if it's not the first effect toggled so we do it here manually + + currentPlayers = WorldGrid.getObjectsInRangePartial(this.location, this.parentZone.getBounds().getHalfExtents().x * 1.2f, MBServerStatics.MASK_PLAYER); + + for (AbstractWorldObject playerObject : currentPlayers) { + + if (playerObject == null) + continue; + if (!this.isLocationOnCityZone(playerObject.getLoc())) + continue; + + player = (PlayerCharacter) playerObject; + player.addCityEffect(Integer.toString(effectBase.getUUID()), effectBase, rank, MBServerStatics.FOURTYFIVE_SECONDS, true,this); + } + + } + + public void removeCityEffect(EffectsBase effectBase, int rank, boolean refreshEffect) { + + + PlayerCharacter player; + + // Remove the city effect from the ago's internal collection + + if (this.getEffects().containsKey(Integer.toString(effectBase.getUUID()))) + this.getEffects().remove(Integer.toString(effectBase.getUUID())); + + // Any players currently in the zone will not be processed by the heartbeat + // so we do it here manually + + + for (Integer playerID : this._playerMemory) { + + player = PlayerCharacter.getFromCache(playerID); + if (player == null) + continue; + + player.endEffectNoPower(Integer.toString(effectBase.getUUID())); + + // Reapply effect with timeout? + + if (refreshEffect == true) + player.addCityEffect(Integer.toString(effectBase.getUUID()), effectBase, rank, MBServerStatics.FOURTYFIVE_SECONDS, false,this); + + } + + } + + public Warehouse getWarehouse() { + if (this.warehouseBuildingID == 0) + return null; + return Warehouse.warehouseByBuildingUUID.get(this.warehouseBuildingID); + } + + public Realm getRealm() { + + return Realm.getRealm(this.realmID); + + } + + public boolean isLocationOnCityGrid(Vector3fImmutable insideLoc) { + + Bounds newBounds = Bounds.borrow(); + newBounds.setBounds(insideLoc); + boolean collided = Bounds.collide(this.getBounds(), newBounds,0); + newBounds.release(); + return collided; + } + + public boolean isLocationOnCityGrid(Bounds newBounds) { + + boolean collided = Bounds.collide(this.getBounds(), newBounds,0); + return collided; + } + + public boolean isLocationWithinSiegeBounds(Vector3fImmutable insideLoc) { + + return insideLoc.isInsideCircle(this.getLoc(), CityBoundsType.SIEGE.extents); + + } + + public boolean isLocationOnCityZone(Vector3fImmutable insideLoc) { + return Bounds.collide(insideLoc, this.parentZone.getBounds()); + } + + private void applyAllCityEffects(PlayerCharacter player) { + + Effect effect; + EffectsBase effectBase; + + try { + for (String cityEffect : this.getEffects().keySet()) { + + effect = this.getEffects().get(cityEffect); + effectBase = effect.getEffectsBase(); + + if (effectBase == null) + continue; + + player.addCityEffect(Integer.toString(effectBase.getUUID()), effectBase, effect.getTrains(), MBServerStatics.FOURTYFIVE_SECONDS, true,this); + } + } catch (Exception e) { + Logger.error( e.getMessage()); + } + + } + + private void removeAllCityEffects(PlayerCharacter player,boolean force) { + + Effect effect; + EffectsBase effectBase; + + try { + for (String cityEffect : this.getEffects().keySet()) { + + effect = this.getEffects().get(cityEffect); + effectBase = effect.getEffectsBase(); + + if (player.getEffects().get(cityEffect) == null) + return; + + // player.endEffectNoPower(cityEffect); + player.addCityEffect(Integer.toString(effectBase.getUUID()), effectBase, effect.getTrains(), MBServerStatics.FOURTYFIVE_SECONDS, false,this); + } + } catch (Exception e) { + Logger.error(e.getMessage()); + } + } + + public void onEnter() { + + HashSet currentPlayers; + HashSet currentMemory; + PlayerCharacter player; + + // Gather current list of players within a distance defined by the x extent of the + // city zone. As we want to grab all players with even a remote possibility of + // being in the zone, we might have to increase this distance. + + currentPlayers = WorldGrid.getObjectsInRangePartial(this.location, this.parentZone.getBounds().getHalfExtents().x * 1.41421356237, MBServerStatics.MASK_PLAYER); + currentMemory = new HashSet<>(); + + for (AbstractWorldObject playerObject : currentPlayers) { + + if (playerObject == null) + continue; + + player = (PlayerCharacter) playerObject; + currentMemory.add(player.getObjectUUID()); + + // Player is already in our memory + + if (_playerMemory.contains(player.getObjectUUID())) + continue; + + if (!this.isLocationOnCityZone(player.getLoc())) + continue; + + // Apply safehold affect to player if needed + + if ((this.isSafeHold == 1)) + player.setSafeZone(true); + + //add spire effects. + if (this.getEffects().size() > 0) + this.applyAllCityEffects(player); + + // Add player to our city's memory + + _playerMemory.add(player.getObjectUUID()); + + // ***For debugging + // Logger.info("PlayerMemory for ", this.getCityName() + ": " + _playerMemory.size()); + } + try { + onExit(currentMemory); + } catch (Exception e) { + Logger.error( e.getMessage()); + } + + } + + /* All characters in city player memory but + * not the current memory have obviously + * left the city. Remove their affects. + */ + + private void onExit(HashSet currentMemory) { + + PlayerCharacter player; + int playerUUID = 0; + HashSet toRemove = new HashSet<>(); + Iterator iter = _playerMemory.iterator(); + + while (iter.hasNext()) { + + playerUUID = iter.next(); + + + + player = PlayerCharacter.getFromCache(playerUUID); + if (this.isLocationOnCityZone(player.getLoc())) + continue; + + // Remove players safezone status if warranted + // they can assumed to be not on the citygrid at + // this point. + + + player.setSafeZone(false); + + this.removeAllCityEffects(player,false); + + // We will remove this player after iteration is complete + // so store it in a temporary collection + + toRemove.add(playerUUID); + + // ***For debugging + // Logger.info("PlayerMemory for ", this.getCityName() + ": " + _playerMemory.size()); + } + + // Remove players from city memory + + _playerMemory.removeAll(toRemove); + } + + public int getWarehouseBuildingID() { + return warehouseBuildingID; + } + + public void setWarehouseBuildingID(int warehouseBuildingID) { + this.warehouseBuildingID = warehouseBuildingID; + } + + public final void destroy() { + + Thread destroyCityThread = new Thread(new DestroyCityThread(this)); + + destroyCityThread.setName("deestroyCity:" + this.getName()); + destroyCityThread.start(); + } + + public final void transfer(AbstractCharacter newOwner) { + + Thread transferCityThread = new Thread(new TransferCityThread(this, newOwner)); + + transferCityThread.setName("TransferCity:" + this.getName()); + transferCityThread.start(); + } + + public final void claim(AbstractCharacter sourcePlayer) { + + Guild sourceNation; + Guild sourceGuild; + Zone cityZone; + + sourceGuild = sourcePlayer.getGuild(); + + if (sourceGuild == null) + return; + + sourceNation = sourcePlayer.getGuild().getNation(); + + if (sourceGuild.isErrant()) + return; + + //cant claim tree with owned tree. + + if (sourceGuild.getOwnedCity() != null) + return; + + cityZone = this.parentZone; + + // Can't claim a tree not in a player city zone + + // Reset sieges withstood + + this.setSiegesWithstood(0); + + this.hasBeenTransfered = true; + + // If currently a sub of another guild, desub when + // claiming your new tree and set as Landed + + if (!sourceNation.isErrant() && sourceNation != sourceGuild) { + if (!DbManager.GuildQueries.UPDATE_PARENT(sourceGuild.getObjectUUID(), MBServerStatics.worldUUID)) { + ChatManager.chatGuildError((PlayerCharacter) sourcePlayer, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + return; + } + + sourceNation.getSubGuildList().remove(sourceGuild); + + if (sourceNation.getSubGuildList().isEmpty()) + sourceNation.downgradeGuildState(); + } + + // Link the mew guild with the tree + + if (!DbManager.GuildQueries.SET_GUILD_OWNED_CITY(sourceGuild.getObjectUUID(), this.getObjectUUID())) { + ChatManager.chatGuildError((PlayerCharacter) sourcePlayer, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + return; + } + + sourceGuild.setCityUUID(this.getObjectUUID()); + + sourceGuild.setNation(sourceGuild); + sourceGuild.setGuildState(GuildState.Sovereign); + GuildManager.updateAllGuildTags(sourceGuild); + GuildManager.updateAllGuildBinds(sourceGuild, this); + + // Build list of buildings within this parent zone + + for (Building cityBuilding : cityZone.zoneBuildingSet) { + + // Buildings without blueprints are unclaimable + + if (cityBuilding.getBlueprintUUID() == 0) + continue; + + // All protection contracts are void upon transfer of a city + + // All protection contracts are void upon transfer of a city + //Dont forget to not Flip protection on Banestones and siege Equipment... Noob. + if (cityBuilding.getBlueprint() != null && !cityBuilding.getBlueprint().isSiegeEquip() + && cityBuilding.getBlueprint().getBuildingGroup() != BuildingGroup.BANESTONE) + cityBuilding.setProtectionState(ProtectionState.NONE); + + // Transfer ownership of valid city assets + // these assets are autoprotected. + + if ((cityBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.TOL) + || (cityBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.SPIRE) + || (cityBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.BARRACK) + || (cityBuilding.getBlueprint().isWallPiece()) + || (cityBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.SHRINE)) { + + cityBuilding.claim(sourcePlayer); + cityBuilding.setProtectionState(ProtectionState.PROTECTED); + } + } + + this.setForceRename(true); + + // Reset city timer for map update + + City.lastCityUpdate = System.currentTimeMillis(); + } + + public final boolean transferGuildLeader(PlayerCharacter sourcePlayer) { + + Guild sourceGuild; + Zone cityZone; + sourceGuild = sourcePlayer.getGuild(); + + + if (sourceGuild == null) + return false; + + if (sourceGuild.isErrant()) + return false; + + cityZone = this.parentZone; + + for (Building cityBuilding : cityZone.zoneBuildingSet) { + + // Buildings without blueprints are unclaimable + + if (cityBuilding.getBlueprintUUID() == 0) + continue; + + // All protection contracts are void upon transfer of a city + //Dont forget to not Flip protection on Banestones and siege Equipment... Noob. + + // Transfer ownership of valid city assets + // these assets are autoprotected. + + if ((cityBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.TOL) + || (cityBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.SPIRE) + || (cityBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.BARRACK) + || (cityBuilding.getBlueprint().isWallPiece()) + || (cityBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.SHRINE) + ) { + + cityBuilding.claim(sourcePlayer); + cityBuilding.setProtectionState(ProtectionState.PROTECTED); + } else if(cityBuilding.getBlueprint().getBuildingGroup() == BuildingGroup.WAREHOUSE) + cityBuilding.claim(sourcePlayer); + + + } + this.setForceRename(true); + CityRecord cityRecord = CityRecord.borrow(this, Enum.RecordEventType.TRANSFER); + DataWarehouse.pushToWarehouse(cityRecord); + return true; + + } + + /** + * @return the forceRename + */ + public boolean isForceRename() { + return forceRename; + } + + /** + * @param siegesWithstood the siegesWithstood to set + */ + public void setSiegesWithstood(int siegesWithstood) { + + // early exit if setting to current value + + if (this.siegesWithstood == siegesWithstood) + return; + + if (DbManager.CityQueries.updateSiegesWithstood(this, siegesWithstood) == true) + this.siegesWithstood = siegesWithstood; + else + Logger.error("Error when writing to database for cityUUID: " + this.getObjectUUID()); + } + + /** + * @param population the population to set + */ + public void setPopulation(int population) { + this.population = population; + } + + public boolean isReverseKOS() { + return reverseKOS; + } + + public void setReverseKOS(boolean reverseKOS) { + this.reverseKOS = reverseKOS; + } + + public String getHash() { + return hash; + } + + public void setHash(String hash) { + this.hash = hash; + } + + public void setHash() { + + this.hash = DataWarehouse.hasher.encrypt(this.getObjectUUID()); + + // Write hash to player character table + + DataWarehouse.writeHash(Enum.DataRecordType.CITY, this.getObjectUUID()); + } + + public boolean setRealmTaxDate(LocalDateTime realmTaxDate) { + + if (!DbManager.CityQueries.updateRealmTaxDate(this, realmTaxDate)) + return false; + + this.realmTaxDate = realmTaxDate; + return true; + + } + + //TODO use this for taxing later. +// public boolean isAfterTaxPeriod(LocalDateTime dateTime,PlayerCharacter player){ +// if (dateTime.isBefore(realmTaxDate)){ +// String wait = ""; +// float hours = 1000*60*60; +// float seconds = 1000; +// float hoursUntil = realmTaxDate.minus(dateTime.get).getMillis() /hours; +// int secondsUntil = (int) (realmTaxDate.minus(dateTime.getMillis()).getMillis() /seconds); +// if (hoursUntil < 1) +// wait = "You must wait " + secondsUntil + " seconds before taxing this city again!"; +// else +// wait = "You must wait " + hoursUntil + " hours before taxing this city again!"; +// ErrorPopupMsg.sendErrorMsg(player, wait); +// return false; +// } +// +// return true; +// } + + + + public synchronized boolean TaxWarehouse(TaxResourcesMsg msg,PlayerCharacter player) { + + // Member variable declaration + Building building = BuildingManager.getBuildingFromCache(msg.getBuildingID()); + Guild playerGuild = player.getGuild(); + + if (building == null){ + ErrorPopupMsg.sendErrorMsg(player, "Not a valid Building!"); + return true; + } + + City city = building.getCity(); + if (city == null){ + ErrorPopupMsg.sendErrorMsg(player, "This building does not belong to a city."); + return true; + } + + + if (playerGuild == null || playerGuild.isErrant()){ + ErrorPopupMsg.sendErrorMsg(player, "You must belong to a guild to do that!"); + return true; + } + + if (playerGuild.getOwnedCity() == null){ + ErrorPopupMsg.sendErrorMsg(player, "Your Guild needs to own a city!"); + return true; + } + + if (playerGuild.getOwnedCity().getTOL() == null){ + ErrorPopupMsg.sendErrorMsg(player, "Cannot find Tree of Life for your city!"); + return true; + } + + if (playerGuild.getOwnedCity().getTOL().getRank() != 8){ + ErrorPopupMsg.sendErrorMsg(player, "Your City needs to Own a realm!"); + return true; + } + + if (playerGuild.getOwnedCity().getRealm() == null){ + ErrorPopupMsg.sendErrorMsg(player, "Cannot find realm for your city!"); + return true; + } + Realm targetRealm = RealmMap.getRealmForCity(city); + + if (targetRealm == null){ + ErrorPopupMsg.sendErrorMsg(player, "Cannot find realm for city you are attempting to tax!"); + return true; + } + + if (targetRealm.getRulingCity() == null){ + ErrorPopupMsg.sendErrorMsg(player, "Realm Does not have a ruling city!"); + return true; + } + + if (targetRealm.getRulingCity().getObjectUUID() != playerGuild.getOwnedCity().getObjectUUID()){ + ErrorPopupMsg.sendErrorMsg(player, "Your guild does not rule this realm!"); + return true; + } + + if (playerGuild.getOwnedCity().getObjectUUID() == city.getObjectUUID()){ + ErrorPopupMsg.sendErrorMsg(player, "You cannot tax your own city!"); + return true; + } + + + + + if (!GuildStatusController.isTaxCollector(player.getGuildStatus())){ + ErrorPopupMsg.sendErrorMsg(player, "You Must be a tax Collector!"); + return true; + } + + + + if (this.realmTaxDate.isAfter(LocalDateTime.now())) + return true; + if (msg.getResources().size() == 0) + return true; + + if (city.getWarehouse() == null) + return true; + Warehouse ruledWarehouse = playerGuild.getOwnedCity().getWarehouse(); + if (ruledWarehouse == null) + return true; + + + + ItemBase.getItemHashIDMap(); + + ArrayListresources = new ArrayList<>(); + + float taxPercent = msg.getTaxPercent(); + if (taxPercent > 20) + taxPercent = .20f; + + for (int resourceHash:msg.getResources().keySet()){ + if (ItemBase.getItemHashIDMap().get(resourceHash) != null) + resources.add(ItemBase.getItemHashIDMap().get(resourceHash)); + + } + + for (Integer itemBaseID:resources){ + ItemBase ib = ItemBase.getItemBase(itemBaseID); + if (ib == null) + continue; + if (ruledWarehouse.isAboveCap(ib, (int) (city.getWarehouse().getResources().get(ib) * taxPercent))){ + ErrorPopupMsg.sendErrorMsg(player, "You're warehouse has enough " + ib.getName() + " already!"); + return true; + } + + } + + if(!city.setRealmTaxDate(LocalDateTime.now().plusDays(7))){ + ErrorPopupMsg.sendErrorMsg(player, "Failed to Update next Tax Date due to internal Error. City was not charged taxes this time."); + return false; + } + try{ + city.getWarehouse().transferResources(player,msg,resources, taxPercent,ruledWarehouse); + }catch(Exception e){ + Logger.info( e.getMessage()); + } + + // Member variable assignment + + ViewResourcesMessage vrm = new ViewResourcesMessage(player); + vrm.setGuild(building.getGuild()); + vrm.setWarehouseBuilding(BuildingManager.getBuildingFromCache(building.getCity().getWarehouse().getBuildingUID())); + vrm.configure(); + Dispatch dispatch = Dispatch.borrow(player, vrm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return true; + + } +} diff --git a/src/engine/objects/Colliders.java b/src/engine/objects/Colliders.java new file mode 100644 index 00000000..cbd68bcf --- /dev/null +++ b/src/engine/objects/Colliders.java @@ -0,0 +1,65 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import java.awt.geom.Line2D; + + +public class Colliders { + + + private Line2D collider; + private int doorID; + private boolean link = false; + public float startX; + public float startY; + public float endX; + public float endY; + + public Colliders(Line2D collider, int doorID, boolean link) { + super(); + this.collider = collider; + this.doorID = doorID; + this.link = link; + } + + public Colliders(float startX, float startY, float endX ,float endY, int doorID, boolean link) { + super(); + this.startX = startX; + this.startY = startY; + this.endX = endX; + this.endY = endY; + this.doorID = doorID; + this.link = link; + } + + + public int getDoorID() { + return doorID; + } + + public Line2D getCollider() { + return collider; + } + + + public boolean isLink() { + return link; + } + + + public void setLink(boolean link) { + this.link = link; + } + + + + +} diff --git a/src/engine/objects/Condemned.java b/src/engine/objects/Condemned.java new file mode 100644 index 00000000..b4ef9744 --- /dev/null +++ b/src/engine/objects/Condemned.java @@ -0,0 +1,98 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.gameManager.DbManager; + +import java.sql.ResultSet; +import java.sql.SQLException; + + + + +public class Condemned { + + private int ID; + private int playerUID; + private int parent; + private int guildUID; + private int friendType; + private boolean active; + public static final int INDIVIDUAL = 2; + public static final int GUILD = 4; + public static final int NATION = 5; + + + + + + /** + * ResultSet Constructor + */ + public Condemned(ResultSet rs) throws SQLException { + this.playerUID = rs.getInt("playerUID"); + this.parent = rs.getInt("buildingUID"); + this.guildUID = rs.getInt("guildUID"); + this.friendType = rs.getInt("friendType"); + this.active = rs.getBoolean("active"); + } + + + + + public Condemned(int playerUID, int parent, int guildUID, int friendType) { + super(); + this.playerUID = playerUID; + this.parent = parent; + this.guildUID = guildUID; + this.friendType = friendType; + this.active = false; + } + + + + + public int getPlayerUID() { + return playerUID; + } + + + public int getParent() { + return parent; + } + + + public int getGuildUID() { + return guildUID; + } + + + public int getFriendType() { + return friendType; + } + + public boolean isActive() { + return active; + } + + + + + public boolean setActive(boolean active) { + if (!DbManager.BuildingQueries.updateActiveCondemn(this, active)) + return false; + this.active = active; + return true; + } + + + + +} diff --git a/src/engine/objects/Contract.java b/src/engine/objects/Contract.java new file mode 100644 index 00000000..b4ed0fb9 --- /dev/null +++ b/src/engine/objects/Contract.java @@ -0,0 +1,301 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import ch.claude_martin.enumbitset.EnumBitSet; +import engine.Enum; +import engine.gameManager.DbManager; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; + +public class Contract extends AbstractGameObject { + + private final int contractID; + private final String name; + private final int mobbaseID; + private final int classID; + private final int extraRune; + private final int iconID; + private int vendorID; + private boolean isTrainer; + private VendorDialog vendorDialog; + private ArrayList npcMenuOptions = new ArrayList<>(); + private ArrayList npcModTypeTable = new ArrayList<>(); + private ArrayList npcModSuffixTable = new ArrayList<>(); + private ArrayList itemModTable = new ArrayList<>(); + private ArrayList sellInventory = new ArrayList<>(); + private EnumBitSet allowedBuildings; + + private ArrayList buyItemType = new ArrayList<>(); + private ArrayList buySkillToken = new ArrayList<>(); + private ArrayList buyUnknownToken = new ArrayList<>(); + + public int equipmentSet = 0; + public int inventorySet = 0; + + /** + * No Table ID Constructor + */ + public Contract(int contractID, String name, int mobbaseID, int classID, int dialogID, int iconID, int extraRune) { + super(); + this.contractID = contractID; + this.name = name; + this.mobbaseID = mobbaseID; + this.classID = classID; + this.iconID = iconID; + this.extraRune = extraRune; + this.vendorDialog = VendorDialog.getVendorDialog(dialogID); + setBools(); + } + + /** + * Normal Constructor + */ + public Contract(int contractID, String name, int mobbaseID, int classID, int dialogID, int iconID, int extraRune, int newUUID) { + super(newUUID); + this.contractID = contractID; + this.name = name; + this.mobbaseID = mobbaseID; + this.classID = classID; + this.iconID = iconID; + this.extraRune = extraRune; + this.vendorDialog = VendorDialog.getVendorDialog(dialogID); + setBools(); + } + + + + /** + * ResultSet Constructor + */ + public Contract(ResultSet rs) throws SQLException { + super(rs); + this.contractID = rs.getInt("contractID"); + this.name = rs.getString("name"); + this.mobbaseID = rs.getInt("mobbaseID"); + this.classID = rs.getInt("classID"); + this.extraRune = rs.getInt("extraRune"); + this.vendorDialog = VendorDialog.getVendorDialog(rs.getInt("dialogID")); + this.iconID = rs.getInt("iconID"); + this.vendorID = rs.getInt("vendorID"); + this.allowedBuildings = EnumBitSet.asEnumBitSet(rs.getLong("allowedBuildingTypeID"), Enum.BuildingGroup.class); + this.equipmentSet = rs.getInt("equipSetID"); + this.inventorySet = rs.getInt("inventorySet"); + + try { + String menuoptions = rs.getString("menuoptions"); + + if (!menuoptions.isEmpty()){ + String[] data = menuoptions.split(" "); + for (String data1 : data) { + this.npcMenuOptions.add(Integer.parseInt(data1)); + } + } + + String modtypetable = rs.getString("pTable"); + if (!modtypetable.isEmpty()){ + String[] data = modtypetable.split(" "); + for (String data1 : data) { + this.npcModTypeTable.add(Integer.parseInt(data1)); + } + } + + String suffix = rs.getString("sTable"); + + if (!suffix.isEmpty()){ + String[] data1 = suffix.split(" "); + + for (String data11 : data1) { + this.npcModSuffixTable.add(Integer.parseInt(data11)); + } + } + + String itemMod = rs.getString("itemModTable"); + + if (!itemMod.isEmpty()){ + String[] data2 = itemMod.split(" "); + for (byte i = 0; i < data2.length; i++) { + this.itemModTable.add(Byte.parseByte(data2[i])); + } + + } + + } catch (SQLException | NumberFormatException e) { + Logger.error( "Error when parsing mod tables"); + } + setBools(); + } + + //Specify if trainer, merchant, banker, etc via classID + private void setBools() { + DbManager.ContractQueries.GET_GENERIC_INVENTORY(this); + DbManager.ContractQueries.GET_SELL_LISTS(this); + + this.isTrainer = this.classID > 2499 && this.classID < 3050 || this.classID == 2028; + + } + + /* + * Getters + */ + public int getContractID() { + return this.contractID; + } + + public String getName() { + return this.name; + } + + public int getMobbaseID() { + return this.mobbaseID; + } + + public int getClassID() { + return this.classID; + } + + public int getExtraRune() { + return this.extraRune; + } + + public boolean isTrainer() { + return this.isTrainer; + } + + public int getIconID() { + return this.iconID; + } + + public int getVendorID() { + return this.vendorID; + } + + public VendorDialog getVendorDialog() { + return this.vendorDialog; + } + + public ArrayList getNPCMenuOptions() { + return this.npcMenuOptions; + } + + public ArrayList getNPCModTypeTable() { + return this.npcModTypeTable; + } + + public ArrayList getNpcModSuffixTable() { + return npcModSuffixTable; + } + + public ArrayList getItemModTable() { + return itemModTable; + } + + public ArrayList getSellInventory() { + return this.sellInventory; + } + + public int getPromotionClass() { + if (this.classID < 2504 || this.classID > 2526) + return 0; + return this.classID; + } + + public boolean isRuneMaster() { + return (this.classID == 850); + } + + public boolean isArtilleryCaptain() { + return this.contractID == 839 || this.contractID == 842 ; + } + + + @Override + public void updateDatabase() { + DbManager.ContractQueries.updateDatabase(this); + } + + public EnumBitSet getAllowedBuildings() { + return allowedBuildings; + } + + public ArrayList getBuyItemType() { + return this.buyItemType; + } + + public ArrayList getBuySkillToken() { + return this.buySkillToken; + } + + public ArrayList getBuyUnknownToken() { + return this.buyUnknownToken; + } + + public boolean canSlotinBuilding(Building building) { + + // Need a building to slot in a building! + if (building == null) + return false; + + // Can't slot in anything but a blueprintted building + if (building.getBlueprintUUID() == 0) + return false; + + // No buildings no slotting + if (this.allowedBuildings.size() == 0) + return false; + + // Binary match + return (building.getBlueprint().getBuildingGroup().elementOf(this.allowedBuildings)); + + } + + public int getEquipmentSet() { + return equipmentSet; + } + + public static boolean NoSlots(Contract contract){ + switch(contract.contractID){ + case 830: + case 838: + case 847: + case 860: + case 866: + case 865: + case 1502003: + case 889: + case 890: + case 896: + case 974: + case 1064: + case 1172: + case 1267: + case 1368: + case 1468: + case 1520: + case 1528: + case 1553: + case 1578: + case 1617: + case 1667: + case 1712: + case 893: + case 820: + return true; + + } + + if (contract.isTrainer) + return true; + return false; + } +} diff --git a/src/engine/objects/Corpse.java b/src/engine/objects/Corpse.java new file mode 100644 index 00000000..e9c4c195 --- /dev/null +++ b/src/engine/objects/Corpse.java @@ -0,0 +1,419 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum.GameObjectType; +import engine.Enum.GridObjectType; +import engine.Enum.ItemType; +import engine.InterestManagement.WorldGrid; +import engine.exception.SerializationException; +import engine.gameManager.BuildingManager; +import engine.gameManager.DbManager; +import engine.job.JobContainer; +import engine.job.JobScheduler; +import engine.jobs.RemoveCorpseJob; +import engine.net.ByteBufferWriter; +import engine.net.DispatchMessage; +import engine.net.client.msg.UnloadObjectsMsg; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicInteger; + +public class Corpse extends AbstractWorldObject { + + private static AtomicInteger corpseCounter = new AtomicInteger(0); + + private String firstName; + private String lastName; + private int level; + private int belongsToType; + private int belongsToID; + private ArrayList inventory; + public JobContainer cleanup; + private boolean asciiLastName = true; + private boolean hasGold = false; + private int inBuildingID = 0; + private int inFloorID = -1; + private int inBuilding = -1; + + /** + * No Id Constructor + */ + public Corpse( int newUUID, AbstractCharacter belongsTo, boolean safeZone,boolean enterWorld) { + super(newUUID); + this.setObjectType(); + this.inventory = new ArrayList<>(); + this.gridObjectType = GridObjectType.STATIC; + this.setObjectTypeMask(MBServerStatics.MASK_CORPSE); + if (belongsTo != null) { + this.firstName = belongsTo.getFirstName(); + this.lastName = belongsTo.getLastName(); + this.asciiLastName = belongsTo.asciiLastName(); + this.level = belongsTo.getLevel(); + this.belongsToType = belongsTo.getObjectType().ordinal(); + this.belongsToID = belongsTo.getObjectUUID(); + this.inBuilding = belongsTo.getInBuilding(); + this.inFloorID = belongsTo.getInFloorID(); + this.inBuildingID = belongsTo.getInBuildingID(); + this.setLoc(belongsTo.getLoc()); + } else { + Logger.error("No player passed in for corpse"); + this.firstName = ""; + this.lastName = ""; + this.level = 1; + this.belongsToType = 0; + this.belongsToID = 0; + } + this.setObjectTypeMask(MBServerStatics.MASK_CORPSE); + + if (!safeZone) + transferInventory(belongsTo,enterWorld); + + + } + + public boolean removeItemFromInventory(Item item) { + synchronized (this.inventory) { + if (this.inventory.contains(item)) { + this.inventory.remove(item); + return true; + } + return false; + } + } + + public void transferInventory(AbstractCharacter belongsTo,boolean enterWorld) { + if (belongsTo == null) { + Logger.error( "Can't find player that corpse " + this.getObjectUUID() + " belongs to"); + return; + } + + //TODO transfer items from players inventory and trade window to corpse + CharacterItemManager cim = belongsTo.getCharItemManager(); + if (cim != null) + cim.transferEntireInventory(this.inventory, this,enterWorld); + else + Logger.error( "Can't find inventory for player " + belongsTo.getObjectUUID()); + } + + public static int getNextCorpseCount() { + return Corpse.corpseCounter.addAndGet(2); //newUUID and runeID + } + + //Create a new corpse + public static Corpse makeCorpse(AbstractCharacter belongsTo,boolean enterWorld) { + boolean safeZone = false; + if (belongsTo != null && belongsTo.getObjectType() == GameObjectType.PlayerCharacter) + safeZone = ((PlayerCharacter)belongsTo).isInSafeZone(); + + + + Corpse corpse = new Corpse(Corpse.getNextCorpseCount(), belongsTo, safeZone,enterWorld); + + //create cleanup job + if (corpse != null) { + RemoveCorpseJob rcj = new RemoveCorpseJob(corpse); + corpse.cleanup = JobScheduler.getInstance().scheduleJob(rcj, MBServerStatics.CORPSE_CLEANUP_TIMER_MS); + DbManager.addToCache(corpse); + } + + return corpse; + } + + //Get existing corpse + public static Corpse getCorpse(int newUUID) { + return (Corpse) DbManager.getFromCache(GameObjectType.Corpse, newUUID); + } + + public Item lootItem(Item i, PlayerCharacter looter) { + //make sure looter exists + if (looter == null) + return null; + + //get looters item manager + CharacterItemManager looterItems = looter.getCharItemManager(); + if (looterItems == null) + return null; + + synchronized (this.inventory) { + + //make sure player has item in inventory + if (!this.inventory.contains(i)) + return null; + + //get weight of item + ItemBase ib = i.getItemBase(); + if (ib == null) + return null; + short weight = ib.getWeight(); + + //make sure looter has room for item + if (ib.getType().equals(ItemType.GOLD) == false && !looterItems.hasRoomInventory(weight)) + return null; + + //attempt to transfer item in db + if (ib.getType().equals(ItemType.GOLD)) { + if (!looterItems.moveGoldToInventory(i, i.getNumOfItems())) + return null; + } else if (!i.moveItemToInventory(looter)) + return null; + + //db transfer successful, remove from this character + this.inventory.remove(this.inventory.indexOf(i)); + } + + //add item to looter. + if (!looterItems.addItemToInventory(i)) + return null; + + //calculate new weights + looterItems.calculateInventoryWeight(); + + return i; + } + + + //remove corpse from world + public static void removeCorpse(int newUUID, boolean fromTimer) { + Corpse c = (Corpse) DbManager.getFromCache(GameObjectType.Corpse, newUUID); + if (c == null) + Logger.error( "No corpse found of ID " + newUUID); + else + Corpse.removeCorpse(c, fromTimer); + } + + public static void removeCorpse(Corpse corpse, boolean fromTimer) { + if (corpse == null) + return; + + corpse.purgeInventory(); + + //cleanup timer + if (!fromTimer) { + JobScheduler.getInstance().cancelScheduledJob(corpse.cleanup); + } + corpse.cleanup = null; + + //Remove from world + UnloadObjectsMsg uom = new UnloadObjectsMsg(); + uom.addObject(corpse); + DispatchMessage.sendToAllInRange(corpse, uom); + WorldGrid.RemoveWorldObject(corpse); + + //clear from cache + DbManager.removeFromCache(corpse); + } + + + public static void _serializeForClientMsg(Corpse corpse, ByteBufferWriter writer) + throws SerializationException {} + + public static void _serializeForClientMsg(Corpse corpse, ByteBufferWriter writer, boolean aln) + throws SerializationException { + + Building building = null; + if (corpse.inBuildingID != 0) + building = BuildingManager.getBuildingFromCache(corpse.inBuildingID); + + //Send Rune Count + writer.putInt(0); + writer.putInt(0); + writer.putInt(1); + + //Send Corpse Rune + writer.putInt(1); + writer.putInt(0); + writer.putInt(MBServerStatics.TOMBSTONE); + writer.putInt(corpse.getObjectType().ordinal()); + writer.putInt((corpse.getObjectUUID() + 1)); + + //Send Stats + writer.putInt(5); + writer.putInt(MBServerStatics.STAT_STR_ID); // Strength ID + writer.putInt(5000); + writer.putInt(MBServerStatics.STAT_SPI_ID); // Spirit ID + writer.putInt(0); + writer.putInt(MBServerStatics.STAT_CON_ID); // Constitution ID + writer.putInt(0); + writer.putInt(MBServerStatics.STAT_DEX_ID); // Dexterity ID + writer.putInt(0); + writer.putInt(MBServerStatics.STAT_INT_ID); // Intelligence ID + writer.putInt(0); + + //Send Name + writer.putString(corpse.firstName); + if (aln && !corpse.asciiLastName) + writer.putString(""); + else + writer.putString(corpse.lastName); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.put((byte)1); + + //Send Corpse Info + writer.putInt(0); + writer.putInt(corpse.getObjectType().ordinal()); + writer.putInt((corpse.getObjectUUID())); + writer.putFloat(10f); //FaceDir or scale + writer.putFloat(10); //FaceDir or scale + writer.putFloat(10); //FaceDir or scale + + writer.putFloat(corpse.getLoc().x); + writer.putFloat(corpse.getLoc().y); + writer.putFloat(corpse.getLoc().z); + + writer.putFloat(6.235f); //1.548146f); //w + writer.putInt(0); + writer.putInt(0); + + //Send BelongsToInfo + writer.putInt(((corpse.level / 10))); //Rank + writer.putInt(corpse.level); //Level + writer.putInt(1); + writer.putInt(1); + writer.putInt(1); //Missing this? + writer.putInt(2); + writer.putInt(1); + // writer.putInt(0); //not needed? + writer.putInt(0); + + writer.putInt(corpse.belongsToType); + writer.putInt(corpse.belongsToID); + + writer.putInt(0); + writer.putInt(0); + + + + for (int i=0;i<9;i++) + writer.putInt(0); + writer.putShort((short)0); + writer.put((byte)0); + + //Send Errant Guild Info + for (int i=0;i<13;i++) + writer.putInt(0); + writer.putInt(16); + writer.putInt(16); + writer.putInt(16); + writer.putInt(0); + writer.putInt(0); //Missing this? + writer.putInt(16); + writer.putInt(16); + writer.putInt(16); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + + //Send unknown counter + writer.putInt(1); + writer.putInt(0x047A0E67); //What is this? + writer.put((byte)0); + + //Send unknown + writer.putInt(0); + writer.putInt(0); + writer.putFloat(1293.4449f); //Unknown + writer.putFloat(-100f); //Unknown + writer.putInt(0); + writer.put((byte)0); + + writer.put((byte)0); //End datablock + + } + + + public boolean hasGold() { + return this.hasGold; + } + + public void setHasGold(boolean value) { + this.hasGold = value; + } + + public ArrayList getInventory() { + synchronized(this.inventory) { + return this.inventory; + } + } + + /** + * Delete and remove all items in the inventory + */ + private void purgeInventory() { + //make a copy so we're not inside synchronized{} while waiting for all items to be junked + ArrayList inventoryCopy; + synchronized(this.inventory) { + inventoryCopy = new ArrayList<>(this.inventory); + this.inventory.clear(); + } + + for (Item item : inventoryCopy) { + item.junk(); + } + } + + @Override + public void updateDatabase() { + } + + @Override + public void runAfterLoad() {} + + public int getBelongsToType() { + return this.belongsToType; + } + + public int getBelongsToID() { + return this.belongsToID; + } + + @Override + public String getName() { + if (this.firstName.length() == 0) { + return "Unknown corpse"; + } + if (this.lastName.length() == 0) { + return this.firstName; + } + return this.firstName + ' ' + this.lastName; + } + + public int getInBuilding() { + return inBuilding; + } + + public void setInBuilding(int inBuilding) { + this.inBuilding = inBuilding; + } + + public int getInFloorID() { + return inFloorID; + } + + public void setInFloorID(int inFloorID) { + this.inFloorID = inFloorID; + } +} diff --git a/src/engine/objects/Effect.java b/src/engine/objects/Effect.java new file mode 100644 index 00000000..85f73226 --- /dev/null +++ b/src/engine/objects/Effect.java @@ -0,0 +1,665 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + package engine.objects; + +import engine.Enum.DamageType; +import engine.Enum.EffectSourceType; +import engine.gameManager.PowersManager; +import engine.job.AbstractJob; +import engine.job.AbstractScheduleJob; +import engine.job.JobContainer; +import engine.jobs.AbstractEffectJob; +import engine.jobs.DamageOverTimeJob; +import engine.jobs.NoTimeJob; +import engine.jobs.PersistentAoeJob; +import engine.net.ByteBufferWriter; +import engine.net.client.ClientConnection; +import engine.powers.ActionsBase; +import engine.powers.EffectsBase; +import engine.powers.PowersBase; +import engine.powers.effectmodifiers.AbstractEffectModifier; + +import java.util.HashSet; +import java.util.concurrent.atomic.AtomicBoolean; + + +public class Effect { + + private JobContainer jc; + + //Fail Conditions + private boolean cancelOnAttack; + private boolean cancelOnAttackSwing; + private boolean cancelOnCast; + private boolean cancelOnCastSpell; + private boolean cancelOnEquipChange; + private boolean cancelOnLogout; + private boolean cancelOnMove; + private boolean cancelOnNewCharm; + private boolean cancelOnSit; + private boolean cancelOnTakeDamage; + private boolean cancelOnTerritoryClaim; + private boolean cancelOnUnEquip; + private boolean cancelOnStun; + private boolean bakedInStat = false; + private boolean isStatic = false; + private int effectSourceType = 0; + private int effectSourceID = 0; + + + private EffectsBase eb; + private int trains; + private float damageAmount = 0f; + + private AtomicBoolean cancel = new AtomicBoolean(false); + + //private AbstractWorldObject owner; + + /** + * Generic Constructor + */ + public Effect(JobContainer jc, EffectsBase eb, int trains) { + this.jc = jc; + this.cancelOnAttack = false; + this.cancelOnAttackSwing = false; + this.cancelOnCast = false; + this.cancelOnCastSpell = false; + this.cancelOnEquipChange = false; + this.cancelOnLogout = false; + this.cancelOnMove = false; + this.cancelOnNewCharm = false; + this.cancelOnSit = false; + this.cancelOnTakeDamage = false; + this.cancelOnTerritoryClaim = false; + this.cancelOnUnEquip = false; + this.cancelOnStun = false; + this.eb = eb; + this.trains = trains; + } + + public Effect(JobContainer jc, EffectsBase eb, int trains,boolean isStatic) { + this.jc = jc; + this.cancelOnAttack = false; + this.cancelOnAttackSwing = false; + this.cancelOnCast = false; + this.cancelOnCastSpell = false; + this.cancelOnEquipChange = false; + this.cancelOnLogout = false; + this.cancelOnMove = false; + this.cancelOnNewCharm = false; + this.cancelOnSit = false; + this.cancelOnTakeDamage = false; + this.cancelOnTerritoryClaim = false; + this.cancelOnUnEquip = false; + this.cancelOnStun = false; + this.eb = eb; + this.trains = trains; + this.isStatic = isStatic; + } + + //called when effect ends. Send message to client to remove effect + public void endEffect() { + if (this.jc != null) { + AbstractJob aj = jc.getJob(); + if (aj == null) + return; + if (aj instanceof AbstractEffectJob) { + ((AbstractEffectJob)aj).setSkipCancelEffect(false); + ((AbstractEffectJob)aj).endEffect(); + } + } + } + + public void endEffectNoPower() { + if (this.jc != null) { + AbstractJob aj = jc.getJob(); + if (aj == null) + return; + if (aj instanceof AbstractEffectJob) { + ((AbstractEffectJob)aj).setSkipCancelEffect(false); + ((AbstractEffectJob)aj).endEffectNoPower(); + } + } + } + + //Called when effect ends before timer done + public void cancelJob() { + if (this.jc != null) { + AbstractJob aj = jc.getJob(); + if (aj == null) + return; + if (aj instanceof AbstractEffectJob) + ((AbstractEffectJob)aj).setSkipCancelEffect(false); + if (aj instanceof AbstractScheduleJob) { + ((AbstractScheduleJob)aj).cancelJob(); + } + } + } + + public void cancelJob(boolean skipEffect) { + if (this.jc != null) { + AbstractJob aj = jc.getJob(); + if (aj == null) + return; + if (skipEffect && aj instanceof AbstractEffectJob) { + ((AbstractEffectJob)aj).setSkipCancelEffect(skipEffect); + } + if (aj instanceof AbstractScheduleJob) { + ((AbstractScheduleJob)aj).cancelJob(); + } + } + } + + public boolean applyBonus(Item item) { + if (this.jc == null) + return false; + AbstractJob aj = jc.getJob(); + if (aj == null) + return false; + if (aj instanceof AbstractEffectJob) { + AbstractEffectJob aej = (AbstractEffectJob)aj; + EffectsBase eb = aej.getEffect(); + if (eb == null) + return false; + HashSet aems = eb.getModifiers(); + for(AbstractEffectModifier aem : aems) + aem.applyBonus(item, aej.getTrains()); + return true; + } + return false; + } + + public boolean applyBonus(Building building) { + if (this.jc == null) + return false; + AbstractJob aj = jc.getJob(); + if (aj == null) + return false; + if (aj instanceof AbstractEffectJob) { + AbstractEffectJob aej = (AbstractEffectJob)aj; + EffectsBase eb = aej.getEffect(); + if (eb == null) + return false; + HashSet aems = eb.getModifiers(); + for(AbstractEffectModifier aem : aems) + aem.applyBonus(building, aej.getTrains()); + return true; + } + return false; + } + + public boolean applyBonus(AbstractCharacter ac) { + if (this.jc == null) + return false; + AbstractJob aj = jc.getJob(); + if (aj == null) + return false; + if (aj instanceof AbstractEffectJob) { + AbstractEffectJob aej = (AbstractEffectJob)aj; + EffectsBase eb = aej.getEffect(); + if (eb == null) + return false; + HashSet aems = eb.getModifiers(); + for(AbstractEffectModifier aem : aems) + aem.applyBonus(ac, aej.getTrains()); + return true; + } + return false; + } + + public boolean applyBonus(Item item, AbstractCharacter ac) { + if (this.jc == null) + return false; + AbstractJob aj = jc.getJob(); + if (aj == null) + return false; + if (aj instanceof AbstractEffectJob) { + AbstractEffectJob aej = (AbstractEffectJob)aj; + EffectsBase eb = aej.getEffect(); + if (eb == null) + return false; + HashSet aems = eb.getModifiers(); + for(AbstractEffectModifier aem : aems) { + aem.applyBonus(item, aej.getTrains()); + aem.applyBonus(ac, aej.getTrains()); + } + return true; + } + return false; + } + + public HashSet getEffectModifiers() { + if (this.jc == null) + return null; + AbstractJob aj = jc.getJob(); + if (aj == null) + return null; + if (aj instanceof AbstractEffectJob) { + AbstractEffectJob aej = (AbstractEffectJob)aj; + EffectsBase eb = aej.getEffect(); + if (eb == null) + return null; + return eb.getModifiers(); + } + return null; + } + + + //Send this effect to a client when loading a player + public void sendEffect(ClientConnection cc) { + if (this.jc == null || this.eb == null || cc == null) + return; + AbstractJob aj = this.jc.getJob(); + if (aj == null || (!(aj instanceof AbstractEffectJob))) + return; + this.eb.sendEffect((AbstractEffectJob)aj, (this.jc.timeToExecutionLeft() / 1000), cc); + } + + public void sendEffectNoPower(ClientConnection cc) { + if (this.jc == null || this.eb == null || cc == null) + return; + AbstractJob aj = this.jc.getJob(); + if (aj == null || (!(aj instanceof AbstractEffectJob))) + return; + this.eb.sendEffectNoPower((AbstractEffectJob)aj, (this.jc.timeToExecutionLeft() / 1000), cc); + } + + public void sendSpireEffect(ClientConnection cc, boolean onEnter) { + if (this.jc == null || this.eb == null || cc == null) + return; + AbstractJob aj = this.jc.getJob(); + if (aj == null || (!(aj instanceof AbstractEffectJob))) + return; + int duration = 45; + if (onEnter) + duration = -1; + this.eb.sendEffectNoPower((AbstractEffectJob)aj, duration, cc); + } + + public void serializeForItem(ByteBufferWriter writer, Item item) { + if (this.jc == null) { + blankFill(writer); + return; + } + AbstractJob aj = this.jc.getJob(); + if (aj == null || (!(aj instanceof AbstractEffectJob))) { + blankFill(writer); + return; + } + AbstractEffectJob aej = (AbstractEffectJob)aj; + PowersBase pb = aej.getPower(); + ActionsBase ab = aej.getAction(); + if (this.eb == null) { + blankFill(writer); + return; + } else if (pb == null && !(this.jc.noTimer())) { + blankFill(writer); + return; + } + if (this.jc.noTimer()) { + if (pb == null) + writer.putInt(this.eb.getToken()); + else + writer.putInt(pb.getToken()); + writer.putInt(aej.getTrains()); + writer.putInt(1); + writer.put((byte)1); + writer.putInt(item.getObjectType().ordinal()); + writer.putInt(item.getObjectUUID()); + + writer.putString(item.getName()); + writer.putFloat(-1000f); + } else { + float duration = this.jc.timeToExecutionLeft() / 1000; + writer.putInt(this.eb.getToken()); + writer.putInt(aej.getTrains()); + writer.putInt(0); + writer.put((byte)0); + writer.putInt(pb.getToken()); + writer.putString(pb.getName()); + writer.putFloat(duration); + } + } + + public void serializeForClientMsg(ByteBufferWriter writer) { + AbstractJob aj = this.jc.getJob(); + if (aj == null || (!(aj instanceof AbstractEffectJob))) { + //TODO put error message here + blankFill(writer); + return; + } + AbstractEffectJob aej = (AbstractEffectJob)aj; + PowersBase pb = aej.getPower(); + ActionsBase ab = aej.getAction(); + if (ab == null || pb == null || this.eb == null) { + //TODO put error message here + blankFill(writer); + return; + } + + if ( aej instanceof PersistentAoeJob){ + blankFill(writer); + return; + } + + float duration = this.jc.timeToExecutionLeft() / 1000; + if (aej instanceof DamageOverTimeJob) + duration = ab.getDurationInSeconds(aej.getTrains()) - (((DamageOverTimeJob)aej).getIteration()*5); + + + + writer.putInt(pb.getToken()); + writer.putInt(aej.getTrains()); + writer.putInt(0); + writer.put((byte)0); + writer.putInt(this.eb.getToken()); + writer.putString(pb.getName()); + writer.putFloat(duration); + } + + public boolean serializeForLoad(ByteBufferWriter writer) { + AbstractJob aj = this.jc.getJob(); + if (aj == null || (!(aj instanceof AbstractEffectJob))) { + return false; + } + + + AbstractEffectJob aej = (AbstractEffectJob)aj; + PowersBase pb = aej.getPower(); + ActionsBase ab = aej.getAction(); + if (this.eb == null) { + return false; + } + if ( aej instanceof PersistentAoeJob){ + return false; + } + + + float duration = this.jc.timeToExecutionLeft() / 1000; + if (aej instanceof DamageOverTimeJob) + if (ab != null) + duration = ab.getDurationInSeconds(aej.getTrains()) - (((DamageOverTimeJob)aej).getIteration()*5); + if (aej instanceof NoTimeJob) + duration = -1; + + int sendToken = this.getEffectToken(); + + if (aej.getAction() != null) + if ( aej.getAction().getPowerAction() != null + && PowersManager.ActionTokenByIDString.containsKey(aej.getAction().getPowerAction().getIDString())) + try{ + sendToken = PowersManager.ActionTokenByIDString.get(aej.getAction().getPowerAction().getIDString()); + }catch(Exception e){ + sendToken = this.getEffectToken(); + } + + + writer.putInt(sendToken); + writer.putInt(this.trains); + writer.putInt(0); //? + if (aej.getEffectSourceID() != 0){ + writer.put((byte) 1); + writer.putInt(aej.getEffectSourceType()); + writer.putInt(aej.getEffectSourceID()); + }else{ + writer.put((byte)0); + writer.putInt(pb != null ? pb.getToken() : 0); + } + writer.putString(pb != null ? pb.getName() : eb.getName()); + + writer.putFloat(duration); + return true; + } + + private static void blankFill(ByteBufferWriter writer) { + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.put((byte)0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + } + + public float getDuration() { + float duration = 0f; + if (this.jc != null) + duration = this.jc.timeToExecutionLeft() / 1000; + return duration; + } + + public boolean containsSource(EffectSourceType source) { + if (this.eb != null) + return this.eb.containsSource(source); + return false; + } + + public JobContainer getJobContainer() { + return this.jc; + } + + public int getTrains() { + return this.trains; + } + + public void setTrains(int value) { + this.trains = value; + } + + public float getDamageAmount() { + return this.damageAmount; + } + + public AbstractJob getJob() { + if (this.jc == null) + return null; + return jc.getJob(); + } + + public boolean bakedInStat() { + return this.bakedInStat; + } + + public void setBakedInStat(boolean value) { + this.bakedInStat = value; + } + + public PowersBase getPower() { + if (this.jc == null) + return null; + AbstractJob aj = jc.getJob(); + if (aj == null || (!(aj instanceof AbstractEffectJob))) + return null; + return ((AbstractEffectJob)aj).getPower(); + } + + public int getPowerToken() { + if (this.jc == null) + return 0; + AbstractJob aj = jc.getJob(); + if (aj == null || (!(aj instanceof AbstractEffectJob))) + return 0; + PowersBase pb = ((AbstractEffectJob)aj).getPower(); + if (pb == null) + return 0; + return pb.getToken(); + } + + public int getEffectToken() { + if (this.eb != null) + return this.eb.getToken(); + return 0; + } + + public EffectsBase getEffectsBase() { + return this.eb; + } + + public String getName() { + if (this.jc == null) + return ""; + AbstractJob aj = this.jc.getJob(); + if (aj == null || !(aj instanceof AbstractEffectJob)) + return ""; + AbstractEffectJob aej = (AbstractEffectJob)aj; + PowersBase pb = aej.getPower(); + if (pb == null) + return ""; + return pb.getName(); + } + + public boolean cancel() { + return this.cancel.compareAndSet(false, true); + } + + public boolean canceled() { + return this.cancel.get(); + } + + public boolean cancelOnAttack() { + if (this.eb == null) + return true; + return this.eb.cancelOnAttack(); + } + + public boolean cancelOnAttackSwing() { + if (this.eb == null) + return true; + return this.eb.cancelOnAttackSwing(); + } + + public boolean cancelOnCast() { + if (this.eb == null) + return true; + return this.eb.cancelOnCast(); + } + + public boolean cancelOnCastSpell() { + if (this.eb == null) + return true; + return this.eb.cancelOnCastSpell(); + } + + public boolean cancelOnEquipChange() { + if (this.eb == null) + return true; + return this.eb.cancelOnEquipChange(); + } + + public boolean cancelOnLogout() { + if (this.eb == null) + return true; + return this.eb.cancelOnLogout(); + } + + public boolean cancelOnMove() { + if (this.eb == null || this.cancelOnMove) + return true; + return this.eb.cancelOnMove(); + } + + public boolean cancelOnNewCharm() { + if (this.eb == null) + return true; + return this.eb.cancelOnNewCharm(); + } + + public boolean cancelOnSit() { + if (this.eb == null) + return true; + return this.eb.cancelOnSit(); + } + + public boolean cancelOnStun() { + if (this.eb == null) + return true; + return this.cancelOnStun; + } + + public boolean cancelOnTakeDamage() { + if (this.eb == null) + return true; + if (this.eb.damageTypeSpecific()) { + return false; //handled in call from resists + } else { + return this.eb.cancelOnTakeDamage(); + } + } + + //Used for verifying when damage absorbers fails + public boolean cancelOnTakeDamage(DamageType type, float amount) { + if (!this.eb.cancelOnTakeDamage()) + return false; + if (this.eb == null || amount < 0f) + return false; + if (this.eb.damageTypeSpecific()) { + if (type == null) + return false; + if (this.eb.containsDamageType(type)) { + this.damageAmount += amount; + return this.damageAmount > this.eb.getDamageAmount(this.trains); + } else + return false; + } else + return false; //handled by call from AbstractCharacter + } + + public boolean isDamageAbsorber() { + if (this.eb == null) + return false; + if (!this.eb.cancelOnTakeDamage()) + return false; + return this.eb.damageTypeSpecific(); + } + + public boolean cancelOnTerritoryClaim() { + if (this.eb == null) + return true; + return this.eb.cancelOnTerritoryClaim(); + } + + public boolean cancelOnUnEquip() { + if (this.eb == null) + return true; + return this.eb.cancelOnUnEquip(); + } + + public void setPAOE() { + this.cancelOnStun = true; + this.cancelOnMove = true; + } + + public boolean isStatic() { + return isStatic; + } + + public void setIsStatic(boolean isStatic) { + this.isStatic = isStatic; + + } + + public int getEffectSourceID() { + return effectSourceID; + } + + public void setEffectSourceID(int effectSourceID) { + this.effectSourceID = effectSourceID; + } + + public int getEffectSourceType() { + return effectSourceType; + } + + public void setEffectSourceType(int effectSourceType) { + this.effectSourceType = effectSourceType; + } + + +} \ No newline at end of file diff --git a/src/engine/objects/EffectsResourceCosts.java b/src/engine/objects/EffectsResourceCosts.java new file mode 100644 index 00000000..c5cf7e51 --- /dev/null +++ b/src/engine/objects/EffectsResourceCosts.java @@ -0,0 +1,76 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class EffectsResourceCosts extends AbstractGameObject { + + private String IDString; + private int resourceID; + private int amount; + private int UID; + + /** + * No Table ID Constructor + */ + public EffectsResourceCosts() { + + } + + /** + * ResultSet Constructor + */ + public EffectsResourceCosts(ResultSet rs) throws SQLException { + + this.UID = rs.getInt("UID"); + this.IDString = rs.getString("IDString"); + this.resourceID = rs.getInt("resource"); + this.amount = rs.getInt("amount"); + } + + + + public String getIDString() { + return this.IDString; + } + + + + public int getAmount() { + return this.amount; + } + + + + public int getResourceID() { + return resourceID; + } + + @Override + public void removeFromCache() { + // TODO Auto-generated method stub + + } + + @Override + public void updateDatabase() { + // TODO Auto-generated method stub + + } + + public int getUID() { + return UID; + } + + +} diff --git a/src/engine/objects/EnchantmentBase.java b/src/engine/objects/EnchantmentBase.java new file mode 100644 index 00000000..9ae2e406 --- /dev/null +++ b/src/engine/objects/EnchantmentBase.java @@ -0,0 +1,92 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + package engine.objects; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class EnchantmentBase extends AbstractGameObject { + + private final String name; + private final String prefix; + private final String suffix; + + private final byte attributeID; + private final int modValue; + + /** + * No Table ID Constructor + */ + public EnchantmentBase(String name, String prefix, String suffix, + byte attributeID, int modValue) { + super(); + this.name = name; + this.prefix = prefix; + this.suffix = suffix; + this.attributeID = attributeID; + this.modValue = modValue; + } + + /** + * Normal Constructor + */ + public EnchantmentBase(String name, String prefix, String suffix, + byte attributeID, int modValue, int newUUID) { + super(newUUID); + this.name = name; + this.prefix = prefix; + this.suffix = suffix; + this.attributeID = attributeID; + this.modValue = modValue; + } + + /** + * ResultSet Constructor + */ + public EnchantmentBase(ResultSet rs) throws SQLException { + super(rs); + + this.name = rs.getString("name"); + this.prefix = rs.getString("prefix"); + this.suffix = rs.getString("suffix"); + this.attributeID = rs.getByte("attributeID"); + this.modValue = rs.getInt("modValue"); + + } + + /* + * Getters + */ + public String getName() { + return name; + } + + public String getPrefix() { + return prefix; + } + + public String getSuffix() { + return suffix; + } + + public byte getAttributeID() { + return attributeID; + } + + public int getModValue() { + return modValue; + } + + @Override + public void updateDatabase() { + // TODO Create update logic. + } +} diff --git a/src/engine/objects/EquipmentSetEntry.java b/src/engine/objects/EquipmentSetEntry.java new file mode 100644 index 00000000..b3bdf8a6 --- /dev/null +++ b/src/engine/objects/EquipmentSetEntry.java @@ -0,0 +1,47 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.gameManager.DbManager; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; + +public class EquipmentSetEntry { + + private float dropChance; + private int itemID; + + static HashMap> EquipmentSetMap = new HashMap<>(); + + /** + * ResultSet Constructor + */ + + public EquipmentSetEntry(ResultSet rs) throws SQLException { + this.dropChance = (rs.getFloat("dropChance")); + this.itemID = (rs.getInt("itemID")); + } + + public static void LoadAllEquipmentSets() { + EquipmentSetMap = DbManager.ItemBaseQueries.LOAD_EQUIPMENT_FOR_NPC_AND_MOBS(); + } + + float getDropChance() { + return dropChance; + } + + public int getItemID() { + return itemID; + } + +} diff --git a/src/engine/objects/Experience.java b/src/engine/objects/Experience.java new file mode 100644 index 00000000..c0ae278a --- /dev/null +++ b/src/engine/objects/Experience.java @@ -0,0 +1,441 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum.TargetColor; +import engine.gameManager.ZoneManager; +import engine.math.Vector3fImmutable; +import engine.server.MBServerStatics; + +import java.util.ArrayList; +import java.util.TreeMap; + +public class Experience { + + private static final TreeMap ExpToLevel; + private static final int[] LevelToExp = { Integer.MIN_VALUE, // Pad + // everything + // over 1 + + // R0 + 0, // Level 1 + 150, // Level 2 + 1200, // Level 3 + 4050, // Level 4 + 9600, // Level 5 + 18750, // Level 6 + 32400, // Level 7 + 51450, // Level 8 + 76800, // Level 9 + + // R1 + 109350, // Level 10 + 150000, // Level 11 + 199650, // Level 12 + 259200, // Level 13 + 329550, // Level 14 + 411600, // Level 15 + 506250, // Level 16 + 614400, // Level 17 + 736950, // Level 18 + 874800, // Level 19 + + // R2 + 1028850, // Level 20 + 1200000, // Level 21 + 1389150, // Level 22 + 1597200, // Level 23 + 1825050, // Level 24 + 2073600, // Level 25 + 2343750, // Level 26 + 2636400, // Level 27 + 2952450, // Level 28 + 3292800, // Level 29 + + // R3 + 3658350, // Level 30 + 4050000, // Level 31 + 4468650, // Level 32 + 4915200, // Level 33 + 5390550, // Level 34 + 5895600, // Level 35 + 6431250, // Level 36 + 6998400, // Level 37 + 7597950, // Level 38 + 8230800, // Level 39 + + // R4 + 8897850, // Level 40 + 10091520, // Level 41 + 11396777, // Level 42 + 12820187, // Level 43 + 14368505, // Level 44 + 16048666, // Level 45 + 17867790, // Level 46 + 19833183, // Level 47 + 21952335, // Level 48 + 24232919, // Level 49 + + // R5 + 26682793, // Level 50 + 29310000, // Level 51 + 32122766, // Level 52 + 35129502, // Level 53 + 38338805, // Level 54 + 41759452, // Level 55 + 45400409, // Level 56 + 49270824, // Level 57 + 53380030, // Level 58 + 57737542, // Level 59 + + // R6 + 62353064, // Level 60 + 67236479, // Level 61 + 72397859, // Level 62 + 77847457, // Level 63 + 83595712, // Level 64 + 89653247, // Level 65 + 96030869, // Level 66 + 102739569, // Level 67 + 109790524, // Level 68 + 117195093, // Level 69 + + // R7 + 124964822, // Level 70 + 133111438, // Level 71 + 141646855, // Level 72 + 150583171, // Level 73 + 159932666, // Level 74 + 169707808, // Level 75 + 179921247, // Level 76 + + }; + + private static final float[] MaxExpPerLevel = { Float.MIN_VALUE, // Pad + // everything + // over + // 1 + + // R0 + 15, // Level 1 + 105, // Level 2 + 285, // Level 3 + 555, // Level 4 + 610, // Level 5 + 682.5f, // Level 6 + 730, // Level 7 + 975, // Level 8 + 1251.92f, // Level 9 + + // R1 + 1563.46f, // Level 10 + 1909.62f, // Level 11 + 2290.38f, // Level 12 + 2705.77f, // Level 13 + 3155.77f, // Level 14 + 3640.38f, // Level 15 + 4159.62f, // Level 16 + 4713.46f, // Level 17 + 5301.92f, // Level 18 + 5925, // Level 19 + + // R2 + 6582.69f, // Level 20 + 7275, // Level 21 + 8001.92f, // Level 22 + 8763.46f, // Level 23 + 9559.62f, // Level 24 + 10390.38f, // Level 25 + 11255.77f, // Level 26 + 12155.77f, // Level 27 + 13090.38f, // Level 28 + 14059.62f, // Level 29 + + // R3 + 15063.46f, // Level 30 + 16101.92f, // Level 31 + 17175, // Level 32 + 18282.69f, // Level 33 + 19425, // Level 34 + 20601.92f, // Level 35 + 21813.46f, // Level 36 + 23059.62f, // Level 37 + 24340.38f, // Level 38 + 25655.77f, // Level 39 + + // R4 + 45910.38f, // Level 40 + 34348.87f, // Level 41 + 37458.16f, // Level 42 + 40745.21f, // Level 43 + 44214.76f, // Level 44 + 47871.68f, // Level 45 + 51720.87f, // Level 46 + 55767.16f, // Level 47 + 60015.37f, // Level 48 + 64470.37f, // Level 49 + + // R5 + 69137.03f, // Level 50 + 74020.16f, // Level 51 + 79124.63f, // Level 52 + 84455.34f, // Level 53 + 90017.03f, // Level 54 + 95814.66f, // Level 55 + 101853.03f, // Level 56 + 108137, // Level 57 + 114671.37f, // Level 58 + 121461.11f, // Level 59 + + // R6 + 128510.92f, // Level 60 + 135825.79f, // Level 61 + 143410.47f, // Level 62 + 151269.87f, // Level 63 + 159408.82f, // Level 64 + 167832.16f, // Level 65 + 176544.74f, // Level 66 + 185551.45f, // Level 67 + 194857.08f, // Level 68 + 204466.55f, // Level 69 + + // R7 + 214384.63f, // Level 70 + 224616.24f, // Level 71 + 235166.21f, // Level 72 + 246039.34f, // Level 73 + 257240.58f, // Level 74 + 1 // 268774.71 //Level 75 + + }; + + static { + ExpToLevel = new TreeMap<>(); + + // flip keys and values for other Map + for (int i = 1; i < LevelToExp.length; i++) { + ExpToLevel.put(LevelToExp[i], i); + } + } // end Static block + + // Used to calcuate the amount of experience a monster grants in the + // following formula + // expGranted = a(moblevel)^2 + b(moblevel) + c + private static final float EXPQUADA = 10.0f; + private static final float EXPQUADB = 6.0f; + private static final float EXPQUADC = -10.0f; + + // Adds addtional exp per addtional member of a group using the following + // (expGranted / group.size()) * (groupBonus * (group.size()-1) +1) + private static final float GROUP_BONUS = 0.5f; // 0.2 grants (20%) addtional + // exp per group member + + // called to determine current level based on xp + public static int getLevel(int experience) { + int expKey = ExpToLevel.floorKey(experience); + int level = ExpToLevel.get(expKey); + if (level > MBServerStatics.LEVELCAP) { + level = MBServerStatics.LEVELCAP; + } + return level; + } + + // Get the base xp for a level + public static int getBaseExperience(int level) { + if (level < LevelToExp.length) { + return LevelToExp[level]; + } + + int fLevel = level - 1; + int baseXP = fLevel * fLevel * fLevel; + return (int) ((fLevel < 40) ? (baseXP * 150) + : (baseXP * (150 + (7.6799998 * (level - 40))))); + } + + // Get XP needed for the next level + public static int getExpForNextLevel(int experience, int currentLevel) { + return (getBaseExperience(currentLevel + 1) - experience); + } + + // Max XP granted for killing a blue, yellow or orange mob + public static float maxXPPerKill(int level) { + if (level < 1) + level = 1; + if (level > 75) + level = 75; + return MaxExpPerLevel[level]; + // return (LevelToExp[level + 1] - LevelToExp[level])/(11 + level/2); + // return ((((level * level)-level)*50)+16); + } + + // Returns a penalty modifier depending on mob color + public static double getConMod(AbstractCharacter pc, AbstractCharacter mob) { + switch (TargetColor.getCon(pc, mob)) { + case Red: + return 1.25; + case Orange: + return 1.15; + case Yellow: + return 1.05; + case Blue: + return 1; + case Cyan: + return 0.8; + case Green: + return 0.5; + default: + return 0; + } + } + + public static double getGroupMemberPenalty(double leadership, + PlayerCharacter currPlayer, ArrayList players, + int highestLevel) { + + double penalty = 0.0; + int adjustedGroupSize = 0; + int totalLevels = 0; + int level = currPlayer.getLevel(); + + // Group Size Penalty + if (players.size() > 2) + penalty = (players.size() - 2) * 1.5; + + // Calculate Penalty For Highest level -> Current Player difference, != + // check to prevent divide by zero error + if (highestLevel != level) + penalty += ((highestLevel - level) * .5); + + // double avgLevels = totalLevels / adjustedGroupSize; + // if (adjustedGroupSize >= 1) + // if (level < avgLevels) + // penalty += ((avgLevels - level) * .5); + + // Extra noob penalty + if ((highestLevel - level) > 25) + penalty += (highestLevel - level - 25); + + return penalty; + } + + public static void doExperience(PlayerCharacter killer, AbstractCharacter mob, Group g) { + // Check for some failure conditions + if (killer == null || mob == null) + return; + + double xp = 0.0; + + //Get the xp modifier for the world + float xpMod = MBServerStatics.EXP_RATE_MOD; + + + + if (g != null) { // Do group EXP stuff + + int leadership = 0; + int highestLevel = 0; + double penalty = 0.0; + + ArrayList giveEXPTo = new ArrayList<>(); + + // Check if leader is within range of kill and then get leadership + // skill + Vector3fImmutable killLoc = mob.getLoc(); + if (killLoc.distanceSquared2D(g.getGroupLead().getLoc()) < (MBServerStatics.EXP_RANGE * MBServerStatics.EXP_RANGE)) { + CharacterSkill leaderskill = g.getGroupLead().skills + .get("Leadership"); + if (leaderskill != null) + leadership = leaderskill.getNumTrains(); + if (leadership > 90) + leadership = 90; // leadership caps at 90% + } + + // Check every group member for distance to see if they get xp + for (PlayerCharacter pc : g.getMembers()) { + if (pc.isAlive()) { // Skip if the player is dead. + + // Check within range + if (killLoc.distanceSquared2D(pc.getLoc()) < (MBServerStatics.EXP_RANGE * MBServerStatics.EXP_RANGE)) { + giveEXPTo.add(pc); + // Track highest level character + if (pc.getLevel() > highestLevel) + highestLevel = pc.getLevel(); + } + } + } + + // Process every player in the group getting XP + for (PlayerCharacter pc : giveEXPTo) { + if (pc.getLevel() >= MBServerStatics.LEVELCAP) + continue; + + // Sets Max XP with server exp mod taken into account. + xp = (double) xpMod * maxXPPerKill(pc.getLevel()); + + // Adjust XP for Mob Level + xp *= getConMod(pc, mob); + + // Process XP for this member + penalty = getGroupMemberPenalty(leadership, pc, giveEXPTo, + highestLevel); + + // Leadership Penalty Reduction + if (leadership > 0) + penalty -= ((leadership) * 0.01) * penalty; + + // Modify for hotzone + if (xp != 0) + if (ZoneManager.inHotZone(mob.getLoc())) + xp *= MBServerStatics.HOT_EXP_RATE_MOD; + + // Check for 0 XP due to white mob, otherwise subtract penalty + // xp + if (xp == 0) { + xp = 1; + } else { + xp -= (penalty * 0.01) * xp; + + // Errant Penalty Calculation + if (pc.getGuild().isErrant()) + xp *= 0.6; + } + + if (xp == 0) + xp = 1; + + // Grant the player the EXP + pc.grantXP((int) Math.floor(xp)); + } + + } else { // Give EXP to a single character + if (!killer.isAlive()) // Skip if the player is dead. + return; + + if (killer.getLevel() >= MBServerStatics.LEVELCAP) + return; + + // Get XP and adjust for Mob Level with world xp modifier taken into account + xp = (double) xpMod * maxXPPerKill(killer.getLevel()); + xp *= getConMod(killer, mob); + + // Modify for hotzone + if (ZoneManager.inHotZone(mob.getLoc())) + xp *= MBServerStatics.HOT_EXP_RATE_MOD; + + // Errant penalty + if (xp != 1) + if (killer.getGuild().isErrant()) + xp *= .6; + + // Grant XP + killer.grantXP((int) Math.floor(xp)); + } + } +} diff --git a/src/engine/objects/Formation.java b/src/engine/objects/Formation.java new file mode 100644 index 00000000..28d147a1 --- /dev/null +++ b/src/engine/objects/Formation.java @@ -0,0 +1,118 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.math.Vector3f; + +public class Formation { + + // Offsets are as follows. + // X determines left/right offset + // Y not used + // Z determines front/back offset + + private static final Vector3f[] COLUMN = { new Vector3f(0, 0, 0), // Group + // Lead + new Vector3f(6, 0, 0), // Player 1 offset + new Vector3f(0, 0, -6), // Player 2 offset + new Vector3f(6, 0, -6), // Player 3 offset + new Vector3f(0, 0, -12), // Player 4 offset + new Vector3f(6, 0, -12), // Player 5 offset + new Vector3f(0, 0, -18), // Player 6 offset + new Vector3f(6, 0, -18), // Player 7 offset + new Vector3f(0, 0, -24), // Player 8 offset + new Vector3f(6, 0, -24) }; // Player 9 offset + + private static final Vector3f[] LINE = { new Vector3f(0, 0, 0), + new Vector3f(0, 0, -6), new Vector3f(0, 0, -12), + new Vector3f(0, 0, -18), new Vector3f(0, 0, -24), + new Vector3f(0, 0, -30), new Vector3f(0, 0, -36), + new Vector3f(0, 0, -42), new Vector3f(0, 0, -48), + new Vector3f(0, 0, -54) }; + + private static final Vector3f[] BOX = { new Vector3f(0, 0, 0), + new Vector3f(-6, 0, 0), new Vector3f(6, 0, 0), + new Vector3f(-6, 0, -6), new Vector3f(0, 0, -6), + new Vector3f(6, 0, -6), new Vector3f(-6, 0, -12), + new Vector3f(0, 0, -12), new Vector3f(5, 0, -12), + new Vector3f(0, 0, -18) }; + + private static final Vector3f[] TRIANGLE = { new Vector3f(0, 0, 0), + new Vector3f(-6, 0, -6), new Vector3f(6, 0, -6), + new Vector3f(-12, 0, -12), new Vector3f(0, 0, -12), + new Vector3f(12, 0, -12), new Vector3f(-18, 0, -18), + new Vector3f(-6, 0, -18), new Vector3f(6, 0, -18), + new Vector3f(18, 0, -18) }; + + private static final Vector3f[] CIRCLE = { new Vector3f(0, 0, 0), + new Vector3f(-12, 0, -3), new Vector3f(12, 0, -3), + new Vector3f(-18, 0, -12), new Vector3f(18, 0, -12), + new Vector3f(-18, 0, -21), new Vector3f(18, 0, -21), + new Vector3f(-12, 0, -30), new Vector3f(12, 0, -30), + new Vector3f(0, 0, -33) }; + + private static final Vector3f[] RANKS = { new Vector3f(0, 0, 0), + new Vector3f(0, 0, -6), new Vector3f(-6, 0, 0), + new Vector3f(-6, 0, -6), new Vector3f(6, 0, 0), + new Vector3f(6, 0, -6), new Vector3f(-12, 0, 0), + new Vector3f(-12, 0, -6), new Vector3f(12, 0, 0), + new Vector3f(12, 0, -6) }; + + private static final Vector3f[] WEDGE = { new Vector3f(0, 0, 0), + new Vector3f(6, 0, 0), new Vector3f(-6, 0, -6), + new Vector3f(12, 0, -6), new Vector3f(-12, 0, -12), + new Vector3f(18, 0, -12), new Vector3f(-18, 0, -18), + new Vector3f(24, 0, -18), new Vector3f(-24, 0, -24), + new Vector3f(30, 0, -24) }; + + private static final Vector3f[] INVERSEWEDGE = { new Vector3f(0, 0, 0), + new Vector3f(6, 0, 0), new Vector3f(-6, 0, 6), + new Vector3f(12, 0, 6), new Vector3f(-12, 0, 12), + new Vector3f(18, 0, 12), new Vector3f(-18, 0, 18), + new Vector3f(24, 0, 18), new Vector3f(-24, 0, 24), + new Vector3f(30, 0, 24) }; + + private static final Vector3f[] T = { new Vector3f(0, 0, 0), + new Vector3f(-6, 0, 0), new Vector3f(6, 0, 0), + new Vector3f(0, 0, -6), new Vector3f(-12, 0, 0), + new Vector3f(12, 0, 0), new Vector3f(0, 0, -12), + new Vector3f(-18, 0, 0), new Vector3f(18, 0, 0), + new Vector3f(0, 0, -18) }; + + public static Vector3f getOffset(int formation, int position) { + if (position > 9 || position < 0) { + // TODO log error here + position = 0; + } + + switch (formation) { + case 0: + return Formation.COLUMN[position]; + case 1: + return Formation.LINE[position]; + case 2: + return Formation.BOX[position]; + case 3: + return Formation.TRIANGLE[position]; + case 4: + return Formation.CIRCLE[position]; + case 5: + return Formation.RANKS[position]; + case 6: + return Formation.WEDGE[position]; + case 7: + return Formation.INVERSEWEDGE[position]; + case 9: + return Formation.T[position]; + default: // default to box + return Formation.BOX[position]; + } + } +} diff --git a/src/engine/objects/Group.java b/src/engine/objects/Group.java new file mode 100644 index 00000000..934deb78 --- /dev/null +++ b/src/engine/objects/Group.java @@ -0,0 +1,182 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.gameManager.GroupManager; +import engine.job.JobScheduler; +import engine.jobs.UpdateGroupJob; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.msg.group.GroupUpdateMsg; +import engine.server.MBServerStatics; + +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + + +public class Group extends AbstractWorldObject { + + private PlayerCharacter groupLead; + public final Set members; + + private boolean splitGold = true; + private int formation = 2; + + private UpdateGroupJob updateGroupJob = null; + + /** + * No Id Constructor + */ + public Group( PlayerCharacter pc) { + super(); + this.groupLead = pc; + this.members = Collections.newSetFromMap(new ConcurrentHashMap<>()); + } + + /** + * Normal Constructor + */ + public Group( PlayerCharacter pc, int newUUID) { + super(newUUID); + this.groupLead = pc; + this.members = Collections.newSetFromMap(new ConcurrentHashMap<>()); + } + + /* + * Getters + */ + public PlayerCharacter getGroupLead() { + return this.groupLead; + } + + public Set getMembers() { + return this.members; + } + + public boolean getSplitGold() { + return this.splitGold; + } + + public int getFormation() { + return this.formation; + } + + public String getFormationName() { + return MBServerStatics.FORMATION_NAMES[this.formation]; + } + + /* + * Setters + */ + public void setFormation(int value) { + if (value < 0 || value > 8) + value = 2; // Default Box + this.formation = value; + } + + public boolean setGroupLead(int ID) { + for (PlayerCharacter pc : this.members) { + if (pc.getObjectUUID() == ID) { + this.groupLead = pc; + return true; + } + } + return false; + } + + public void setSplitGold(boolean value) { + this.splitGold = value; + } + + /* + * Utils + */ + public boolean isGroupLead(int ID) { + return (this.groupLead.getObjectUUID() == ID); + } + + public boolean isGroupLead(PlayerCharacter pc) { + if (pc == null || this.groupLead == null) + return false; + return (this.groupLead.getObjectUUID() == pc.getObjectUUID()); + } + + public boolean toggleSplitGold() { + this.splitGold = this.splitGold == false; + return this.splitGold; + } + + public void sendUpdate(GroupUpdateMsg msg) { + + for (PlayerCharacter pc : this.members) { + Dispatch dispatch = Dispatch.borrow(pc, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + } + } + + public boolean addGroupMember(PlayerCharacter pc) { + + if (this.members.size() > 9) // group full + return false; + + if (this.members.contains(pc)) // Can't add player twice + return false; + + this.members.add(pc); + return true; + } + + public int removeGroupMember(PlayerCharacter pc) { + + this.members.remove(pc); // remove player + return this.members.size(); + } + + public void clearMembers() { + this.members.clear(); + } + + public static boolean sameGroup(PlayerCharacter a, PlayerCharacter b) { + + if (a == null || b == null) + return false; + + Group aG = GroupManager.getGroup(a); + Group bG = GroupManager.getGroup(b); + + if (aG == null || bG == null) + return false; + + return aG.getObjectUUID() == bG.getObjectUUID(); + + } + + public void addUpdateGroupJob() { + this.updateGroupJob = new UpdateGroupJob(this); + JobScheduler.getInstance().scheduleJob(this.updateGroupJob, MBServerStatics.UPDATE_GROUP_RATE); + } + + public void removeUpdateGroupJob() { + this.updateGroupJob.cancelJob(); + this.updateGroupJob = null; + } + + /* + * Database + */ + @Override + public void updateDatabase() { + // TODO Create update logic. + } + + @Override + public void runAfterLoad() {} +} diff --git a/src/engine/objects/Guild.java b/src/engine/objects/Guild.java new file mode 100644 index 00000000..e28009af --- /dev/null +++ b/src/engine/objects/Guild.java @@ -0,0 +1,1297 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum; +import engine.Enum.*; +import engine.db.archive.DataWarehouse; +import engine.db.archive.GuildRecord; +import engine.db.handlers.dbGuildHandler; +import engine.gameManager.*; +import engine.net.ByteBufferWriter; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.msg.AllianceChangeMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.UpdateClientAlliancesMsg; +import engine.net.client.msg.guild.GuildInfoMsg; +import engine.server.MBServerStatics; +import org.joda.time.DateTime; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; + +public class Guild extends AbstractWorldObject { + + private final String name; + private Guild nation; + private static Guild g; + private final GuildTag guildTag; + // TODO add these to database + private String motto = ""; + private String motd = ""; + private String icmotd = ""; + private String nmotd = ""; + private int guildLeaderUUID; + private int realmsOwned; + private final int charter; + private int cityUUID = 0; + private final String leadershipType; // Have to see how this is sent to the client + private final int repledgeMin; + private final int repledgeMax; + private final int repledgeKick; + private final int teleportMin; + private final int teleportMax; + private int mineTime; + private ArrayList banishList; + private ArrayList characterKOSList; + private ArrayList guildKOSList; + private ArrayList allyList = new ArrayList<>(); + private ArrayList enemyList = new ArrayList<>(); + private ArrayList recommendList = new ArrayList<>(); + private ArrayList subGuildList; + private int nationUUID = 0; + private GuildState guildState = GuildState.Errant; + private ConcurrentHashMap guildCondemned = new ConcurrentHashMap<>(); + private String hash; + private boolean ownerIsNPC; + + public HashMap guildAlliances = new HashMap<>(); + + /** + * No Id Constructor + */ + public Guild(String name, Guild nat, int charter, + String leadershipType, GuildTag gt, String motto) { + super(); + this.name = name; + this.nation = nat; + this.charter = charter; + this.realmsOwned = 0; + this.leadershipType = leadershipType; + + this.banishList = new ArrayList<>(); + this.characterKOSList = new ArrayList<>(); + this.guildKOSList = new ArrayList<>(); + this.allyList = new ArrayList<>(); + this.enemyList = new ArrayList<>(); + this.subGuildList = new ArrayList<>(); + + this.guildTag = gt; + + //set for player city + this.repledgeMin = 1; + this.repledgeMax = 100; + this.repledgeKick = 100; + this.teleportMin = 1; + this.teleportMax = 100; + this.mineTime = 0; + this.motto = motto; + } + + /** + * Normal Constructor + */ + public Guild( String name, Guild nat, int charter, + String leadershipType, GuildTag gt, int newUUID) { + super(newUUID); + this.name = name; + this.nation = nat; + + this.charter = charter; + this.realmsOwned = 0; + this.leadershipType = leadershipType; + + this.banishList = new ArrayList<>(); + this.characterKOSList = new ArrayList<>(); + this.guildKOSList = new ArrayList<>(); + this.allyList = new ArrayList<>(); + this.enemyList = new ArrayList<>(); + this.subGuildList = new ArrayList<>(); + this.guildTag = gt; + + //set for player city + this.repledgeMin = 1; + this.repledgeMax = 100; + this.repledgeKick = 100; + this.teleportMin = 1; + this.teleportMax = 100; + this.mineTime = 0; + this.hash = "ERRANT"; + } + + /** + * ResultSet Constructor + */ + public Guild(ResultSet rs) throws SQLException { + super(rs); + DbObjectType objectType; + + this.name = rs.getString("name"); + this.charter = rs.getInt("charter"); + this.leadershipType = rs.getString("leadershipType"); + + this.guildTag = new GuildTag(rs.getInt("backgroundColor01"), + rs.getInt("backgroundColor02"), + rs.getInt("symbolColor"), + rs.getInt("symbol"), + rs.getInt("backgroundDesign")); + + //Declare Nations and Subguilds + this.nationUUID = rs.getInt("parent"); + this.cityUUID = rs.getInt("ownedCity"); + this.guildLeaderUUID = rs.getInt("leaderUID"); + this.motto = rs.getString("motto"); + this.motd = rs.getString("motd"); + this.icmotd = rs.getString("icMotd"); + this.nmotd = rs.getString("nationMotd"); + + this.repledgeMin = rs.getInt("repledgeMin"); + this.repledgeMax = rs.getInt("repledgeMax"); + this.repledgeKick = rs.getInt("repledgeKick"); + this.teleportMin = rs.getInt("teleportMin"); + this.teleportMax = rs.getInt("teleportMax"); + + this.mineTime = rs.getInt("mineTime"); + this.hash = rs.getString("hash"); + + } + + public void setNation(Guild nation) { + if (nation == null) + this.nation = Guild.getErrantGuild(); + else + this.nation = nation; + } + + /* + * Getters + */ + @Override + public String getName() { + return name; + } + + public String getLeadershipType() { + return leadershipType; + } + + public Guild getNation() { + + if (this.nation == null) + return Guild.getErrantGuild(); + return this.nation; + } + + public boolean isNation() { + return this.nation != null && this.cityUUID != 0 && this.nation == this; + } + + public City getOwnedCity() { + + return City.getCity(this.cityUUID); + } + + public void setCityUUID(int cityUUID) { + this.cityUUID = cityUUID; + } + + public ArrayList getBanishList() { + if (banishList == null) + return new ArrayList<>(); + return banishList; + } + + public ArrayList getCharacterKOSList() { + return characterKOSList; + } + + public ArrayList getGuildKOSList() { + return guildKOSList; + } + + public ArrayList getAllyList() { + return allyList; + } + + public ArrayList getEnemyList() { + return enemyList; + } + + public ArrayList getSubGuildList() { + + return subGuildList; + } + + public GuildTag getGuildTag() { + return this.guildTag; + } + + public int getCharter() { + return charter; + } + + public int getGuildLeaderUUID() { + return this.guildLeaderUUID; + } + + public static AbstractCharacter GetGL(Guild guild) { + if (guild == null) + return null; + + if (guild.guildLeaderUUID == 0) + return null; + + if (guild.ownerIsNPC) + return NPC.getFromCache(guild.guildLeaderUUID); + + return PlayerCharacter.getFromCache(guild.guildLeaderUUID); + } + + public String getMOTD() { + return this.motd; + } + + public String getICMOTD() { + return this.icmotd; + } + + public boolean isNPCGuild() { + + return this.ownerIsNPC; + } + + public int getRepledgeMin() { + return this.repledgeMin; + } + + public int getRepledgeMax() { + return this.repledgeMax; + } + + public int getRepledgeKick() { + return this.repledgeKick; + } + + public int getTeleportMin() { + return this.teleportMin; + } + + public int getTeleportMax() { + return this.teleportMax; + } + + public int getMineTime() { + return this.mineTime; + } + + /* + * Setters + */ + public void setGuildLeaderUUID(int value) { + this.guildLeaderUUID = value; + } + + public boolean setGuildLeader(AbstractCharacter ac) { + if (ac == null) + return false; + // errant guilds cant be guild leader. + if (this.isErrant()) + return false; + + if (!DbManager.GuildQueries.SET_GUILD_LEADER(ac.getObjectUUID(), this.getObjectUUID())){ + if (ac.getObjectType().equals(GameObjectType.PlayerCharacter)) + ChatManager.chatGuildError((PlayerCharacter)ac, "Failed to change guild leader!"); + return false; + } + + PlayerCharacter oldGuildLeader = PlayerCharacter.getFromCache(this.guildLeaderUUID); + + //old guildLeader no longer has guildLeadership stauts. + if (oldGuildLeader != null) + oldGuildLeader.setGuildLeader(false); + + if (ac.getObjectType().equals(GameObjectType.PlayerCharacter)) + ((PlayerCharacter)ac).setGuildLeader(true); + this.guildLeaderUUID = ac.getObjectUUID(); + + return true; + } + + public boolean setGuildLeaderForCreate(AbstractCharacter ac) { + if (ac == null) + return false; + // errant guilds cant be guild leader. + if (this.isErrant()) + return false; + + if (ac.getObjectType().equals(GameObjectType.PlayerCharacter)) + ((PlayerCharacter)ac).setGuildLeader(true); + this.guildLeaderUUID = ac.getObjectUUID(); + + return true; + } + + public void setMOTD(String value) { + this.motd = value; + } + + public void setICMOTD(String value) { + this.icmotd = value; + } + + public int getBgc1() { + if (this.guildTag != null) + return this.guildTag.backgroundColor01; + return 16; + } + + public int getBgc2() { + if (this.guildTag != null) + return this.guildTag.backgroundColor02; + else + return 16; + } + + public int getBgDesign() { + if (this.guildTag != null) + return this.guildTag.backgroundDesign; + return 0; + } + + public int getSc() { + if (this.guildTag != null) + return this.guildTag.symbolColor; + return 16; + } + + public int getSymbol() { + if (this.guildTag != null) + return this.guildTag.symbol; + return 0; + } + + /* + * Utils + */ + + + public static Guild getErrantGuild() { + return g; + } + + public static void CreateErrantGuild(){ + + g = new Guild( "None", Guild.getErrantNation(), 0, + "Anarchy", GuildTag.ERRANT, 0); + g.getObjectType(); + + } + + public boolean isErrant() { + if (this.getObjectUUID() == Guild.g.getObjectUUID()) + return true; + return this.getObjectUUID() == Guild.errant.getObjectUUID(); + } + + + + public static boolean sameGuild(Guild a, Guild b) { + if (a == null || b == null) + return false; + return a.getObjectUUID() == b.getObjectUUID(); + } + + public static boolean sameGuildExcludeErrant(Guild a, Guild b) { + if (a == null || b == null) + return false; + if (a.isErrant() || b.isErrant()) + return false; + return a.getObjectUUID() == b.getObjectUUID(); + } + + public static boolean sameGuildIncludeErrant(Guild a, Guild b) { + if (a == null || b == null) + return false; + if (a.isErrant() || b.isErrant()) + return true; + return a.getObjectUUID() == b.getObjectUUID(); + } + + public static boolean sameNation(Guild a, Guild b) { + if (a == null || b == null) + return false; + if (a.getObjectUUID() == b.getObjectUUID()) + return true; + if (a.nation == null || b.nation == null) + return false; + return a.nation.getObjectUUID() == b.nation.getObjectUUID(); + } + + public static boolean sameNationExcludeErrant(Guild a, Guild b) { + if (a == null || b == null) + return false; + if (a.getObjectUUID() == b.getObjectUUID()) + return true; + if (a.nation == null || b.nation == null) + return false; + return a.nation.getObjectUUID() == b.nation.getObjectUUID() && !a.nation.isErrant(); + } + + public boolean isGuildLeader(int uuid) { + + return (this.guildLeaderUUID == uuid); + } + + public static boolean isTaxCollector(int uuid) { + //TODO add the handling for this later + return false; + } + + /** + * Removes a PlayerCharacter from this (non-Errant) Guild. + * + * @param pc PlayerCharacter to be removed + */ + public void removePlayer(PlayerCharacter pc, GuildHistoryType historyType) { + + if (this.isErrant()) { + Logger.warn( "Attempted to remove a PlayerCharacter (" + pc.getObjectUUID() + ") from an errant guild."); + return; + } + + //Add to Guild History + if (pc.getGuild() != null){ + if (DbManager.GuildQueries.ADD_TO_GUILDHISTORY(pc.getGuildUUID(), pc, DateTime.now(), historyType)){ + GuildHistory guildHistory = new GuildHistory(pc.getGuildUUID(), pc.getGuild().name,DateTime.now(), historyType) ; + pc.getGuildHistory().add(guildHistory); + } + } + + // Clear Guild Ranks + pc.resetGuildStatuses(); + pc.setGuild(Guild.getErrantGuild()); + + pc.incVer(); + DispatchMessage.sendToAllInRange(pc, new GuildInfoMsg(pc, Guild.getErrantGuild(), 2)); + + } + + public void upgradeGuildState(boolean nation){ + if (nation){ + this.guildState = GuildState.Nation; + return; + } + switch(this.guildState){ + + case Errant: + this.guildState = GuildState.Petitioner; + break; + case Sworn: + //Can't upgrade + break; + case Protectorate: + this.guildState = GuildState.Province; + break; + case Petitioner: + this.guildState = GuildState.Sworn; + break; + case Province: + //Can't upgrade + break; + case Nation: + //Can't upgrade + break; + case Sovereign: + this.guildState = GuildState.Protectorate; + break; + } + + } + + public void downgradeGuildState(){ + + switch(this.guildState){ + case Errant: + break; + case Sworn: + this.guildState = GuildState.Errant; + break; + case Protectorate: + this.guildState = GuildState.Sovereign; + break; + case Petitioner: + this.guildState = GuildState.Errant; + break; + case Province: + this.guildState = GuildState.Sovereign; + break; + case Nation: + this.guildState = GuildState.Sovereign; + break; + case Sovereign: + this.guildState = GuildState.Errant; + break; + } + + } + + public boolean canSubAGuild(Guild toSub){ + + boolean canSub; + + if (this.equals(toSub)) + return false; + + switch(this.guildState){ + case Nation: + case Sovereign: + canSub = true; + break; + default: + canSub = false; + } + + switch(toSub.guildState){ + case Errant: + case Sovereign: + canSub = true; + break; + default: + canSub = false; + } + + return canSub; + } + + public static boolean canSwearIn(Guild toSub){ + + boolean canSwear = false; + + switch(toSub.guildState){ + + case Protectorate: + case Petitioner: + canSwear = true; + break; + default: + canSwear = false; + } + + return canSwear; + } + + /* + * Serialization + */ + + public static void _serializeForClientMsg(Guild guild, ByteBufferWriter writer) { +Guild.serializeForClientMsg(guild,writer, null, false); + } + + public static void serializeForClientMsg(Guild guild, ByteBufferWriter writer, PlayerCharacter pc, boolean reshowGuild) { + writer.putInt(guild.getObjectType().ordinal()); + writer.putInt(guild.getObjectUUID()); + writer.putInt(guild.nation.getObjectType().ordinal()); + writer.putInt(guild.nation.getObjectUUID()); + + if (pc == null) { + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); // Defaults + writer.putInt(0); // Defaults + } else { + writer.putString(guild.name); + writer.putString(guild.nation.name); + writer.putInt(GuildStatusController.getTitle(pc.getGuildStatus())); // TODO Double check this is + // title and rank + if (GuildStatusController.isGuildLeader(pc.getGuildStatus())) + writer.putInt(PlayerCharacter.GetPlayerRealmTitle(pc)); + else + writer.putInt(GuildStatusController.getRank(pc.getGuildStatus())); + //writer.putInt(GuildStatusController.getRank(pc.getGuildStatus())); + } + + City ownedCity = guild.getOwnedCity(); + + if (ownedCity != null){ + Realm realm = guild.getOwnedCity().getRealm(); + if (realm != null && realm.getRulingCity() != null){ + if (realm.getRulingCity().equals(ownedCity)){ + writer.putInt(realm.getCharterType()); + }else + writer.putInt(0); + }else{ + writer.putInt(0); + } + }else + writer.putInt(0); + + writer.putFloat(200); + writer.putFloat(200); // Pad + + GuildTag._serializeForDisplay(guild.guildTag,writer); + GuildTag._serializeForDisplay(guild.nation.guildTag,writer); + if (reshowGuild) { + writer.putInt(1); + writer.putInt(guild.getObjectType().ordinal()); + writer.putInt(guild.getObjectUUID()); + + } else + writer.putInt(0); // Pad + } + + public static void serializeForTrack(Guild guild,ByteBufferWriter writer) { + Guild.serializeGuildForTrack(guild,writer); + if (guild.nation != null) + Guild.serializeGuildForTrack(guild.nation,writer); + else + Guild.addErrantForTrack(writer); + } + + public static void serializeGuildForTrack(Guild guild, ByteBufferWriter writer) { + writer.putInt(guild.getObjectType().ordinal()); + writer.putInt(guild.getObjectUUID()); + writer.put((byte) 1); + GuildTag._serializeForDisplay(guild.guildTag,writer); + } + + public static void serializeErrantForTrack(ByteBufferWriter writer) { + addErrantForTrack(writer); //Guild + addErrantForTrack(writer); //Nation + } + + public int getRealmsOwnedFlag(){ + int flag = 0; + switch(realmsOwned){ + case 0: + flag = 0; + case 1: + case 2: + flag = 1; + break; + case 3: + case 4: + flag = 2; + break; + case 5: + flag = 3; + break; + default: + flag = 3; + break; + } + return flag; + } + + private static void addErrantForTrack(ByteBufferWriter writer) { + writer.putInt(0); //type + writer.putInt(0); //ID + writer.put((byte) 1); + writer.putInt(16); //Tags + writer.putInt(16); + writer.putInt(16); + writer.putInt(0); + writer.putInt(0); + } + + public void serializeForPlayer(ByteBufferWriter writer) { + writer.putInt(this.getObjectType().ordinal()); + writer.putInt(this.getObjectUUID()); + writer.putInt(this.nation.getObjectType().ordinal()); + writer.putInt(this.nation.getObjectUUID()); + + } + + private static Guild errant; + + public static Guild getErrantNation() { + if (Guild.errant == null) + Guild.errant = new Guild("None", null, 10, "Despot Rule", GuildTag.ERRANT, 0); + return Guild.errant; + } + + /* + * Game Object Manager + */ + public static Guild getGuild(final int objectUUID) { + + if (objectUUID == 0) + return Guild.getErrantGuild(); + Guild guild = (Guild) DbManager.getFromCache(Enum.GameObjectType.Guild, objectUUID); + if (guild != null) + return guild; + + Guild dbGuild = DbManager.GuildQueries.GET_GUILD(objectUUID); + + if (dbGuild == null) + return Guild.getErrantGuild(); + else + return dbGuild; + } + + + @Override + public void updateDatabase() { + DbManager.GuildQueries.updateDatabase(this); + } + + public boolean isRealmRuler() { + + City ownedCity; + Building tol; + + ownedCity = this.getOwnedCity(); + + if (ownedCity == null) + return false; + + tol = ownedCity.getTOL(); + + if (tol == null) + return false; + + return tol.getRank() == 8; + + } + + @Override + public void runAfterLoad() { + + try { + DbObjectType objectType = DbManager.BuildingQueries.GET_UID_ENUM(this.guildLeaderUUID); + this.ownerIsNPC = (objectType == DbObjectType.NPC); + } catch (Exception e) { + this.ownerIsNPC = false; + Logger.error("Failed to find Object Type for owner " + this.guildLeaderUUID); + } + + + //LOad Owners in Cache so we do not have to continuely look in the db for owner. + if (this.ownerIsNPC){ + if (NPC.getNPC(this.guildLeaderUUID) == null) + Logger.info( "Guild UID " + this.getObjectUUID() + " Failed to Load NPC Owner with ID " + this.guildLeaderUUID); + + }else if (this.guildLeaderUUID != 0){ + if (PlayerCharacter.getPlayerCharacter(this.guildLeaderUUID) == null) + Logger.info( "Guild UID " + this.getObjectUUID() + " Failed to Load Player Owner with ID " + this.guildLeaderUUID); + } + + // If loading this guild for the first time write it's character record to disk + + if (ConfigManager.serverType.equals(ServerType.WORLDSERVER) + && (hash == null)) { + + this.setHash(); + + if (DataWarehouse.recordExists(Enum.DataRecordType.GUILD, this.getObjectUUID()) == false) { + GuildRecord guildRecord = GuildRecord.borrow(this, Enum.RecordEventType.CREATE); + DataWarehouse.pushToWarehouse(guildRecord); + } + + } + + if (MBServerStatics.worldUUID == nationUUID && this.cityUUID != 0) + this.nation = this; + else if (nationUUID == 0 || (MBServerStatics.worldUUID == nationUUID && this.cityUUID == 0)) { + this.nation = Guild.getErrantGuild(); + this.nmotd = ""; + } else + this.nation = Guild.getGuild(nationUUID); + + if (this.nation == null) + this.nation = Guild.getErrantGuild(); + //Get guild states. + try { + this.subGuildList = DbManager.GuildQueries.GET_SUB_GUILDS(this.getObjectUUID()); + }catch(Exception e){ + + this.subGuildList = new ArrayList<>(); + Logger.error( "FAILED TO LOAD SUB GUILDS FOR UUID " + this.getObjectUUID()); + } + + if (this.nation == this && subGuildList.size() > 0) + this.guildState = GuildState.Nation; + else if (this.nation.equals(this)) + this.guildState = GuildState.Sovereign; + else if (!this.nation.isErrant() && this.cityUUID != 0) + this.guildState = GuildState.Province; + else if (!this.nation.isErrant()) + this.guildState = GuildState.Sworn; + else + this.guildState = GuildState.Errant; + + if (this.cityUUID == 0) + return; + + + // Calculate number of realms this guild controls + // Only do this on the game server to avoid loading a TOL/City/Zone needlessly + + if ((ConfigManager.serverType.equals(ServerType.WORLDSERVER)) && + (this.isRealmRuler() == true)) { + this.realmsOwned++; + if (!this.nation.equals(this)) { + this.nation.realmsOwned++; + } + } + + if (ConfigManager.serverType.equals(ServerType.WORLDSERVER)){ + + //add alliance list, clear all lists as there seems to be a bug where alliances are doubled, need to find where. + //possible runAfterLoad being called twice?!?! + this.banishList = dbGuildHandler.GET_GUILD_BANISHED(this.getObjectUUID()); + this.characterKOSList = DbManager.GuildQueries.GET_GUILD_KOS_CHARACTER(this.getObjectUUID()); + this.guildKOSList = DbManager.GuildQueries.GET_GUILD_KOS_GUILD(this.getObjectUUID()); + + this.allyList.clear(); + this.enemyList.clear(); + this.recommendList.clear(); + + try{ + DbManager.GuildQueries.LOAD_ALL_ALLIANCES_FOR_GUILD(this); + for (GuildAlliances guildAlliance:this.guildAlliances.values()){ + if (guildAlliance.isRecommended()){ + Guild recommendedGuild = Guild.getGuild(guildAlliance.getAllianceGuild()); + if (recommendedGuild != null) + this.recommendList.add(recommendedGuild); + }else if (guildAlliance.isAlly()){ + Guild alliedGuild = Guild.getGuild(guildAlliance.getAllianceGuild()); + if (alliedGuild != null) + this.allyList.add(alliedGuild); + }else{ + Guild enemyGuild = Guild.getGuild(guildAlliance.getAllianceGuild()); + if (enemyGuild != null) + this.enemyList.add(enemyGuild); + } + + } + }catch(Exception e){ + Logger.error(this.getObjectUUID() + e.getMessage()); + } + } + } + + /** + * @return the motto + */ + public String getMotto() { + return motto; + } + + public GuildState getGuildState() { + return guildState; + } + + public void setGuildState(GuildState guildState) { + this.guildState = guildState; + } + + /** + * @return the realmsOwned + */ + public int getRealmsOwned() { + return realmsOwned; + } + + /** + * @param realmsOwned the realmsOwned to set + */ + public void setRealmsOwned(int realmsOwned) { + this.realmsOwned = realmsOwned; + } + + public void removeSubGuild(Guild subGuild) { + + // Update database + + if (!DbManager.GuildQueries.UPDATE_PARENT(subGuild.getObjectUUID(), MBServerStatics.worldUUID)) + Logger.debug("Failed to set Nation Guild for Guild with UID " + subGuild.getObjectUUID()); + + // Guild without any subs is no longer a nation + + if (subGuild.getOwnedCity() == null) { + subGuild.nation = null; + } + else { + subGuild.nation = subGuild; + } + + // Downgrade guild + + subGuild.downgradeGuildState(); + + // Remove from collection + + subGuildList.remove(subGuild); + + GuildManager.updateAllGuildTags(subGuild); + GuildManager.updateAllGuildBinds(subGuild, subGuild.getOwnedCity()); + + } + + public void setMineTime(int mineTime) { + this.mineTime = mineTime; + } + + public ConcurrentHashMap getGuildCondemned() { + return guildCondemned; + } + + + public String getHash() { + return hash; + } + + public void setHash() { + this.hash = DataWarehouse.hasher.encrypt(this.getObjectUUID()); + + DataWarehouse.writeHash(Enum.DataRecordType.GUILD, this.getObjectUUID()); + } + + public Enum.GuildType getGuildType(){ + try{ + return Enum.GuildType.values()[this.charter]; + }catch(Exception e){ + Logger.error(e); + return Enum.GuildType.NONE; + } + + } + + public ArrayList getRecommendList() { + return recommendList; + } + + public void setRecommendList(ArrayList recommendList) { + this.recommendList = recommendList; + } + + public synchronized boolean addGuildToAlliance(AllianceChangeMsg msg, final AllianceType allianceType, Guild toGuild, PlayerCharacter player){ + + Dispatch dispatch; + + // Member variable assignment + + + if (toGuild == null) + return false; + + if (!Guild.sameGuild(player.getGuild(), this)){ + msg.setMsgType(AllianceChangeMsg.ERROR_NOT_SAME_GUILD); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return false; + } + + if (allianceType == AllianceType.Ally || allianceType == AllianceType.Enemy) + if (!GuildStatusController.isInnerCouncil(player.getGuildStatus()) && !GuildStatusController.isGuildLeader(player.getGuildStatus())){ + msg.setMsgType(AllianceChangeMsg.ERROR_NOT_AUTHORIZED); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return false; + } + + if (allianceType == AllianceType.RecommendedAlly || allianceType == AllianceType.RecommendedEnemy){ + if (!GuildStatusController.isFullMember(player.getGuildStatus())){ + msg.setMsgType(AllianceChangeMsg.ERROR_NOT_AUTHORIZED); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return false; + } + } + + // if (this.getGuildType() != toGuild.getGuildType()){ + // msg.setMsgType(AllianceChangeMsg.ERROR_NOT_SAME_FACTION); + // dispatch = Dispatch.borrow(player, msg); + // DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + // return false; + // } + + + + + + + switch(allianceType){ + case RecommendedAlly: + if (recommendList.size() == 10){ + msg.setMsgType(AllianceChangeMsg.ERROR_TOO_MANY); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return false; + } + + if (recommendList.contains(toGuild)){ + ErrorPopupMsg.sendErrorMsg(player, "This guild is already recommonded!"); + msg.setMsgType((byte)15); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return false; + } + + if (!DbManager.GuildQueries.ADD_TO_ALLIANCE_LIST(this.getObjectUUID(), toGuild.getObjectUUID(), true, true, player.getFirstName())){ + msg.setMsgType((byte)15); + dispatch = Dispatch.borrow(player, msg); + return false; + } + + GuildAlliances guildAlliance = new GuildAlliances(this.getObjectUUID(), toGuild.getObjectUUID(), true, true, player.getFirstName()); + this.guildAlliances.put(toGuild.getObjectUUID(), guildAlliance); + this.removeGuildFromEnemy(toGuild); + this.removeGuildFromAlliance(toGuild); + this.recommendList.add(toGuild); + + + + return true; + + case RecommendedEnemy: + if (recommendList.size() == 10){ + msg.setMsgType(AllianceChangeMsg.ERROR_TOO_MANY); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return false; + } + + if (recommendList.contains(toGuild)){ + ErrorPopupMsg.sendErrorMsg(player, "This guild is already recommonded!"); + msg.setMsgType((byte)15); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return false; + } + + if (!DbManager.GuildQueries.ADD_TO_ALLIANCE_LIST(this.getObjectUUID(), toGuild.getObjectUUID(), true, false, player.getFirstName())){ + msg.setMsgType((byte)15); + dispatch = Dispatch.borrow(player, msg); + return false; + } + + GuildAlliances enemyAlliance = new GuildAlliances(this.getObjectUUID(), toGuild.getObjectUUID(), true, false, player.getFirstName()); + this.guildAlliances.put(toGuild.getObjectUUID(), enemyAlliance); + this.removeGuildFromEnemy(toGuild); + this.removeGuildFromAlliance(toGuild); + this.recommendList.add(toGuild); + + return true; + + case Ally: + if (allyList.size() == 10){ + msg.setMsgType(AllianceChangeMsg.ERROR_TOO_MANY); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return false; + } + + if (allyList.contains(toGuild)){ + ErrorPopupMsg.sendErrorMsg(player, "This guild is already an Ally!"); + msg.setMsgType((byte)15); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return false; + } + + if (!this.guildAlliances.containsKey(toGuild.getObjectUUID())){ + ErrorPopupMsg.sendErrorMsg(player, "A Serious error has Occured. Please contact CCR!"); + Logger.error(this.getObjectUUID() + " Could not find alliance Guild"); + msg.setMsgType((byte)15); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return false; + } + + GuildAlliances ally = this.guildAlliances.get(toGuild.getObjectUUID()); + if (!ally.UpdateAlliance(AllianceType.Ally, this.recommendList.contains(toGuild))){ + ErrorPopupMsg.sendErrorMsg(player, "A Serious error has Occured. Please contact CCR!"); + Logger.error( this.getObjectUUID() + " failed to update alliance Database"); + msg.setMsgType((byte)15); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return false; + + } + + this.removeGuildFromEnemy(toGuild); + this.removeGuildFromRecommended(toGuild); + + this.allyList.add(toGuild); + Guild.UpdateClientAlliances(this); + + + break; + case Enemy: + if (enemyList.size() == 10){ + msg.setMsgType(AllianceChangeMsg.ERROR_TOO_MANY); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return false; + } + + if (enemyList.contains(toGuild)){ + ErrorPopupMsg.sendErrorMsg(player, "This guild is already an Enemy!"); + msg.setMsgType((byte)15); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return false; + } + + if (!this.guildAlliances.containsKey(toGuild.getObjectUUID())){ + ErrorPopupMsg.sendErrorMsg(player, "A Serious error has Occured. Please contact CCR!"); + Logger.error( this.getObjectUUID() + " Could not find alliance Guild"); + msg.setMsgType((byte)15); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return false; + } + + GuildAlliances enemy = this.guildAlliances.get(toGuild.getObjectUUID()); + if (!enemy.UpdateAlliance(AllianceType.Enemy, this.recommendList.contains(toGuild))){ + ErrorPopupMsg.sendErrorMsg(player, "A Serious error has Occured. Please contact CCR!"); + Logger.error(this.getObjectUUID() + " failed to update alliance Database"); + msg.setMsgType((byte)15); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return false; + + } + + //remove from other allied lists. + this.removeGuildFromAlliance(toGuild); + this.removeGuildFromRecommended(toGuild); + + this.enemyList.add(toGuild); + + Guild.UpdateClientAlliances(this); + break; + } + + // once here everything passed, send successMsg; + msg.setMsgType(AllianceChangeMsg.INFO_SUCCESS); + dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return true; + } + + public synchronized boolean removeGuildFromAlliance(Guild toRemove){ + if (this.allyList.contains(toRemove)){ + this.allyList.remove(toRemove); + } + return true; + } + public synchronized boolean removeGuildFromEnemy(Guild toRemove){ + if (this.enemyList.contains(toRemove)){ + this.enemyList.remove(toRemove); + } + return true; + } + public synchronized boolean removeGuildFromRecommended(Guild toRemove){ + if (this.recommendList.contains(toRemove)){ + this.recommendList.remove(toRemove); + } + return true; + } + + public synchronized boolean removeGuildFromAllAlliances(Guild toRemove){ + + if (!this.guildAlliances.containsKey(toRemove.getObjectUUID())){ + return false; + } + + if (!DbManager.GuildQueries.REMOVE_FROM_ALLIANCE_LIST(this.getObjectUUID(), toRemove.getObjectUUID())) + return false; + + + + this.guildAlliances.remove(toRemove.getObjectUUID()); + + this.removeGuildFromAlliance(toRemove); + this.removeGuildFromEnemy(toRemove); + this.removeGuildFromRecommended(toRemove); + + Guild.UpdateClientAlliances(this); + + + return true; + + } + + public static void UpdateClientAlliances(Guild toUpdate){ + UpdateClientAlliancesMsg ucam = new UpdateClientAlliancesMsg(toUpdate); + + + + for (PlayerCharacter player : SessionManager.getAllActivePlayerCharacters()) { + + if (Guild.sameGuild(player.getGuild(), toUpdate)){ + Dispatch dispatch = Dispatch.borrow(player, ucam); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + + } + } + + public static void UpdateClientAlliancesForPlayer(PlayerCharacter toUpdate){ + UpdateClientAlliancesMsg ucam = new UpdateClientAlliancesMsg(toUpdate.getGuild()); + Dispatch dispatch = Dispatch.borrow(toUpdate, ucam); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + + } + + public static Guild getFromCache(int id) { + return (Guild) DbManager.getFromCache(GameObjectType.Guild, id); + } + + public static ArrayList GuildRoster(Guild guild){ + ArrayList roster = new ArrayList<>(); + if (guild == null) + return roster; + + if (guild.isErrant()) + return roster; + + if (DbManager.getList(GameObjectType.PlayerCharacter) == null) + return roster; + for (AbstractGameObject ago : DbManager.getList(GameObjectType.PlayerCharacter)){ + PlayerCharacter toAdd = (PlayerCharacter)ago; + + if (!toAdd.getGuild().equals(guild)) + continue; + + if (toAdd.isDeleted()) + continue; + + roster.add(toAdd); + + } + return roster; + } + + + +} diff --git a/src/engine/objects/GuildAlliances.java b/src/engine/objects/GuildAlliances.java new file mode 100644 index 00000000..7c078729 --- /dev/null +++ b/src/engine/objects/GuildAlliances.java @@ -0,0 +1,102 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum.AllianceType; +import engine.gameManager.DbManager; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class GuildAlliances { + + private int sourceGuild; + private int allianceGuild; + private boolean isRecommended; + private boolean isAlly; + private String recommender; + + /** + * ResultSet Constructor + */ + + public GuildAlliances(ResultSet rs) throws SQLException { + this.sourceGuild = rs.getInt("GuildID"); + this.allianceGuild = rs.getInt("OtherGuildID"); + this.isRecommended = rs.getBoolean("isRecommended"); + this.isAlly = rs.getBoolean("isAlliance"); + this.recommender =rs.getString("recommender"); + } + + public GuildAlliances(int sourceGuild, int allianceGuild, boolean isRecommended, boolean isAlly, + String recommender) { + super(); + this.sourceGuild = sourceGuild; + this.allianceGuild = allianceGuild; + this.isRecommended = isRecommended; + this.isAlly = isAlly; + this.recommender = recommender; + } + + public int getSourceGuild() { + return sourceGuild; + } + + public int getAllianceGuild() { + return allianceGuild; + } + + public boolean isRecommended() { + return isRecommended; + } + + public boolean isAlly() { + return isAlly; + } + + public String getRecommender() { + return recommender; + } + + public synchronized boolean UpdateAlliance(final AllianceType allianceType, boolean updateRecommended){ + switch (allianceType){ + case Ally: + if (updateRecommended){ + if (!DbManager.GuildQueries.UPDATE_ALLIANCE_AND_RECOMMENDED(this.sourceGuild, this.allianceGuild, true)) + return false; + this.isAlly = true; + this.isRecommended = false; + }else{ + if (!DbManager.GuildQueries.UPDATE_ALLIANCE(this.sourceGuild, this.allianceGuild, true)) + return false; + this.isAlly = true; + this.isRecommended = false; + } + break; + case Enemy: + + if (updateRecommended){ + if (!DbManager.GuildQueries.UPDATE_ALLIANCE_AND_RECOMMENDED(this.sourceGuild, this.allianceGuild, false)) + return false; + this.isAlly = false; + this.isRecommended = false; + }else{ + if (!DbManager.GuildQueries.UPDATE_ALLIANCE(this.sourceGuild, this.allianceGuild, false)) + return false; + this.isAlly = false; + this.isRecommended = false; + } + break; + + } + return true; + } + +} diff --git a/src/engine/objects/GuildCondemn.java b/src/engine/objects/GuildCondemn.java new file mode 100644 index 00000000..87a883d5 --- /dev/null +++ b/src/engine/objects/GuildCondemn.java @@ -0,0 +1,77 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; + + + + +public class GuildCondemn { + + private int ID; + private int playerUID; + private int parentGuildUID; + private int guildUID; + private int friendType; + public static HashMap> GetCondemnedFromGuildID = new HashMap<>(); + + + /** + * ResultSet Constructor + */ + public GuildCondemn(ResultSet rs) throws SQLException { + this.playerUID = rs.getInt("playerUID"); + this.parentGuildUID = rs.getInt("buildingUID"); + this.guildUID = rs.getInt("guildUID"); + this.friendType = rs.getInt("friendType"); + } + + + + + public GuildCondemn(int playerUID, int parentGuildUID, int guildUID, int friendType) { + super(); + this.playerUID = playerUID; + this.parentGuildUID = parentGuildUID; + this.guildUID = guildUID; + this.friendType = friendType; + } + + + + + public int getPlayerUID() { + return playerUID; + } + + + public int getParentGuildUID() { + return parentGuildUID; + } + + + public int getGuildUID() { + return guildUID; + } + + + public int getFriendType() { + return friendType; + } + + + + + +} diff --git a/src/engine/objects/GuildHistory.java b/src/engine/objects/GuildHistory.java new file mode 100644 index 00000000..ad48e476 --- /dev/null +++ b/src/engine/objects/GuildHistory.java @@ -0,0 +1,94 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + + +import engine.Enum.GameObjectType; +import engine.Enum.GuildHistoryType; +import engine.net.ByteBufferWriter; +import org.joda.time.DateTime; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class GuildHistory { + + + private int guildID; + private String guildName; + private DateTime time; + private GuildHistoryType historyType; + + + + + public GuildHistory( int guildID, String guildName, + DateTime dateTime, GuildHistoryType historyType ) { + super(); + this.guildID = guildID; + this.guildName = guildName; + this.time = dateTime; + this.historyType = historyType; + + } + + public GuildHistoryType getHistoryType() { + return historyType; + } + + public GuildHistory(ResultSet rs) throws SQLException { + java.util.Date sqlDateTime; + this.guildID = rs.getInt("guildID"); + Guild guild = Guild.getGuild(this.guildID); + if (guild != null) + this.guildName = guild.getName(); + else + this.guildName = "Guild Not Found"; + + sqlDateTime = rs.getTimestamp("historyDate"); + if (sqlDateTime != null) + this.time = new DateTime(sqlDateTime); + else + this.time = DateTime.now().minusYears(1); + this.historyType = GuildHistoryType.valueOf(rs.getString("historyType")); + } + + + public long getGuildID() { + return guildID; + } + + public String getGuildName() { + return guildName; + } + + + + public void _serialize(ByteBufferWriter writer) { + writer.putInt(this.historyType.getType()); + writer.putInt(GameObjectType.Guild.ordinal()); + writer.putInt(this.guildID); + writer.putString(guildName); + writer.putInt(0); //Pad + writer.putDateTime(this.time); + } + + public DateTime getTime() { + return time; + } + + public void setTime(DateTime time) { + this.time = time; + } + + + +} diff --git a/src/engine/objects/GuildStatusController.java b/src/engine/objects/GuildStatusController.java new file mode 100644 index 00000000..ac76790b --- /dev/null +++ b/src/engine/objects/GuildStatusController.java @@ -0,0 +1,131 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import java.util.concurrent.atomic.AtomicInteger; + +public class GuildStatusController { + + /* + * Status is stored in a single integer contained within the Character Table + * + * This class is responsible for maintaining and interpreting that value. + * + * Byte 1 - All : Title [0x000000FF] + * Byte 2 - Low : isFullMember [0x00000F00] + * Byte 2 - High : isTaxCollector [0x0000F000] + * Byte 3 - Low : isRecruiter [0x000F0000] + * Byte 3 - High : isInnerCouncil [0x00F00000] + * Byte 4 - Low : isGuildLeader [0x0F000000] + * Byte 4 - High : Empty [0xF0000000] + */ + + //Getters + public static boolean isGuildLeader(AtomicInteger status) { + return ((status.get() & GUILDLEADER) > 0); + } + + public static boolean isInnerCouncil(AtomicInteger status) { + return ((status.get() & INNERCOUNCIL) > 0); + } + + public static boolean isRecruiter(AtomicInteger status) { + return ((status.get() & RECRUITER) > 0); + } + + public static boolean isTaxCollector(AtomicInteger status) { + return ((status.get() & TAXCOLLECTOR) > 0); + } + + public static boolean isFullMember(AtomicInteger status) { + return ((status.get() & FULLMEMBER) > 0); + } + + public static int getTitle(AtomicInteger status) { + return status.get() & TITLE; + } + + public static int getRank(AtomicInteger status) { + int value = status.get(); + + //Guild Leader + if(value > 0x00FFFFFF) { + return 10; + } + + //Inner Council + if(value > 0x000FFFFF) { + return 9; + } + + //Recruiter + if(value > 0x0000FFFF) { + return 8; + } + + //Tax Collector + if(value > 0x00000FFF) { + return 7; + } + + //Full Member + if(value > 0x000000FF) { + return 6; + } + + //Petitioner + return 5; + } + + //Setters + public static void setTitle(AtomicInteger current, int i) { + int value; + i &= TITLE; + do { + value = current.get(); + }while(!current.compareAndSet(value, (value & ~TITLE) | i)); + } + + + public static void setFullMember(AtomicInteger status, boolean newValue) { + setNibble(status, newValue, FULLMEMBER); + } + + public static void setTaxCollector(AtomicInteger status, boolean newValue) { + setNibble(status, newValue, TAXCOLLECTOR); + } + + public static void setRecruiter(AtomicInteger status, boolean newValue) { + setNibble(status, newValue, RECRUITER); + } + + public static void setInnerCouncil(AtomicInteger status, boolean newValue) { + setNibble(status, newValue, INNERCOUNCIL); + } + + public static void setGuildLeader (AtomicInteger status, boolean newValue) { + setNibble(status, newValue, GUILDLEADER); + } + + private static void setNibble(AtomicInteger current, boolean newValue, int mask) { + int value, i = ((newValue)?mask & -1:0); + do { + value = current.get(); + }while(!current.compareAndSet(value, (value & ~mask) | i)); + } + + //Constants + private static final int TITLE = 0x000000FF; // 00, F0 and 0F had no effect + private static final int FULLMEMBER = 0x00000F00; + private static final int TAXCOLLECTOR = 0x0000F000; + private static final int RECRUITER = 0x000F0000; + private static final int INNERCOUNCIL = 0x00F00000; + private static final int GUILDLEADER = 0x0F000000; +} diff --git a/src/engine/objects/GuildTag.java b/src/engine/objects/GuildTag.java new file mode 100644 index 00000000..014c0341 --- /dev/null +++ b/src/engine/objects/GuildTag.java @@ -0,0 +1,93 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; + +public class GuildTag { + public final int backgroundColor01; + public final int backgroundColor02; + public final int symbolColor; + public final int symbol; + public final int backgroundDesign; + public static final GuildTag ERRANT = new GuildTag(16,16,16,0,0); + + public GuildTag(int backgroundColor01, int backgroundColor02, + int symbolColor, int symbol, int backgroundDesign) { + super(); + this.backgroundColor01 = backgroundColor01; + this.backgroundColor02 = backgroundColor02; + this.symbolColor = symbolColor; + this.symbol = symbol; + this.backgroundDesign = backgroundDesign; + } + + public GuildTag(ByteBufferReader reader, boolean forCreation) { + this.backgroundColor01 = reader.getInt(); + this.backgroundColor02 = reader.getInt(); + this.symbolColor = reader.getInt(); + if(forCreation) { + this.symbol = reader.getInt(); + this.backgroundDesign = reader.getInt(); + } else { + this.backgroundDesign = reader.getInt(); + this.symbol = reader.getInt(); + } + } + + public GuildTag(ByteBufferReader reader) { + this(reader, false); + } + + public boolean isValid() { + if(this.backgroundColor01 < 0 || this.backgroundColor01 > 18) + return false; + if(this.backgroundColor02 < 0 || this.backgroundColor02 > 18) + return false; + if(this.symbolColor < 0 || this.symbolColor > 18) + return false; + if(this.symbol < 0 || this.symbol > 183) + return false; + return this.backgroundDesign >= 0 && this.backgroundDesign <= 14; + } + + + public static void _serializeForGuildCreation(GuildTag guildTag, ByteBufferWriter writer) { + writer.putInt(guildTag.backgroundColor01); + writer.putInt(guildTag.backgroundColor02); + writer.putInt(guildTag.symbolColor); + writer.putInt(guildTag.symbol); + writer.putInt(guildTag.backgroundDesign); + } + + public static void _serializeForDisplay(GuildTag guildTag, ByteBufferWriter writer) { + writer.putInt(guildTag.backgroundColor01); + writer.putInt(guildTag.backgroundColor02); + writer.putInt(guildTag.symbolColor); + writer.putInt(guildTag.backgroundDesign); + writer.putInt(guildTag.symbol); + } + + public void serializeObject(ByteBufferWriter writer) { + writer.put((byte)this.backgroundColor01); + writer.put((byte)this.backgroundColor02); + writer.put((byte)this.symbolColor); + writer.put((byte)this.backgroundDesign); + writer.put((byte)this.symbol); + } + + + + public String summarySentence() { + return "Bkgrnd: " + this.backgroundDesign + '(' + this.backgroundColor01 + '-' + this.backgroundColor02 + ')' + + "; Symbol: " + this.symbol + '(' + this.symbolColor + ')'; + } +} diff --git a/src/engine/objects/Heraldry.java b/src/engine/objects/Heraldry.java new file mode 100644 index 00000000..f7757a95 --- /dev/null +++ b/src/engine/objects/Heraldry.java @@ -0,0 +1,171 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum.GameObjectType; +import engine.gameManager.DbManager; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; + +public class Heraldry { + + public int playerUID; + public int characterUUID; + public int characterType; + + public static HashMap > HeraldyMap = new HashMap<>(); + + /** + * ResultSet Constructor + */ + + public Heraldry(ResultSet rs) throws SQLException { + this.playerUID = rs.getInt("playerUID"); + this.characterUUID = rs.getInt("characterUID"); + this.characterType = rs.getInt("characterType"); + + //cache player friends. + //hashset already created, just add to set. + if (HeraldyMap.containsKey(playerUID)){ + HashMap playerHeraldySet = HeraldyMap.get(playerUID); + playerHeraldySet.put(characterUUID,characterType); + //hashset not yet created, create new set, and add to map. + }else{ + HashMap playerHeraldySet = new HashMap<>(); + playerHeraldySet.put(characterUUID,characterType); + HeraldyMap.put(this.playerUID, playerHeraldySet); + } + + } + + public Heraldry(int playerUID, int friendUID) { + super(); + this.playerUID = playerUID; + this.characterUUID = friendUID; + } + + public int getPlayerUID() { + return playerUID; + } + + public static boolean AddToHeraldy(int playerID, AbstractWorldObject character){ + HashMap characters = HeraldyMap.get(playerID); + + if (characters != null){ + //already in friends list, don't do anything. + if (characters.containsKey(character.getObjectUUID())) + return false; + + DbManager.PlayerCharacterQueries.ADD_HERALDY(playerID, character); + characters.put(character.getObjectUUID(),character.getObjectType().ordinal()); + }else{ + characters = new HashMap<>(); + DbManager.PlayerCharacterQueries.ADD_HERALDY(playerID, character); + characters.put(character.getObjectUUID(),character.getObjectType().ordinal()); + HeraldyMap.put(playerID, characters); + } + return true; + } + + public static boolean RemoveFromHeraldy(int playerID, int characterID){ + + if (!CanRemove(playerID, characterID)) + return false; + + HashMap characters = HeraldyMap.get(playerID); + + if (characters != null){ + DbManager.PlayerCharacterQueries.REMOVE_HERALDY(playerID, characterID); + characters.remove(characterID); + } + return true; + } + + public static boolean CanRemove(int playerID, int toRemove){ + if (HeraldyMap.get(playerID) == null) + return false; + + if (HeraldyMap.get(playerID).isEmpty()) + return false; + + if (!HeraldyMap.get(playerID).containsKey(toRemove)) + return false; + + return true; + } + + public static void AuditHeraldry() { + + HashMap characterMap; + ArrayList purgeList = new ArrayList<>(); + + for (int playerID : Heraldry.HeraldyMap.keySet()) { + + characterMap = Heraldry.HeraldyMap.get(playerID); + + if (characterMap == null || characterMap.isEmpty()) + continue; + + // Loop through map adding deleted characters to our purge map + + purgeList.clear(); + + for (int characterID : characterMap.keySet()) { + + int characterType = characterMap.get(characterID); + + if (characterType != GameObjectType.PlayerCharacter.ordinal()) + continue; + + // Player is deleted, add to purge list + + if (PlayerCharacter.getFromCache(characterID) == null) + purgeList.add(characterID); + + } + + // Run purge + + for (int uuid : purgeList) { + + if (!Heraldry.RemoveFromHeraldy(playerID, uuid)) + continue; + + Logger.info("Removed Deleted Character ID " + uuid + " from PlayerID " + playerID + " heraldry."); + + } + } + } + + public static void ValidateHeraldry(int playerUUID) { + + HashMap heraldryMap = Heraldry.HeraldyMap.get(playerUUID); + + if (heraldryMap == null || heraldryMap.isEmpty()) + return; + + for (int characterID : heraldryMap.keySet()){ + int characterType = heraldryMap.get(characterID); + + GameObjectType objectType = GameObjectType.values()[characterType]; + + AbstractGameObject ago = DbManager.getFromCache(objectType, characterID); + + if (ago == null) + heraldryMap.remove(characterID); + + } + } + +} diff --git a/src/engine/objects/Item.java b/src/engine/objects/Item.java new file mode 100644 index 00000000..64f139e3 --- /dev/null +++ b/src/engine/objects/Item.java @@ -0,0 +1,1466 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum; +import engine.Enum.*; +import engine.exception.SerializationException; +import engine.gameManager.ConfigManager; +import engine.gameManager.DbManager; +import engine.gameManager.PowersManager; +import engine.net.ByteBufferReader; +import engine.net.ByteBufferWriter; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.DeleteItemMsg; +import engine.powers.EffectsBase; +import engine.powers.effectmodifiers.AbstractEffectModifier; +import engine.powers.poweractions.AbstractPowerAction; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantLock; + + +public class Item extends AbstractWorldObject { + + private int ownerID; //may be character, account, npc, mob + private int flags; //1 = isIDed + private int numberOfItems; + private short durabilityCurrent; + private final short durabilityMax; + private final byte chargesMax; + private byte chargesRemaining; + private byte equipSlot; + private boolean canDestroy; + private boolean rentable; + private boolean isRandom = false; + + private int value; + + public Enum.ItemContainerType containerType; + + private OwnerType ownerType; + private int itemBaseID; + private AbstractWorldObject lastOwner; + private ArrayList enchants = new ArrayList<>(); + private final ConcurrentHashMap bonuses = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private final ArrayList effectNames = new ArrayList<>(); + private static ConcurrentHashMap enchantValues = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private long dateToUpgrade; + public ReentrantLock lootLock = new ReentrantLock(); + private String customName = ""; + private int magicValue; + + /** + * No Id Constructor + */ + public Item( ItemBase itemBase, int ownerID, + OwnerType ownerType, byte chargesMax, byte chargesRemaining, + short durabilityCurrent, short durabilityMax, boolean canDestroy, + boolean rentable, Enum.ItemContainerType containerType, byte equipSlot, + ArrayList enchants, String name) { + super(); + this.itemBaseID = itemBase.getUUID(); + this.ownerID = ownerID; + this.ownerType = ownerType; + + if (itemBase.getType().getValue() == 20){ + this.chargesMax = chargesMax; + this.chargesRemaining = chargesRemaining; + } + else{ + this.chargesMax = (byte) itemBase.getNumCharges(); + this.chargesRemaining = (byte) itemBase.getNumCharges(); + } + + this.durabilityMax = (short) itemBase.getDurability(); + this.durabilityCurrent = (short) itemBase.getDurability(); + this.containerType = containerType; + this.canDestroy = canDestroy; + this.rentable = rentable; + this.equipSlot = equipSlot; + this.enchants = enchants; + this.flags = 1; + this.value = this.magicValue; + this.customName = name; + + loadEnchantments(); + bakeInStats(); + } + + public Item( ItemBase itemBase, int ownerID, + OwnerType ownerType, byte chargesMax, byte chargesRemaining, + short durabilityCurrent, short durabilityMax, boolean canDestroy, + boolean rentable, boolean inBank, boolean inVault, + boolean inInventory, boolean isEquipped, boolean isForge, byte equipSlot, + ArrayList enchants) { + + super(); + this.itemBaseID = itemBase.getUUID(); + this.ownerID = ownerID; + this.ownerType = ownerType; + + this.chargesMax = (byte) itemBase.getNumCharges(); + this.chargesRemaining = (byte) itemBase.getNumCharges(); + + this.durabilityMax = (short) itemBase.getDurability(); + this.durabilityCurrent = (short) itemBase.getDurability(); + + this.canDestroy = canDestroy; + this.rentable = rentable; + + this.equipSlot = equipSlot; + this.enchants = enchants; + this.flags = 1; + + this.value = this.magicValue; + + loadEnchantments(); + bakeInStats(); + } + + /** + * Normal Constructor + */ + public Item(ItemBase itemBase, int ownerID, + OwnerType ownerType, byte chargesMax, byte chargesRemaining, + short durabilityCurrent, short durabilityMax, boolean canDestroy, + boolean rentable, boolean inBank, boolean inVault, + boolean inInventory, boolean isEquipped, byte equipSlot, + ArrayList enchants, int newUUID) { + + super(newUUID); + this.itemBaseID = itemBase.getUUID(); + this.ownerID = ownerID; + this.ownerType = ownerType; + this.customName = ""; + + this.chargesMax = (byte) itemBase.getNumCharges(); + this.chargesRemaining = (byte) itemBase.getNumCharges(); + + this.durabilityMax = (short) itemBase.getDurability(); + this.durabilityCurrent = (short) itemBase.getDurability(); + this.canDestroy = canDestroy; + this.rentable = rentable; + this.equipSlot = equipSlot; + this.enchants = enchants; + this.flags = 1; + this.value = this.magicValue; + + loadEnchantments(); + bakeInStats(); + } + /** + * ResultSet Constructor + */ + public Item(ResultSet rs) throws SQLException { + super(rs); + + this.itemBaseID = rs.getInt("item_itemBaseID"); + + // Set container enumeration + + String container = rs.getString("item_container"); + + switch (container) { + case "inventory": + this.containerType = Enum.ItemContainerType.INVENTORY; + break; + case "bank": + this.containerType = Enum.ItemContainerType.BANK; + break; + case "vault": + this.containerType = Enum.ItemContainerType.VAULT; + break; + case "equip": + this.containerType = Enum.ItemContainerType.EQUIPPED; + break; + case "forge": + this.containerType = Enum.ItemContainerType.FORGE; + break; + case "warehouse": + this.containerType = Enum.ItemContainerType.FORGE; + break; + } + + this.ownerID = rs.getInt("parent"); + + if (this.getItemBase() != null) + this.chargesMax = (byte) this.getItemBase().getNumCharges(); + else + this.chargesMax = 0; + + this.chargesRemaining = rs.getByte("item_chargesRemaining"); + + this.durabilityCurrent = rs.getShort("item_durabilityCurrent"); + this.durabilityMax = rs.getShort("item_durabilityMax"); + + String ot = DbManager.ItemQueries.GET_OWNER(this.ownerID); + + if (ot.equals("character")) + this.ownerType = OwnerType.PlayerCharacter; + else if (ot.equals("npc")) + this.ownerType = OwnerType.Npc; + else if (ot.equals("account")) + this.ownerType = OwnerType.Account; + + this.canDestroy = true; + + this.equipSlot = rs.getByte("item_equipSlot"); + + this.numberOfItems = rs.getInt("item_numberOfItems"); + + this.flags = rs.getInt("item_flags"); + this.dateToUpgrade = rs.getLong("item_dateToUpgrade"); + this.value = rs.getInt("item_value"); + this.customName = rs.getString("item_name"); + + + } + + public String getCustomName() { + return customName; + } + + public void setName(String name) { + this.customName = name; + } + + public ItemBase getItemBase() { + return ItemBase.getItemBase(itemBaseID); + } + + public int getItemBaseID() { + return this.itemBaseID; + } + + public int getOwnerID() { + return ownerID; + } + + public OwnerType getOwnerType() { + return ownerType; + } + + public AbstractGameObject getOwner() { + if (this.ownerType == OwnerType.Npc) + return NPC.getFromCache(this.ownerID); + else if (this.ownerType == OwnerType.PlayerCharacter) + return PlayerCharacter.getFromCache(this.ownerID); + else if (this.ownerType == OwnerType.Mob) + return Mob.getFromCache(this.ownerID); + else if (this.ownerType == OwnerType.Account) + return DbManager.AccountQueries.GET_ACCOUNT(this.ownerID); + else + return null; + } + + //Only to be used for trading + public void setOwnerID(int ownerID) { + this.ownerID = ownerID; + } + + public boolean setOwner(AbstractGameObject owner) { + if (owner == null) + return false; + if (owner.getObjectType().equals(GameObjectType.NPC)) + this.ownerType = OwnerType.Npc; + else if (owner.getObjectType().equals(GameObjectType.PlayerCharacter)) + this.ownerType = OwnerType.PlayerCharacter; + else if (owner.getObjectType().equals(GameObjectType.Mob)) + this.ownerType = OwnerType.Mob; + else if (owner.getObjectType().equals(GameObjectType.Account)) + this.ownerType = OwnerType.Account; + else + return false; + this.ownerID = owner.getObjectUUID(); + return true; + } + + public boolean isOwnerNPC() { + return (ownerType == OwnerType.Npc); + } + + public boolean isOwnerCharacter() { + return (ownerType == OwnerType.PlayerCharacter); + } + + public boolean isOwnerAccount() { + return (ownerType == OwnerType.Account); + } + + public byte getChargesMax() { + return chargesMax; + } + + public byte getChargesRemaining() { + return chargesRemaining; + } + + public short getDurabilityCurrent() { + return durabilityCurrent; + } + + public short getDurabilityMax() { + return durabilityMax; + } + + public void setDurabilityCurrent(short value) { + this.durabilityCurrent = value; + } + + public boolean isCanDestroy() { + return canDestroy; + } + + public boolean isRentable() { + return rentable; + } + + public byte getEquipSlot() { + return equipSlot; + } + + public ArrayList getEnchants() { + return enchants; + } + + public int getNumOfItems() { + return this.numberOfItems; + } + + public synchronized void setNumOfItems(int numberOfItems) { + this.numberOfItems = numberOfItems; + } + + public ConcurrentHashMap getBonuses() { + return this.bonuses; + } + + public void clearBonuses() { + this.bonuses.clear(); + } + + + public float getBonus(ModType modType, SourceType sourceType) { + + int amount = 0; + for (AbstractEffectModifier modifier: this.getBonuses().keySet()){ + if (modifier.getPercentMod() != 0) + continue; + if (modifier.modType.equals(modType) == false || modifier.sourceType.equals(sourceType)== false) + continue; + amount += this.bonuses.get(modifier); + } + return amount; + } + +public float getBonusPercent(ModType modType, SourceType sourceType) { + + int amount = 0; + for (AbstractEffectModifier modifier: this.getBonuses().keySet()){ + + if (modifier.getPercentMod() == 0) + continue; + if (modifier.modType.equals(modType) == false || modifier.sourceType.equals(sourceType)== false) + continue; + amount += this.bonuses.get(modifier); + } + return amount; + } + + public boolean isID() { + return ((this.flags & 1) > 0); + } + + public void setIsID(boolean value) { + if (value) + this.flags |= 1; + else + this.flags &= ~1; + } + + public void setIsComplete(boolean value) { + if (value) + this.flags |= 2; + else + this.flags &= ~2; + } + + public boolean isComplete() { + return this.dateToUpgrade < System.currentTimeMillis() + 1000; + } + + public String getContainerInfo() { + String ret = "OwnerID: " + this.ownerID + ", container: "; + ret += this.containerType.toString(); + ret += "Equip Slot: " + this.equipSlot; + return ret; + } + + public int getFlags() { + return this.flags; + } + + public void setFlags(int value) { + this.flags = value; + } + + public void addBonus(AbstractEffectModifier key, float amount) { + if (this.bonuses.containsKey(key)) + this.bonuses.put(key, (this.bonuses.get(key) + amount)); + else + this.bonuses.put(key, amount); + } + + public void multBonus(AbstractEffectModifier key, float amount) { + if (this.bonuses.containsKey(key)) + this.bonuses.put(key, (this.bonuses.get(key) * amount)); + else + this.bonuses.put(key, amount); + } + + public synchronized void decrementChargesRemaining() { + this.chargesRemaining -= 1; + if (this.chargesRemaining < 0) + this.chargesRemaining = 0; + DbManager.ItemQueries.UPDATE_REMAINING_CHARGES(this); + } + + protected void validateItemContainer() { + + if (this.containerType == Enum.ItemContainerType.NONE) + + if (this.ownerID != 0) + // Item has an owner, just somehow the flags got messed up. + // Default to bank. + // TODO NEED LOG EVENT HERE. + this.containerType = Enum.ItemContainerType.BANK; + else + // This item is on the ground. Nothing to worry about. + this.zeroItem(); + } + + // Removes all ownership of item and 'orphans' it. + protected synchronized void junk() { + + DbManager.ItemQueries.UPDATE_OWNER(this, 0, false, false, false, ItemContainerType.NONE, 0); + this.zeroItem(); + + //TODO do we want to delete the item here? + this.lastOwner = null; + //cleanup item from server. + this.removeFromCache(); + } + + public synchronized void zeroItem() { + this.ownerID = 0; + + this.ownerType = null; + this.containerType = Enum.ItemContainerType.NONE; + this.equipSlot = MBServerStatics.SLOT_UNEQUIPPED; + } + + protected synchronized boolean moveItemToInventory(PlayerCharacter pc) { + if (!DbManager.ItemQueries.UPDATE_OWNER(this, + pc.getObjectUUID(), //tableID + false, //isNPC + true, //isPlayer + false, //isAccount + ItemContainerType.INVENTORY, + 0)) //Slot + + return false; + + this.zeroItem(); + this.ownerID = pc.getObjectUUID(); + this.ownerType = OwnerType.PlayerCharacter; + this.containerType = ItemContainerType.INVENTORY; + return true; + } + + protected synchronized boolean moveItemToInventory(NPC npc) { + if (npc.isStatic()) { + if (!DbManager.ItemQueries.UPDATE_OWNER(this, 0, false, false, false,ItemContainerType.INVENTORY, 0)) + return false; + } else + if (!DbManager.ItemQueries.UPDATE_OWNER(this, + npc.getObjectUUID(), //UUID + true, //isNPC + false, //isPlayer + false, //isAccount + ItemContainerType.INVENTORY, + 0)) //Slot + + return false; + this.zeroItem(); + this.ownerID = npc.getObjectUUID(); + this.ownerType = OwnerType.Npc; + this.containerType = Enum.ItemContainerType.INVENTORY; + return true; + } + + protected synchronized boolean moveItemToInventory(Corpse corpse) { + if (!DbManager.ItemQueries.UPDATE_OWNER(this, + 0, //no ID for corpse + false, //isNPC + true, //isPlayer + false, //isAccount + ItemContainerType.INVENTORY, + 0)) //Slot + + return false; + this.zeroItem(); + this.ownerID = 0; + this.ownerType = null; + this.containerType = Enum.ItemContainerType.INVENTORY; + return true; + } + + protected synchronized boolean moveItemToBank(PlayerCharacter pc) { + if (!DbManager.ItemQueries.UPDATE_OWNER(this, + pc.getObjectUUID(), //UUID + false, //isNPC + true, //isPlayer + false, //isAccount + ItemContainerType.BANK, + 0)) //Slot + + return false; + this.zeroItem(); + this.ownerID = pc.getObjectUUID(); + this.ownerType = OwnerType.PlayerCharacter; + this.containerType = Enum.ItemContainerType.BANK; + return true; + } + + protected synchronized boolean moveItemToBank(NPC npc) { + if (!DbManager.ItemQueries.UPDATE_OWNER(this, + npc.getObjectUUID(), //UUID + true, //isNPC + false, //isPlayer + false, //isAccount + ItemContainerType.BANK, + 0)) //Slot + + return false; + this.zeroItem(); + this.ownerID = npc.getObjectUUID(); + this.ownerType = OwnerType.Npc; + this.containerType = Enum.ItemContainerType.BANK; + return true; + } + + protected synchronized boolean moveItemToVault(Account a) { + if (!DbManager.ItemQueries.UPDATE_OWNER(this, + a.getObjectUUID(), //UUID + false, //isNPC + false, //isPlayer + true, //isAccount + ItemContainerType.VAULT, + 0)) //Slot + + return false; + this.zeroItem(); + this.ownerID = a.getObjectUUID(); + this.ownerType = OwnerType.Account; + this.containerType = Enum.ItemContainerType.VAULT; + return true; + } + + protected synchronized boolean equipItem(PlayerCharacter pc, byte slot) { + + if (!DbManager.ItemQueries.UPDATE_OWNER(this, + pc.getObjectUUID(), //tableID + false, //isNPC + true, //isPlayer + false, //isAccount + ItemContainerType.EQUIPPED, + slot)) //Slot + + return false; + this.zeroItem(); + this.ownerID = pc.getObjectUUID(); + this.ownerType = OwnerType.PlayerCharacter; + this.containerType = Enum.ItemContainerType.EQUIPPED; + this.equipSlot = slot; + return true; + } + + protected synchronized boolean equipItem(NPC npc, byte slot) { + if (!DbManager.ItemQueries.UPDATE_OWNER(this, + npc.getObjectUUID(), //UUID + true, //isNPC + false, //isPlayer + false, //isAccount + ItemContainerType.EQUIPPED, + slot)) //Slot + + return false; + this.zeroItem(); + this.ownerID = npc.getObjectUUID(); + this.ownerType = OwnerType.Npc; + this.containerType = Enum.ItemContainerType.EQUIPPED; + this.equipSlot = slot; + return true; + } + + protected synchronized boolean equipItem(Mob npc, byte slot) { + + this.zeroItem(); + this.ownerID = npc.getObjectUUID(); + this.ownerType = OwnerType.Mob; + this.containerType = Enum.ItemContainerType.EQUIPPED; + this.equipSlot = slot; + return true; + } + + + public static void _serializeForClientMsg(Item item, ByteBufferWriter writer) + throws SerializationException { + Item._serializeForClientMsg(item, writer, true); + } + + public static void serializeForClientMsgWithoutSlot(Item item, ByteBufferWriter writer) { + Item._serializeForClientMsg(item, writer, false); + } + + public static void serializeForClientMsgForVendor(Item item, ByteBufferWriter writer, float percent) { + Item._serializeForClientMsg(item, writer, true); + int baseValue = item.magicValue; + writer.putInt(baseValue); + writer.putInt((int) (baseValue * percent)); + } + + public static void serializeForClientMsgForVendorWithoutSlot(Item item,ByteBufferWriter writer, float percent) { + Item._serializeForClientMsg(item, writer, false); + writer.putInt(item.getValue()); + writer.putInt(item.getValue()); + } + + public static void _serializeForClientMsg(Item item,ByteBufferWriter writer, + boolean includeSlot) { + if (includeSlot) + writer.putInt(item.equipSlot); + writer.putInt(0); // Pad + writer.putInt(item.getItemBase().getUUID()); + + writer.putInt(item.getObjectType().ordinal()); + writer.putInt(item.getObjectUUID()); + + // Unknown statics + for (int i = 0; i < 3; i++) { + writer.putInt(0); // Pad + } + for (int i = 0; i < 4; i++) { + writer.putInt(0x3F800000); // Static + } + for (int i = 0; i < 5; i++) { + writer.putInt(0); // Pad + } + for (int i = 0; i < 2; i++) { + writer.putInt(0xFFFFFFFF); // Static + } + + // Handle Hair / Beard / horns Color. + boolean isHair = (item.equipSlot == (byte) MBServerStatics.SLOT_HAIRSTYLE); + boolean isBeard = (item.equipSlot == (byte) MBServerStatics.SLOT_BEARDSTYLE); + int itemColor = 0; + if (isHair || isBeard) { + PlayerCharacter pc = PlayerCharacter.getFromCache(item.ownerID); + if (pc != null) + if (isHair) + itemColor = pc.getHairColor(); + else if (isBeard) + itemColor = pc.getBeardColor(); + } + writer.putInt(itemColor); + + writer.put((byte) 1); // End Datablock byte + if (item.customName.isEmpty() || item.customName.isEmpty()){ + writer.putInt(0); + } + + else + writer.putString(item.customName); // Unknown. pad? + writer.put((byte) 1); // End Datablock byte + + writer.putFloat((float)item.durabilityMax); + writer.putFloat((float)item.durabilityCurrent); + + writer.put((byte) 1); // End Datablock byte + + writer.putInt(0); // Pad + writer.putInt(0); // Pad + + if (item.getItemBase().equals(ItemBase.GOLD_ITEM_BASE)){ + + if (item.getOwner() != null && item.getOwner().getObjectType() == GameObjectType.PlayerCharacter){ + PlayerCharacter player = (PlayerCharacter)item.getOwner(); + int tradingAmount = player.getCharItemManager().getGoldTrading(); + writer.putInt(item.numberOfItems - tradingAmount); + }else + writer.putInt(item.numberOfItems); // Amount of gold + } + + else + writer.putInt(item.getItemBase().getBaseValue()); + + writer.putInt(item.getValue()); + + int effectsSize = item.effects.size(); + ArrayList effs = null; + Effect nextE = null; + if (effectsSize > 0 && item.isID()) { + effs = new ArrayList<>(item.effects.values()); + + //Don't send effects that have a token of 1 + Iterator efi = effs.iterator(); + while (efi.hasNext()) { + nextE = efi.next(); + if (nextE.getEffectToken() == 1 || nextE.bakedInStat()) + efi.remove(); + } + } else + effs = new ArrayList<>(); + + int effectsToSendSize = effs.size(); + writer.putInt(effectsToSendSize); + for (int i = 0; i < effectsToSendSize; i++) { + effs.get(i).serializeForItem(writer, item); + } + writer.putInt(0x00000000); + + + if (effectsSize > 0) + if (item.isID()) + writer.putInt(36); //Magical, blue name + else + writer.putInt(40); //Magical, unidentified + else if (item.getItemBase().getBakedInStats().size() > 0) + writer.putInt(36); //Magical, blue name + else + writer.putInt(4); //Non-Magical, grey name + writer.putInt(item.chargesRemaining); + writer.putInt(0); // Pad + writer.putInt(item.numberOfItems); + writer.put((byte)0); + + + if (item.getItemBase().getType().getValue() != 20){ + writer.putShort((short)0); + return; + } + writer.put((byte)1); // + writer.putInt(0); + writer.putInt(0); + if (item.chargesRemaining == 0) + writer.putInt(1); + else + writer.putInt(item.chargesRemaining); + writer.put((byte) 0); + } + + public static void SerializeTradingGold(PlayerCharacter player,ByteBufferWriter writer) { + + writer.putInt(0); // Pad + writer.putInt(7); + + writer.putInt(GameObjectType.Item.ordinal()); + writer.putInt(player.getObjectUUID()); + + // Unknown statics + for (int i = 0; i < 3; i++) { + writer.putInt(0); // Pad + } + for (int i = 0; i < 4; i++) { + writer.putInt(0x3F800000); // Static + } + for (int i = 0; i < 5; i++) { + writer.putInt(0); // Pad + } + for (int i = 0; i < 2; i++) { + writer.putInt(0xFFFFFFFF); // Static + } + + // Handle Hair / Beard / horns Color. + + int itemColor = 0; + writer.putInt(itemColor); + + writer.put((byte) 1); // End Datablock byte + writer.putInt(0); + writer.put((byte) 1); // End Datablock byte + + writer.putFloat((float)1); + writer.putFloat((float)1); + + writer.put((byte) 1); // End Datablock byte + + writer.putInt(0); // Pad + writer.putInt(0); // Pad + + + writer.putInt(player.getCharItemManager().getGoldTrading()); // Amount of gold + + + writer.putInt(0); + + + writer.putInt(0); + + writer.putInt(0x00000000); + + writer.putInt(4); //Non-Magical, grey name + writer.putInt(1); + writer.putInt(0); // Pad + writer.putInt(player.getCharItemManager().getGoldTrading()); + writer.put((byte)0); + + writer.putShort((short)0); + + } + + public static boolean MakeItemForPlayer(ItemBase toCreate, PlayerCharacter reciever, int amount) { + + boolean itemWorked = false; + + Item item = new Item( toCreate, reciever.getObjectUUID(), OwnerType.PlayerCharacter, (byte) 0, (byte) 0, + (short) 1, (short) 1, true, false, Enum.ItemContainerType.INVENTORY, (byte) 0, + new ArrayList<>(),""); + + synchronized (item) { + item.numberOfItems = amount; + } + item.containerType = Enum.ItemContainerType.INVENTORY; + + try { + item = DbManager.ItemQueries.ADD_ITEM(item); + itemWorked = true; + } catch (Exception e) { + Logger.error(e); + } + + if (!itemWorked) + return false; + + reciever.getCharItemManager().addItemToInventory(item); + reciever.getCharItemManager().updateInventory(); + + return true; + } + + public static Item deserializeFromClientMsg(ByteBufferReader reader, + boolean includeSlot) { + if (includeSlot) + reader.getInt(); + reader.getInt(); + int itemBase = reader.getInt(); //itemBase + int objectType = reader.getInt(); //object type; + int UUID = reader.getInt(); + for (int i = 0; i < 14; i++) { + reader.getInt(); // Pads and statics + } + int unknown = reader.getInt(); + + byte readString = reader.get(); + if (readString == 1) + reader.getString(); + byte readDurability = reader.get(); + if (readDurability == 1){ + reader.getInt(); + reader.getInt(); + } + + byte readEnchants = reader.get(); + if (readEnchants == 1){ + reader.getInt(); + reader.getInt(); + reader.getInt(); + reader.getInt(); + int enchantSize = reader.getInt(); + for (int i = 0; i < enchantSize; i++) { + reader.getInt(); //effect token + reader.getInt(); //trains + int type = reader.getInt(); + reader.get(); + if (type == 1) + reader.getLong(); //item comp + else + reader.getInt(); //power token + reader.getString(); //name + reader.getFloat(); //duration + } + for (int i = 0; i < 5; i++) { + reader.getInt(); + } + } + + reader.get(); + byte isContract = reader.get(); + if (isContract == 1){ + reader.getInt(); + reader.getInt(); + reader.getInt(); + } + reader.get(); + + if (UUID == 0 || objectType == 0) + return null; + if (objectType == GameObjectType.MobLoot.ordinal()) + return MobLoot.getFromCache(UUID); + return Item.getFromCache(UUID); + } + + public final int getMagicValue() { + return this.magicValue; + } + + public int getBaseValue() { + if (this.getItemBase() != null) + return this.getItemBase().getBaseValue(); + return 0; + } + + public static void putListForVendor(ByteBufferWriter writer, ArrayList list, NPC vendor) { + putList(writer, list, false, vendor.getObjectUUID(), true, vendor); + } + + public static void putList(ByteBufferWriter writer, ArrayList list, boolean includeSlot, int ownerID) { + putList(writer, list, includeSlot, ownerID, false, null); + } + + private static void putList(ByteBufferWriter writer, ArrayList list, boolean includeSlot, int ownerID, boolean forVendor, NPC vendor) { + int indexPosition = writer.position(); + //reserve 4 bytes for index. + writer.putInt(0); + + int serialized = 0; + for (Item item : list) { + + if (item.getItemBase().getType().equals(ItemType.GOLD)) + if (item.numberOfItems == 0) + continue; + try { + if (includeSlot && !forVendor) + Item._serializeForClientMsg(item,writer); + else if (!includeSlot && !forVendor) + Item.serializeForClientMsgWithoutSlot(item,writer); + + if (!includeSlot && forVendor) //TODO separate for sell/buy percent + + Item.serializeForClientMsgForVendorWithoutSlot(item,writer, vendor.getSellPercent()); + + if (includeSlot && forVendor) //TODO separate for sell/buy percent + + Item.serializeForClientMsgForVendor(item,writer, vendor.getSellPercent()); + + } catch (SerializationException se) { + continue; + } + ++serialized; + } + + writer.putIntAt(serialized, indexPosition); + } + + public static void putTradingList(PlayerCharacter player, ByteBufferWriter writer, ArrayList list, boolean includeSlot, int ownerID, boolean forVendor, NPC vendor) { + int indexPosition = writer.position(); + //reserve 4 bytes for index. + writer.putInt(0); + + int serialized = 0; + for (Item item : list) { + + if (item.getItemBase().getType().equals(ItemType.GOLD)) + if (item.numberOfItems == 0) + continue; + try { + if (includeSlot && !forVendor) + Item._serializeForClientMsg(item,writer); + else if (!includeSlot && !forVendor) + Item.serializeForClientMsgWithoutSlot(item,writer); + + if (!includeSlot && forVendor) //TODO separate for sell/buy percent + + Item.serializeForClientMsgForVendorWithoutSlot(item,writer, vendor.getSellPercent()); + + if (includeSlot && forVendor) //TODO separate for sell/buy percent + + Item.serializeForClientMsgForVendor(item,writer, vendor.getSellPercent()); + + } catch (SerializationException se) { + continue; + } + ++serialized; + } + if (player.getCharItemManager().getGoldTrading() > 0){ + Item.SerializeTradingGold(player, writer); + ++serialized; + } + + + writer.putIntAt(serialized, indexPosition); + } + + public AbstractWorldObject getLastOwner() { + return this.lastOwner; + } + + public void setLastOwner(AbstractWorldObject value) { + this.lastOwner = value; + } + + + @Override + public String getName() { + if (this.customName.isEmpty()) + if (this.getItemBase() != null) + return this.getItemBase().getName(); + return this.customName; + } + + + + private void bakeInStats() { + + EffectsBase effect; + + if (ConfigManager.serverType.equals(Enum.ServerType.LOGINSERVER)) + return; + + if (this.getItemBase() != null) + + for (Integer token : this.getItemBase().getBakedInStats().keySet()) { + + effect = PowersManager.getEffectByToken(token); + + if (effect == null) { + Logger.error("missing effect of token " + token); + continue; + } + AbstractPowerAction apa = PowersManager.getPowerActionByIDString(effect.getIDString()); + apa.applyBakedInStatsForItem(this, this.getItemBase().getBakedInStats().get(token)); + } + } + + public final void loadEnchantments() { + //dont load mobloot enchantments, they arent in db. + if (this.getObjectType().equals(GameObjectType.MobLoot)){ + this.magicValue = this.getItemBase().getBaseValue() + calcMagicValue(); + return; + } + + + ConcurrentHashMap enchantList = DbManager.EnchantmentQueries.GET_ENCHANTMENTS_FOR_ITEM(this.getObjectUUID()); + + for (String enchant : enchantList.keySet()) { + AbstractPowerAction apa = PowersManager.getPowerActionByIDString(enchant); + if (apa != null) { + apa.applyEffectForItem(this, enchantList.get(enchant)); + this.effectNames.add(enchant); + } + } + + this.magicValue = this.getItemBase().getBaseValue() + calcMagicValue(); + } + + public HashMap getBakedInStats() { + if (this.getItemBase() != null) + return this.getItemBase().getBakedInStats(); + return null; + } + + public void clearEnchantments() { + + //Clear permanent enchantment out of database + DbManager.EnchantmentQueries.CLEAR_ENCHANTMENTS((long) this.getObjectUUID()); + + for (String name : this.getEffects().keySet()) { + Effect eff = this.getEffects().get(name); + if (!eff.bakedInStat()) + this.endEffect(name); + } + this.effectNames.clear(); + } + + public void addPermanentEnchantment(String enchantID, int rank) { + AbstractPowerAction apa = PowersManager.getPowerActionByIDString(enchantID); + if (apa == null) + return; + + DbManager.EnchantmentQueries.CREATE_ENCHANTMENT_FOR_ITEM((long) this.getObjectUUID(), enchantID, rank); + apa.applyEffectForItem(this, rank); + this.effectNames.add(enchantID); + } + + public void addPermanentEnchantmentForDev(String enchantID, int rank) { + AbstractPowerAction apa = PowersManager.getPowerActionByIDString(enchantID); + if (apa == null) + return; + + DbManager.EnchantmentQueries.CREATE_ENCHANTMENT_FOR_ITEM((long) this.getObjectUUID(), enchantID, rank); + apa.applyEffectForItem(this, rank); + this.effectNames.add(enchantID); + } + + protected int calcMagicValue() { + int ret = 0; + for (String enchant : this.effectNames) { + ret += Item.getEnchantValue(enchant+ 'A'); + } + return ret; + } + + public static Item createItemForPlayer(PlayerCharacter pc, ItemBase ib) { + Item item = null; + byte charges = 0; + + charges = (byte) ib.getNumCharges(); + + short durability = (short) ib.getDurability(); + + Item temp = new Item( ib, pc.getObjectUUID(), + OwnerType.PlayerCharacter, charges, charges, durability, durability, + true, false, Enum.ItemContainerType.INVENTORY, (byte) 0, + new ArrayList<>(),""); + try { + item = DbManager.ItemQueries.ADD_ITEM(temp); + } catch (Exception e) { + Logger.error(e); + } + return item; + } + + public static Item createItemForPlayerBank(PlayerCharacter pc, ItemBase ib) { + Item item = null; + byte charges = 0; + + charges = (byte) ib.getNumCharges(); + + short durability = (short) ib.getDurability(); + + Item temp = new Item( ib, pc.getObjectUUID(), + OwnerType.PlayerCharacter, charges, charges, durability, durability, + true, false, Enum.ItemContainerType.BANK, (byte) 0, + new ArrayList<>(),""); + try { + item = DbManager.ItemQueries.ADD_ITEM(temp); + } catch (Exception e) { + } + return item; + } + + public static Item createItemForMob(Mob mob, ItemBase ib) { + Item item = null; + byte charges = 0; + + charges = (byte) ib.getNumCharges(); + short durability = (short) ib.getDurability(); + + Item temp = new Item( ib, mob.getObjectUUID(), + OwnerType.Mob, charges, charges, durability, durability, + true, false, Enum.ItemContainerType.INVENTORY, (byte) 0, + new ArrayList<>(),""); + try { + item = DbManager.ItemQueries.ADD_ITEM(temp); + } catch (Exception e) { + Logger.error(e); + } + return item; + } + + public static Item getFromCache(int id) { + return (Item) DbManager.getFromCache(GameObjectType.Item, id); + } + + + + public void addToCache() { + DbManager.addToCache(this); + } + + public static Item newGoldItem(AbstractWorldObject awo, ItemBase ib, Enum.ItemContainerType containerType) { + return newGoldItem(awo, ib, containerType, true); + } + + //used for vault! + public static Item newGoldItem(int accountID,ItemBase ib, Enum.ItemContainerType containerType) { + return newGoldItem(accountID, ib, containerType, true); + } + + private static Item newGoldItem(int accountID, ItemBase ib, Enum.ItemContainerType containerType, boolean persist) { + + int ownerID; + OwnerType ownerType; + + ownerID = accountID; + ownerType = OwnerType.Account; + + + Item newGold = new Item( ib, ownerID, ownerType, + (byte) 0, (byte) 0, (short) 0, (short) 0, true, false, containerType, (byte) 0, + new ArrayList<>(),""); + + synchronized (newGold) { + newGold.numberOfItems = 0; + } + + if (persist) { + try { + newGold = DbManager.ItemQueries.ADD_ITEM(newGold); + if (newGold != null) { + synchronized (newGold) { + newGold.numberOfItems = 0; + } + } + } catch (Exception e) { + Logger.error(e); + } + DbManager.ItemQueries.ZERO_ITEM_STACK(newGold); + } + + return newGold; + } + + private static Item newGoldItem(AbstractWorldObject awo, ItemBase ib, Enum.ItemContainerType containerType,boolean persist) { + + int ownerID; + OwnerType ownerType; + + if (awo.getObjectType().equals(GameObjectType.Mob)) + return null; + + if (containerType == Enum.ItemContainerType.VAULT) { + if (!(awo.getObjectType().equals(GameObjectType.PlayerCharacter))) { + Logger.error("AWO is not a PlayerCharacter"); + return null; + } + ownerID = ((PlayerCharacter) awo).getAccount().getObjectUUID(); + ownerType = OwnerType.Account; + } else { + + ownerID = awo.getObjectUUID(); + + switch (awo.getObjectType()) { + + case NPC: + ownerType = OwnerType.Npc; + break; + case PlayerCharacter: + ownerType = OwnerType.PlayerCharacter; + break; + case Mob: + ownerType = OwnerType.Mob; + break; + default: + Logger.error("Unsupported AWO object type."); + return null; + } + } + + Item newGold = new Item( ib, ownerID, ownerType, + (byte) 0, (byte) 0, (short) 0, (short) 0, true, false, containerType, (byte) 0, + new ArrayList<>(),""); + + synchronized (newGold) { + newGold.numberOfItems = 0; + } + + if (persist) { + try { + newGold = DbManager.ItemQueries.ADD_ITEM(newGold); + if (newGold != null) { + synchronized (newGold) { + newGold.numberOfItems = 0; + } + } + } catch (Exception e) { + Logger.error(e); + } + DbManager.ItemQueries.ZERO_ITEM_STACK(newGold); + } + newGold.containerType = containerType; + + return newGold; + } + + // This is to be used for trades - the new item is not stored in the database + public static Item newGoldItemTemp(AbstractWorldObject awo, ItemBase ib) { + return Item.newGoldItem(awo, ib, Enum.ItemContainerType.NONE,false); + } + + public static Item getItem(int UUID) { + if (UUID == 0) + return null; + + Item item = (Item) DbManager.getFromCache(GameObjectType.Item, UUID); + if (item != null) + return item; + return DbManager.ItemQueries.GET_ITEM(UUID); + } + + @Override + public void updateDatabase() { + //DbManager.ItemQueries.updateDatabase(this); + } + + public static void addEnchantValue(String enchant, int value) { + Item.enchantValues.put(enchant, value); + } + + public static int getEnchantValue(String enchant) { + if (Item.enchantValues.containsKey(enchant)) + return Item.enchantValues.get(enchant); + return 0; + } + + @Override + public void runAfterLoad() { + loadEnchantments(); + bakeInStats(); + } + + + public ArrayList getEffectNames() { + return effectNames; + } + + public boolean validForItem(long flags) { + if (this.getItemBase() == null) + return false; + return this.getItemBase().validSlotFlag(flags); + } + + public boolean validForInventory(ClientConnection origin, PlayerCharacter pc, CharacterItemManager charItemMan) { + + if (origin == null || pc == null || charItemMan == null) + return false; + + if (ownerID != pc.getObjectUUID()) { + Logger.warn("Inventory Item " + this.getObjectUUID() + " not owned by Character " + charItemMan.getOwner().getObjectUUID()); + charItemMan.updateInventory(); + return false; + } + + if (!charItemMan.inventoryContains(this)){ + charItemMan.updateInventory(); + return false; + } + return true; + } + + public boolean validForBank(ClientConnection origin, PlayerCharacter pc, CharacterItemManager charItemMan) { + if (origin == null || pc == null || charItemMan == null) + return false; + + if (!charItemMan.bankContains(this)) + return false; + else if (ownerID != pc.getObjectUUID()) { + Logger.warn("Bank Item " + this.getObjectUUID() + " not owned by Character " + charItemMan.getOwner().getObjectUUID()); + return false; + } + return true; + } + + public boolean validForEquip(ClientConnection origin, PlayerCharacter pc, CharacterItemManager charItemMan) { + if (origin == null || pc == null || charItemMan == null) + return false; + + if (!charItemMan.equippedContains(this)) + return false; + else if (ownerID != pc.getObjectUUID()) { + //duped item, cleanup + Logger.warn("Duped item id " + + this.getObjectUUID() + " removed from PC " + pc.getObjectUUID() + '.'); + DeleteItemMsg deleteItemMsg = new DeleteItemMsg(this.getObjectType().ordinal(), this.getObjectUUID()); + charItemMan.cleanupDupe(this); + Dispatch dispatch = Dispatch.borrow(pc, deleteItemMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + return false; + } + return true; + } + + public boolean validForVault(ClientConnection origin, PlayerCharacter pc, CharacterItemManager charItemMan) { + if (origin == null || pc == null || charItemMan == null) + return false; + + if (pc.getAccount() == null) + return false; + + if (!pc.getAccount().getVault().contains(this)) + return false; + else if (ownerID != pc.getAccount().getObjectUUID()) { + //duped item, cleanup + Logger.warn("Duped item id " + + this.getObjectUUID() + " removed from PC " + pc.getObjectUUID() + '.'); + DeleteItemMsg deleteItemMsg = new DeleteItemMsg(this.getObjectType().ordinal(), this.getObjectUUID()); + charItemMan.cleanupDupe(this); + Dispatch dispatch = Dispatch.borrow(pc, deleteItemMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + return false; + } + return true; + } + + public long getDateToUpgrade() { + return dateToUpgrade; + } + + public void setDateToUpgrade(long dateToUpgrade) { + this.dateToUpgrade = dateToUpgrade; + } + + /** + * @return the value + */ + public int getValue() { + + if (this.value == 0) + if (this.isID()) { + return this.getMagicValue(); + } + else + return this.getBaseValue(); + + return this.value; + } + + /** + * @param value the value to set + */ + public void setValue(int value) { + this.value = value; + } + + public boolean isRandom() { + return isRandom; + } + + public void setRandom(boolean isRandom) { + this.isRandom = isRandom; + } + + public boolean isCustomValue(){ + if (this.value == 0) + return false; + return true; + } +} diff --git a/src/engine/objects/ItemBase.java b/src/engine/objects/ItemBase.java new file mode 100644 index 00000000..f6b0dee4 --- /dev/null +++ b/src/engine/objects/ItemBase.java @@ -0,0 +1,918 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum.DamageType; +import engine.Enum.GameObjectType; +import engine.Enum.ItemType; +import engine.gameManager.DbManager; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.concurrent.ConcurrentHashMap; + +public class ItemBase { + + public static final byte GOLD_BASE_TYPE = 4; + public static ItemBase GOLD_ITEM_BASE = null; + public static int GOLD_BASE_ID = 7; + public static ArrayList AnniverseryGifts = new ArrayList<>(); + // Internal cache + private static HashMap itemHashIDMap = new HashMap<>(); + private static HashMap _IDsByNames = new HashMap<>(); + public static HashMap _itemBaseByUUID = new HashMap<>(); + private static ArrayList _resourceList = new ArrayList<>(); + private final int uuid; + private final String name; + private float durability; + private int value; + private short weight; + private short color; + private ItemType type; + private int vendorType; + private int modTable; + private int useID; + private int hashID; + private byte useAmount; + // Armor and weapon related values + private int equipFlag; + private int restrictFlag; + private String skillRequired; + private short percentRequired; + private float slashResist; + private float crushResist; + private float pierceResist; + private float blockMod; + private short defense; + private float dexPenalty; + private float speed; + private float range; + private short minDamage; + private short maxDamage; + private String mastery; + private engine.Enum.DamageType damageType; + private boolean twoHanded; + private boolean isConsumable; + private boolean isStackable; + private int numCharges; + // Item stat modifiers + private HashMap bakedInStats = new HashMap<>(); + private HashMap usedStats = new HashMap<>(); + private float parryBonus; + private boolean isStrBased; + private ArrayList animations = new ArrayList<>(); + private ArrayList offHandAnimations = new ArrayList<>(); + private boolean autoID = false; + public static HashMap> ItemBaseTypeMap = new HashMap<>(); + /** + * ResultSet Constructor + */ + public ItemBase(ResultSet rs) throws SQLException { + + this.uuid = rs.getInt("ID"); + this.name = rs.getString("name"); + this.durability = rs.getInt("durability"); + this.value = rs.getInt("value"); + this.weight = rs.getShort("weight"); + this.color = rs.getShort("color"); + this.type = ItemType.valueOf(rs.getString("Type")); + this.useID = rs.getInt("useID"); + this.vendorType = rs.getInt("vendorType"); + this.useAmount = rs.getByte("useAmount"); + this.modTable = rs.getInt("modTable"); + this.hashID = rs.getInt("itemHashID"); + + this.isConsumable = false; + this.isStackable = false; + this.numCharges = rs.getShort("numCharges"); + + this.equipFlag = rs.getInt("equipFlag"); + this.restrictFlag = rs.getInt("restrictFlag"); + this.skillRequired = rs.getString("skillRequired"); + this.percentRequired = rs.getShort("percentRequired"); + this.slashResist = rs.getFloat("slashResist"); + this.crushResist = rs.getFloat("crushResist"); + this.pierceResist = rs.getFloat("pierceResist"); + this.blockMod = rs.getFloat("blockMod"); + this.defense = rs.getShort("defense"); + this.dexPenalty = rs.getFloat("dexPenalty"); + this.parryBonus = rs.getFloat("parryBonus"); + this.isStrBased = (rs.getInt("isStrBased") == 1); + this.speed = rs.getFloat("speed"); + this.range = rs.getFloat("range"); + this.minDamage = rs.getShort("minDamage"); + this.maxDamage = rs.getShort("maxDamage"); + + this.mastery = rs.getString("mastery"); + damageType = DamageType.valueOf(rs.getString("damageType")); + + this.twoHanded = (rs.getInt("twoHanded") == 1); + + switch (this.type) { + case RUNE: + case SCROLL: + case COMMANDROD: + case POTION: + case TEARS: + case GUILDCHARTER: + case DEED: + case CONTRACT: + case WATERBUCKET: + case REALMCHARTER: + case GIFT: + this.isConsumable = true; + break; + case OFFERING: + this.isConsumable = true; + Boon.HandleBoonListsForItemBase(uuid); + break; + case RESOURCE: + this.isStackable = true; + break; + + } + + this.autoIDItemsCheck(); + + try{ + DbManager.ItemBaseQueries.LOAD_ANIMATIONS(this); + }catch(Exception e){ + Logger.error( e.getMessage()); + } + initBakedInStats(); + initializeHashes(); + + } + + public static void addToCache(ItemBase itemBase) { + + _itemBaseByUUID.put(itemBase.uuid, itemBase); + + if (itemBase.type.equals(ItemType.RESOURCE)) + _resourceList.add(itemBase); + + _IDsByNames.put(itemBase.name.toLowerCase().replace(" ", "_"), itemBase.uuid); + } + + public static HashMap getItemHashIDMap() { + return itemHashIDMap; + } + + /* + * Database + */ + public static ItemBase getItemBase(int uuid) { + + return _itemBaseByUUID.get(uuid); + } + + /** + * Get the ItemBase instance for Gold. + * + * @return ItemBase for Gold + */ + public static ItemBase getGoldItemBase() { + if (ItemBase.GOLD_ITEM_BASE == null) + ItemBase.GOLD_ITEM_BASE = getItemBase(7); + return ItemBase.GOLD_ITEM_BASE; + } + + public static int getIDByName(String name) { + if (ItemBase._IDsByNames.containsKey(name)) + return ItemBase._IDsByNames.get(name); + return 0; + } + + /** + * @return the _itemBaseByUUID + */ + public static HashMap getUUIDCache() { + return _itemBaseByUUID; + } + + /** + * @return the _resourceList + */ + public static ArrayList getResourceList() { + return _resourceList; + } + + public static void loadAllItemBases() { + DbManager.ItemBaseQueries.LOAD_ALL_ITEMBASES(); + AnniverseryGifts.add(971000); + AnniverseryGifts.add(971001); + AnniverseryGifts.add(971002); + AnniverseryGifts.add(971003); + AnniverseryGifts.add(971004); + AnniverseryGifts.add(971005); + AnniverseryGifts.add(971006); + AnniverseryGifts.add(971007); + AnniverseryGifts.add(971008); + AnniverseryGifts.add(971009); + AnniverseryGifts.add(971010); + AnniverseryGifts.add(5101000); + AnniverseryGifts.add(5101020); + AnniverseryGifts.add(5101100); + AnniverseryGifts.add(5101120); + AnniverseryGifts.add(5101040); + AnniverseryGifts.add(5101140); + AnniverseryGifts.add(5101060); + AnniverseryGifts.add(5101080); + + + } + + /* + * Getters + */ + public String getName() { + return this.name; + } + + public float getDurability() { + return this.durability; + } + + private void initBakedInStats() { + DbManager.ItemBaseQueries.LOAD_BAKEDINSTATS(this); + } + + //TODO fix this later. Shouldn't be gotten from item base + public int getMagicValue() { + return this.value; + } + + public int getBaseValue() { + return this.value; + } + + public short getWeight() { + return this.weight; + } + + public int getColor() { + return this.color; + } + + public boolean isConsumable() { + return this.isConsumable; + } + + public boolean isStackable() { + return this.isStackable; + } + + public int getNumCharges() { + + return this.numCharges; + + } + + public int getEquipFlag() { + + if ((this.type == ItemType.ARMOR) + || (this.type == ItemType.WEAPON) + || (this.type == ItemType.JEWELRY)) + return this.equipFlag; + else + return 0; + } + + public boolean isRune() { + int ID = uuid; + if (ID > 2499 && ID < 3050) //class, discipline runes + return true; + else return ID > 249999 && ID < 252137; + } + + public boolean isStatRune() { + int ID = uuid; + return ID > 249999 && ID < 250045; + } + + public boolean isGlass() { + int ID = uuid; + return ID > 7000099 && ID < 7000281; + } + + + public boolean isMasteryRune() { + int ID = uuid; + if (ID > 250114 && ID < 252128) + switch (ID) { + case 250115: + case 250118: + case 250119: + case 250120: + case 250121: + case 250122: + case 252123: + case 252124: + case 252125: + case 252126: + case 252127: + return true; + default: + return false; + } + return false; + } + + //returns powers tokens baked in to item + public HashMap getBakedInStats() { + return this.bakedInStats; + } + + //returns power tokens granted when using item, such as scrolls and potions + public HashMap getUsedStats() { + return this.usedStats; + } + + public final void initializeHashes() { + itemHashIDMap.put(this.hashID, uuid); + + } + + public ItemType getType() { + return this.type; + } + + public int getUseID() { + return this.useID; + } + + public byte getUseAmount() { + return this.useAmount; + } + + public int getModTable() { + return modTable; + } + + public int getVendorType() { + return vendorType; + } + + public void setVendorType(int vendorType) { + this.vendorType = vendorType; + } + + public int getHashID() { + return hashID; + } + + public void setHashID(int hashID) { + this.hashID = hashID; + } + + private void autoIDItemsCheck(){ + //AUto ID Vorg and Glass + switch (uuid){ + + case 27550: + case 27560: + case 27580: + case 27590: + case 188500: + case 188510: + case 188520: + case 188530: + case 188540: + case 188550: + case 189100: + case 189110: + case 189120: + case 189130: + case 189140: + case 189150: + case 189510: + case 27600: + case 181840: + case 188700: + case 188720: + case 189550: + case 189560: + case 7000100: + case 7000110: + case 7000120: + case 7000130: + case 7000140: + case 7000150: + case 7000160: + case 7000170: + case 7000180: + case 7000190: + case 7000200: + case 7000210: + case 7000220: + case 7000230: + case 7000240: + case 7000250: + case 7000270: + case 7000280: + this.autoID = true; + break; + default: + this.autoID = false; + } + } + + public boolean validForSkills(ConcurrentHashMap skills) { + + CharacterSkill characterSkill; + + if (this.skillRequired.isEmpty()) + return true; + + characterSkill = skills.get(this.skillRequired); + + if (characterSkill == null) + return false; + + return !(this.percentRequired > characterSkill.getModifiedAmountBeforeMods()); + } + + public boolean canEquip(int slot, CharacterItemManager itemManager, AbstractCharacter abstractCharacter, Item item) { + + if (itemManager == null || abstractCharacter == null) + return false; + + if (abstractCharacter.getObjectType().equals(GameObjectType.PlayerCharacter)) { + + if (!validForSlot(slot, itemManager.getEquipped(), item)) + return false; + + if (!validForSkills(abstractCharacter.getSkills())) + return false; + + return item.getItemBase().value != 0 || Kit.IsNoobGear(item.getItemBase().uuid); + //players can't wear 0 value items. + + } + + return true; //Mobiles and NPC's don't need to check equip + } + + public int getValidSlot() { + int slotValue = 0; + + switch (this.type) { + case WEAPON: + if ((this.equipFlag & 1) != 0) + slotValue = MBServerStatics.SLOT_MAINHAND; + else if ((this.equipFlag & 2) != 0) + slotValue = MBServerStatics.SLOT_OFFHAND; + break; + case ARMOR: + if ((this.equipFlag & 2) != 0) + slotValue = MBServerStatics.SLOT_OFFHAND; + else if ((this.equipFlag & 4) != 0) + slotValue = MBServerStatics.SLOT_HELMET; + else if ((this.equipFlag & 8) != 0) + slotValue = MBServerStatics.SLOT_CHEST; + else if ((this.equipFlag & 16) != 0) + slotValue = MBServerStatics.SLOT_ARMS; + else if ((this.equipFlag & 32) != 0) + slotValue = MBServerStatics.SLOT_GLOVES; + else if ((this.equipFlag & 64) != 0) + slotValue = MBServerStatics.SLOT_RING2; + else if ((this.equipFlag & 128) != 0) + slotValue = MBServerStatics.SLOT_RING1; + else if ((this.equipFlag & 256) != 0) + slotValue = MBServerStatics.SLOT_NECKLACE; + else if ((this.equipFlag & 512) != 0) + slotValue = MBServerStatics.SLOT_LEGGINGS; + else if ((this.equipFlag & 1024) != 0) + slotValue = MBServerStatics.SLOT_FEET; + break; + + case HAIR: + if (this.equipFlag == 131072) + slotValue = MBServerStatics.SLOT_HAIRSTYLE; + else if(this.equipFlag == 65536) + slotValue = MBServerStatics.SLOT_BEARDSTYLE; + break; + + } + return slotValue; + + } + + public boolean validSlotFlag(long flags) { + + boolean validSlot = false; + + switch (this.type) { + case WEAPON: + if (this.isMelee()) + validSlot = ((flags & 1) != 0); + else if (this.isThrowing()) + validSlot = ((flags & 2) != 0); + else if (this.isArchery()) + validSlot = ((flags & 4) != 0); + else if (this.isScepter()) + validSlot = ((flags & 8) != 0); + else if (this.isStaff()) + validSlot = ((flags & 16) != 0); + break; + case JEWELRY: + if (this.isNecklace()) + validSlot = ((flags & 2147483648L) != 0L); + else + validSlot = ((flags & 4294967296L) != 0L); + break; + case ARMOR: + + if (this.isShield()) { + validSlot = ((flags & 32) != 0); + break; + } + + if (this.isClothArmor()) { + + if (this.getEquipFlag() == 4) //hood + validSlot = ((flags & 64) != 0); + else if (this.getEquipFlag() == 8) { + if ((restrictFlag & 512) != 0) //Robe + validSlot = ((flags & 128) != 0); + else + validSlot = ((flags & 1024) != 0); //Tunic/Shirt + + break; + } else if (this.getEquipFlag() == 16) //Sleeves + validSlot = ((flags & 2048) != 0); + else if (this.getEquipFlag() == 32) //Gloves + validSlot = ((flags & 512) != 0); + else if (this.getEquipFlag() == 512) //Pants + validSlot = ((flags & 4096) != 0); + else if (this.getEquipFlag() == 1024) //Boots + validSlot = ((flags & 256) != 0); + + break; + } + + if (this.isLightArmor()) { + if (this.getEquipFlag() == 4) //helm + validSlot = ((flags & 8192) != 0); + else if (this.getEquipFlag() == 8) //Chest + validSlot = ((flags & 16384) != 0); + else if (this.getEquipFlag() == 16) //Sleeves + validSlot = ((flags & 32768) != 0); + else if (this.getEquipFlag() == 32) //Gloves + validSlot = ((flags & 65536) != 0); + else if (this.getEquipFlag() == 512) //Pants + validSlot = ((flags & 131072) != 0); + else if (this.getEquipFlag() == 1024) //Boots + validSlot = ((flags & 262144) != 0); + + break; + } + + if (this.isMediumArmor()) { + if (this.getEquipFlag() == 4) //helm + validSlot = ((flags & 524288) != 0); + else if (this.getEquipFlag() == 8) //Chest + validSlot = ((flags & 1048576) != 0); + else if (this.getEquipFlag() == 16) //Sleeves + validSlot = ((flags & 2097152) != 0); + else if (this.getEquipFlag() == 32) //Gloves + validSlot = ((flags & 4194304) != 0); + else if (this.getEquipFlag() == 512) //Pants + validSlot = ((flags & 8388608) != 0); + else if (this.getEquipFlag() == 1024) //Boots + validSlot = ((flags & 16777216) != 0); + + break; + } + + if (this.isHeavyArmor()) + if (this.getEquipFlag() == 4) //helm + validSlot = ((flags & 33554432) != 0); + else if (this.getEquipFlag() == 8) //Chest + validSlot = ((flags & 67108864) != 0); + else if (this.getEquipFlag() == 16) //Sleeves + validSlot = ((flags & 134217728) != 0); + else if (this.getEquipFlag() == 32) //Gloves + validSlot = ((flags & 268435456) != 0); + else if (this.getEquipFlag() == 512) //Pants + validSlot = ((flags & 536870912) != 0); + else if (this.getEquipFlag() == 1024) //Boots + validSlot = ((flags & 1073741824) != 0); + break; + } + return validSlot; + } + + public boolean validForSlot(int slot, ConcurrentHashMap equipped, Item item) { + + boolean validSlot = false; + + if (equipped == null) + return validSlot; + + // Cannot equip an item in a slot already taken + if (equipped.get(slot) != null && equipped.get(slot).equals(item) == false) + return validSlot; + + switch (item.getItemBase().type) { + case WEAPON: + + // Only two slots available for weapons + if ((slot != MBServerStatics.SLOT_MAINHAND) && (slot != MBServerStatics.SLOT_OFFHAND)) + break; + + //make sure weapon is valid for slot + if ((slot & this.equipFlag) == 0) + break; + + // Two handed weapons take up two slots + if ((this.twoHanded == true) && + ((slot == MBServerStatics.SLOT_OFFHAND && equipped.get(MBServerStatics.SLOT_MAINHAND) != null) || + (slot == MBServerStatics.SLOT_MAINHAND && equipped.get(MBServerStatics.SLOT_OFFHAND) != null))) + break; + + // Validation passed, must be a valid weapon + + validSlot = true; + break; + case JEWELRY: + // Not a valid slot for ring + + if (this.isRing() && + ((slot != MBServerStatics.SLOT_RING1) && (slot != MBServerStatics.SLOT_RING2))) + break; + + // Not a valid slot for necklace + + if (this.isNecklace() && slot != MBServerStatics.SLOT_NECKLACE) + break; + + // Passed validation, must be valid bling bling + + validSlot = true; + break; + case ARMOR: + + // Invalid slot for armor? + if (slot == MBServerStatics.SLOT_OFFHAND && ((2 & this.equipFlag) == 0)) + break; + if (slot == MBServerStatics.SLOT_HELMET && ((4 & this.equipFlag) == 0)) + break; + if (slot == MBServerStatics.SLOT_CHEST && ((8 & this.equipFlag) == 0)) + break; + if (slot == MBServerStatics.SLOT_ARMS && ((16 & this.equipFlag) == 0)) + break; + if (slot == MBServerStatics.SLOT_GLOVES && ((32 & this.equipFlag) == 0)) + break; + if (slot == MBServerStatics.SLOT_LEGGINGS && ((512 & this.equipFlag) == 0)) + break; + if (slot == MBServerStatics.SLOT_FEET && ((1024 & this.equipFlag) == 0)) + break; + + // Is slot for this piece already taken? + if (((this.restrictFlag & 2) != 0) && (equipped.get(MBServerStatics.SLOT_OFFHAND) != null) && slot != MBServerStatics.SLOT_OFFHAND) + break; + if (((this.restrictFlag & 4) != 0) && (equipped.get(MBServerStatics.SLOT_HELMET) != null) && slot != MBServerStatics.SLOT_HELMET) + break; + if (((this.restrictFlag & 8) != 0) && (equipped.get(MBServerStatics.SLOT_CHEST) != null) && slot != MBServerStatics.SLOT_CHEST) + break; + if (((this.restrictFlag & 16) != 0) && (equipped.get(MBServerStatics.SLOT_ARMS) != null) && slot != MBServerStatics.SLOT_ARMS) + break; + if (((this.restrictFlag & 32) != 0) && (equipped.get(MBServerStatics.SLOT_GLOVES) != null) && slot != MBServerStatics.SLOT_GLOVES) + break; + if (((this.restrictFlag & 512) != 0) && (equipped.get(MBServerStatics.SLOT_LEGGINGS) != null) && slot != MBServerStatics.SLOT_LEGGINGS) + break; + if (((this.restrictFlag & 1024) != 0) && (equipped.get(MBServerStatics.SLOT_FEET) != null) && slot != MBServerStatics.SLOT_FEET) + break; + + // Passed validation. Is a valid armor piece + + validSlot = true; + break; + } + return validSlot; + } + + /** + * @return the uuid + */ + public final int getUUID() { + return uuid; + } + + public boolean isRing() { + return ((this.equipFlag & (64 | 128 | 192)) != 0); + } + + public boolean isNecklace() { + return (this.equipFlag == 256); + } + + public boolean isShield() { + return this.type.equals(ItemType.ARMOR) && this.equipFlag == 2; + } + + public boolean isLightArmor() { + return this.skillRequired.equals("Wear Armor, Light"); + } + + public boolean isMediumArmor() { + return this.skillRequired.equals("Wear Armor, Medium"); + } + + public boolean isHeavyArmor() { + return this.skillRequired.equals("Wear Armor, Heavy"); + } + + public boolean isClothArmor() { + return this.skillRequired.isEmpty(); + } + + public boolean isThrowing() { + return this.mastery.equals("Throwing") ? true : false; + } + + public boolean isStaff() { + return this.mastery.equals("Staff") ? true : false; + } + + public boolean isScepter() { + return this.mastery.equals("Benediction") ? true : false; + } + + public boolean isArchery() { + return this.mastery.equals("Archery") ? true : false; + } + + public boolean isMelee() { + return (this.isThrowing() == false && this.isStaff() == false && this.isScepter() == false && this.isArchery() == false); + } + + public boolean isTwoHanded() { + return this.twoHanded; + } + + /** + * @return the restrictFlag + */ + public int getRestrictFlag() { + return restrictFlag; + } + + /** + * @return the slashResist + */ + public float getSlashResist() { + return slashResist; + } + + /** + * @return the crushResist + */ + public float getCrushResist() { + return crushResist; + } + + /** + * @return the pierceResist + */ + public float getPierceResist() { + return pierceResist; + } + + /** + * @return the skillRequired + */ + public String getSkillRequired() { + return skillRequired; + } + + /** + * @return the mastery + */ + public String getMastery() { + return mastery; + } + + /** + * @return the blockMod + */ + public float getBlockMod() { + return blockMod; + } + + /** + * @return the defense + */ + public short getDefense() { + return defense; + } + + /** + * @return the dexPenalty + */ + public float getDexPenalty() { + return dexPenalty; + } + + /** + * @return the speed + */ + public float getSpeed() { + return speed; + } + + /** + * @return the range + */ + public float getRange() { + return range; + } + + /** + * @return the isStrBased + */ + public boolean isStrBased() { + return isStrBased; + } + + /** + * @return the parryBonus + */ + public float getParryBonus() { + return parryBonus; + } + + /** + * @return the maxDamage + */ + public short getMaxDamage() { + return maxDamage; + } + + /** + * @return the minDamage + */ + public short getMinDamage() { + return minDamage; + } + + /** + * @return the damageType + */ + public engine.Enum.DamageType getDamageType() { + return damageType; + } + + public short getPercentRequired() { + return percentRequired; + } + + public ArrayList getAnimations() { + return animations; + } + + public void setAnimations(ArrayList animations) { + this.animations = animations; + } + + public ArrayList getOffHandAnimations() { + return offHandAnimations; + } + + public void setOffHandAnimations(ArrayList offHandAnimations) { + this.offHandAnimations = offHandAnimations; + } + + public boolean isAutoID() { + return autoID; + } + + public void setAutoID(boolean autoID) { + this.autoID = autoID; + } +} diff --git a/src/engine/objects/ItemContainer.java b/src/engine/objects/ItemContainer.java new file mode 100644 index 00000000..558fb3f5 --- /dev/null +++ b/src/engine/objects/ItemContainer.java @@ -0,0 +1,102 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + package engine.objects; + +import engine.Enum.ContainerType; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.concurrent.ConcurrentHashMap; + + +public class ItemContainer extends AbstractGameObject { + + private AbstractWorldObject owner; + private ConcurrentHashMapitemMap = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + + private ContainerType containerType; + + /** + * No Table ID Constructor + */ + public ItemContainer(AbstractWorldObject owner, ContainerType containerType) { + super(); + this.owner = owner; + this.containerType = containerType; + } + + /** + * Normal Constructor + */ + public ItemContainer(AbstractWorldObject owner, ContainerType containerType, int newUUID) { + super(newUUID); + this.owner = owner; + this.containerType = containerType; + } + + /** + * ResultSet Constructor + */ + public ItemContainer(ResultSet rs) throws SQLException { + super(rs); + + //get owner + long ownerID = rs.getLong("parent"); + this.owner = (AbstractWorldObject)AbstractGameObject.getFromTypeAndID(ownerID); + + //get ContainerType + String ct = rs.getString("container_type"); + try { + this.containerType = ContainerType.valueOf(ct.toUpperCase()); + } catch (Exception e) { + this.containerType = ContainerType.INVENTORY; + Logger.error( "invalid containerType"); + } + } + + /* + * Getters + */ + public AbstractWorldObject getOwner() { + return this.owner; + } + + public ConcurrentHashMap getItemMap() { + return this.itemMap; + } + + public ContainerType getContainerType() { + return this.containerType; + } + + public boolean isBank() { + return (this.containerType == ContainerType.BANK); + } + + public boolean isInventory() { + return (this.containerType == ContainerType.INVENTORY); + } + + public boolean isVault() { + return (this.containerType == ContainerType.VAULT); + } + + public boolean containsItem(long itemID) { + return this.itemMap.containsKey(itemID); + } + + @Override + public void updateDatabase() { + // TODO Create update logic. + } + +} diff --git a/src/engine/objects/ItemFactory.java b/src/engine/objects/ItemFactory.java new file mode 100644 index 00000000..92491684 --- /dev/null +++ b/src/engine/objects/ItemFactory.java @@ -0,0 +1,1202 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum; +import engine.Enum.ItemContainerType; +import engine.Enum.ItemType; +import engine.Enum.OwnerType; +import engine.gameManager.BuildingManager; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.gameManager.PowersManager; +import engine.net.ItemProductionManager; +import engine.net.ItemQueue; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ErrorPopupMsg; +import engine.powers.EffectsBase; +import engine.server.MBServerStatics; +import org.joda.time.DateTime; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; + +public class ItemFactory { + + public static void fillInventory(PlayerCharacter pc, int objectID, int count) { + + if(pc == null) + return; + + int max = 20; + CharacterItemManager itemManager = pc.getCharItemManager(); + ItemBase ib = ItemBase.getItemBase(objectID); + if (count > max) + count = max; + + ClientConnection cc = pc.getClientConnection(); + + if(itemManager == null || ib == null || cc == null) + return; + + boolean worked; + for (int i = 0; i < count; i++) { + worked = false; + + if(!itemManager.hasRoomInventory(ib.getWeight())) { + if (pc != null) + ChatManager.chatSystemInfo(pc, "You can not carry any more of that item."); + break; + } + + Item item = new Item(ib, pc.getObjectUUID(), OwnerType.PlayerCharacter, (byte) 0, (byte) 0, + (short) 1, (short) 1, true, false, ItemContainerType.INVENTORY, (byte) 0, + new ArrayList<>(),""); + try { + item = DbManager.ItemQueries.ADD_ITEM(item); + worked = true; + } catch (Exception e) { + Logger.error(e); + } + if (worked) { + itemManager.addItemToInventory(item); + } + } + itemManager.updateInventory(); + } + public static Item fillForge(NPC npc, PlayerCharacter pc,int itemsToRoll, int itemID, int pToken, int sToken, String customName) { + + String prefixString = ""; + String suffixString = ""; + if(npc == null) + return null; + + boolean useWarehouse = false; + + ItemBase ib = ItemBase.getItemBase(itemID); + + if (ib == null) + return null; + + Building forge = npc.getBuilding(); + + if (forge == null) + return null; + + + + if (!npc.getCharItemManager().hasRoomInventory(ib.getWeight())){ + if (pc!= null) + ErrorPopupMsg.sendErrorPopup(pc, 21); + return null; + } + + Zone zone = npc.getBuilding().getParentZone(); + + if (zone == null) + return null; + + City city = City.getCity(zone.getPlayerCityUUID()); + + if (city == null) + return null; + MobLoot ml = null; + city.transactionLock.writeLock().lock(); + + try{ + Warehouse cityWarehouse = city.getWarehouse(); + + if (cityWarehouse != null && forge.assetIsProtected()) + useWarehouse = true; + // ROLL BANE SCROLL. + + if (ib.getUUID() > 910010 && ib.getUUID() < 910019){ + ConcurrentHashMap resources = cityWarehouse.getResources(); + + + + int buildingWithdraw = BuildingManager.GetWithdrawAmountForRolling(forge, ib.getBaseValue()); + int overdraft = BuildingManager.GetOverdraft(forge, ib.getBaseValue()); + + if (overdraft > 0 && !useWarehouse){ + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Not enough gold in building strongbox." + " " + ib.getName()); + return null; + } + + if (overdraft > 0 && cityWarehouse.isResourceLocked(ItemBase.GOLD_ITEM_BASE)){ + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Warehouse gold is barred! Overdraft cannot be withdrawn from warehouse." + " " + ib.getName()); + return null; + } + + if (overdraft > resources.get(ItemBase.GOLD_ITEM_BASE)){ + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse for overdraft." + " " + ib.getName()); + return null; + } + + //All checks passed, lets withdraw from building first. + + // if (pc != null){ + // ChatManager.chatGuildInfo(pc.getGuild(), "Building withdraw = " + buildingWithdraw); + // ChatManager.chatGuildInfo(pc.getGuild(), "Warehouse overdraft withdraw = " + overdraft); + // + // ChatManager.chatGuildInfo(pc.getGuild(), "total withdraw = " + (overdraft + buildingWithdraw)); + // } + + if (!forge.transferGold(-buildingWithdraw,false)){ + overdraft += buildingWithdraw; + + if (!useWarehouse){ + ErrorPopupMsg.sendErrorMsg(pc, "Building does not have enough gold to produce this item."+ ib.getName()); + return null; + }else{ + if (overdraft > resources.get(ItemBase.GOLD_ITEM_BASE)){ + ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse to produce this item."+ ib.getName()); + return null; + } + } + } + + if (overdraft > 0) + if(!cityWarehouse.withdraw(npc, ItemBase.GOLD_ITEM_BASE, overdraft, false,true)){ + //ChatManager.chatGuildError(pc, "Failed to create Item"); + Logger.error( "Warehouse With UID of " + cityWarehouse.getUID() + " Failed to Create Item."+ ib.getName()); + return null; + } + + + + + + ml = new MobLoot(npc, ib, false); + + + ml.containerType = Enum.ItemContainerType.FORGE; + ml.setValue(0); + ml.loadEnchantments(); + + float time; + float rank = npc.getBuilding().getRank() - 1; + float rate = (float) (2.5 * rank); + time = (20 - rate); + time *= MBServerStatics.ONE_MINUTE; + + if (ml.getItemBase().getUUID() > 910010 && ml.getItemBase().getUUID() < 910019){ + rank = ml.getItemBaseID() - 910010; + time = rank * 60 * 60 * 3 * 1000; + + } + + // No job is submitted, as object's upgradetime field + // is used to determin whether or not an object has + // compelted rolling. The game object exists previously + // to this, not when 'compelte' is pressed. + long upgradeTime = System.currentTimeMillis() + (long)(time * MBServerStatics.PRODUCTION_TIME_MULTIPLIER) ; + + DateTime dateTime = new DateTime(); + dateTime = dateTime.withMillis(upgradeTime); + ml.setDateToUpgrade(upgradeTime); + + npc.addItemToForge(ml); + + int playerID = 0; + + if (pc != null) + playerID = pc.getObjectUUID(); + DbManager.NPCQueries.ADD_TO_PRODUCTION_LIST(ml.getObjectUUID(),npc.getObjectUUID(), ml.getItemBaseID(), dateTime, "", "", "", false,playerID); + ProducedItem pi = new ProducedItem(ml.getObjectUUID(),npc.getObjectUUID(),ml.getItemBaseID(),dateTime,false,"", "", "",playerID); + pi.setProducedItemID(ml.getObjectUUID()); + pi.setAmount(itemsToRoll); + pi.setRandom(false); + + ItemQueue produced = ItemQueue.borrow(pi, (long) (time * MBServerStatics.PRODUCTION_TIME_MULTIPLIER)); + ItemProductionManager.send(produced); + + return ml; + } + + + + int galvorAmount = 0; + int wormwoodAmount = 0; + int prefixCost = 0; + int suffixCost = 0; + + + if (ib.getType() == ItemType.WEAPON && ib.getPercentRequired() == 110){ + switch (ib.getSkillRequired()){ + case "Bow": + case "Crossbow": + case "Spear": + case "Pole Arm": + case "Staff": + wormwoodAmount = 20; + break; + case "Axe": + case "Dagger": + case "Sword": + case "Hammer": + case "Unarmed Combat": + + if (ib.isTwoHanded()) + galvorAmount = 20; + else + galvorAmount = 10; + break; + } + } + + ItemBase galvor = ItemBase.getItemBase(1580017); + ItemBase wormwood = ItemBase.getItemBase(1580018); + + if (galvorAmount > 0 || wormwoodAmount > 0) + if (!useWarehouse){ + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "This item requires resources to roll! Please make sure the forge is protected to access the warehouse."+ ib.getName()); + return null; + + } + + if (galvorAmount > 0){ + if (cityWarehouse.isResourceLocked(galvor)){ + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Galvor is locked."+ ib.getName()); + return null; + } + + if (cityWarehouse.getResources().get(galvor) < galvorAmount){ + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Not enough Galvor in warehouse to roll this item."+ ib.getName()); + return null; + } + } + + if (wormwoodAmount > 0){ + if (cityWarehouse.isResourceLocked(wormwood)){ + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Wormwood is locked."+ ib.getName()); + return null; + } + + if (cityWarehouse.getResources().get(wormwood) < wormwoodAmount){ + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Not enough Wormwood in warehouse to roll this item."+ ib.getName()); + return null; + } + } + ConcurrentHashMap suffixResourceCosts = null; + ConcurrentHashMap prefixResourceCosts = null; + EffectsBase prefix = null; + if (pToken != 0){ + + if (!useWarehouse){ + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Forge cannot access warehouse! Check to make sure forge is protected."+ ib.getName()); + return null; + } + prefix = PowersManager.getEffectByToken(pToken); + if (prefix == null) + return null; + EffectsBase prefixValue = PowersManager.getEffectByIDString(prefix.getIDString() + 'A'); + if (prefixValue == null) + return null; + + int baseCost = ib.getBaseValue(); + int effectCost = (int) prefixValue.getValue(); + int total = baseCost * 10 + effectCost; + + prefixCost = effectCost; + int buildingWithdraw = BuildingManager.GetWithdrawAmountForRolling(forge, total); + int overdraft = BuildingManager.GetOverdraft(forge, total); + + if (overdraft > 0 && !useWarehouse){ + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Not enough gold in building strongbox."+ ib.getName()); + return null; + } + + if (overdraft > 0 && cityWarehouse.isResourceLocked(ItemBase.GOLD_ITEM_BASE)){ + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Warehouse gold is barred! Overdraft cannot be withdrawn from warehouse."+ ib.getName()); + return null; + } + + if (overdraft > cityWarehouse.getResources().get(ItemBase.GOLD_ITEM_BASE)){ + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse for overdraft."+ ib.getName()); + return null; + } + prefixResourceCosts = prefix.getResourcesForEffect(); + for (ItemBase ibResources: prefixResourceCosts.keySet()){ + int warehouseAmount = cityWarehouse.getResources().get(ibResources); + int creationAmount = prefixResourceCosts.get(ibResources); + //ChatManager.chatInfoError(pc, "Prefix : " + ibResources.getName() + " / " + creationAmount); + if (warehouseAmount < creationAmount){ + //ChatManager.chatInfoError(pc, "You need at least " + creationAmount + " " + ibResources.getName() + " to Create this item."); + return null; + } + + } + } + + EffectsBase suffix = null; + if (sToken != 0){ + + if (!useWarehouse){ + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Forge cannot access warehouse! Check to make sure forge is protected."+ ib.getName()); + return null; + } + suffix = PowersManager.getEffectByToken(sToken); + if (suffix == null) + return null; + EffectsBase suffixValue = PowersManager.getEffectByIDString(suffix.getIDString() + 'A'); + if (suffixValue == null) + return null; + suffixResourceCosts = suffix.getResourcesForEffect(); + int baseCost = ib.getBaseValue(); + int effectCost = (int) suffixValue.getValue(); + suffixCost = effectCost; + int total = baseCost * 10 + effectCost; + + + + // int buildingWithdraw = Building.GetWithdrawAmountForRolling(forge, total); + int overdraft = BuildingManager.GetOverdraft(forge, total); + + if (overdraft > 0 && !useWarehouse){ + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Not enough gold in building strongbox."+ ib.getName()); + return null; + } + + if (overdraft > 0 && cityWarehouse.isResourceLocked(ItemBase.GOLD_ITEM_BASE)){ + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Warehouse gold is barred! Overdraft cannot be withdrawn from warehouse."+ ib.getName()); + return null; + } + + if (overdraft > cityWarehouse.getResources().get(ItemBase.GOLD_ITEM_BASE)){ + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse for overdraft."+ ib.getName()); + return null; + } + + + for (ItemBase ibResources: suffixResourceCosts.keySet()){ + int warehouseAmount = cityWarehouse.getResources().get(ibResources); + int creationAmount = suffixResourceCosts.get(ibResources); + if (warehouseAmount < creationAmount){ + // if (pc != null) + // ChatManager.chatInfoError(pc, "You need at least " + creationAmount + " " + ibResources.getName() + " to Create this item."); + return null; + } + + + } + + } + + + //Check if Total suffix and prefix costs + itemCost can be withdrawn. + int costToCreate = suffixCost + prefixCost + (ib.getBaseValue()); + int buildingWithdraw = BuildingManager.GetWithdrawAmountForRolling(forge, costToCreate); + + int overdraft = BuildingManager.GetOverdraft(forge, costToCreate); + + if (overdraft > 0 && !useWarehouse){ + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Not enough gold in building strongbox."+ ib.getName()); + return null; + } + + if (overdraft > 0 && useWarehouse && cityWarehouse.isResourceLocked(ItemBase.GOLD_ITEM_BASE)){ + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Warehouse gold is barred! Overdraft cannot be withdrawn from warehouse."+ ib.getName()); + return null; + } + + if (useWarehouse && overdraft > cityWarehouse.getResources().get(ItemBase.GOLD_ITEM_BASE)){ + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse for overdraft."+ ib.getName()); + return null; + } + + // if (pc != null){ + // ChatManager.chatGuildInfo(pc.getGuild(), "Building withdraw = " + buildingWithdraw); + // ChatManager.chatGuildInfo(pc.getGuild(), "Warehouse overdraft withdraw = " + overdraft); + // + // ChatManager.chatGuildInfo(pc.getGuild(), "total withdraw = " + (overdraft + buildingWithdraw)); + // } + + if (!forge.transferGold(-buildingWithdraw,false)){ + overdraft += buildingWithdraw; + + if (!useWarehouse){ + ErrorPopupMsg.sendErrorMsg(pc, "Building does not have enough gold to produce this item."+ ib.getName()); + return null; + }else{ + if (overdraft > cityWarehouse.getResources().get(ItemBase.GOLD_ITEM_BASE)){ + ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse to produce this item."+ ib.getName()); + return null; + } + } + } + + if (overdraft > 0 && useWarehouse) + if(!cityWarehouse.withdraw(npc, ItemBase.GOLD_ITEM_BASE, overdraft, false,true)){ + //ChatManager.chatGuildError(pc, "Failed to create Item"); + Logger.error("Warehouse With UID of " + cityWarehouse.getUID() + " Failed to Create Item."+ ib.getName()); + return null; + } + + + if (prefix != null){ + + if (!useWarehouse){ + ErrorPopupMsg.sendErrorMsg(pc, "Cannot Resource Roll without access to the warehouse! Make sure the forge is currently protected."+ ib.getName()); + return null; + } + + + for (ItemBase ibResources: prefixResourceCosts.keySet()){ + + int creationAmount = prefixResourceCosts.get(ibResources); + + if (cityWarehouse.isResourceLocked(ibResources) == true) + return null; + + int oldAmount = cityWarehouse.getResources().get(ibResources); + int amount = creationAmount; + + if (oldAmount < amount) + amount = oldAmount; + + if(!cityWarehouse.withdraw(npc, ibResources, amount, false,true)){ + //ChatManager.chatGuildError(pc, "Failed to create Item"); + Logger.error("Warehouse With UID of " + cityWarehouse.getUID() + " Failed to Create Item."+ ib.getName()); + return null; + } + } + } + + if (suffix != null) { + + for (ItemBase ibResources: suffixResourceCosts.keySet()){ + int creationAmount = suffixResourceCosts.get(ibResources); + + if (cityWarehouse.isResourceLocked(ibResources) == true) { + ChatManager.chatSystemError(pc, ibResources.getName() + " is locked!"+ ib.getName()); + return null; + } + + int oldAmount = cityWarehouse.getResources().get(ibResources); + int amount = creationAmount; + if (oldAmount < amount) + amount = oldAmount; + if(!cityWarehouse.withdraw(npc, ibResources, amount, false,true)){ + //ChatManager.chatGuildError(pc, "Failed to create Item"); + Logger.error( "Warehouse With UID of " + cityWarehouse.getUID() + " Failed to Create Item."+ ib.getName()); + return null; + } + } + } + + if (prefix == null && suffix == null){ + + int baseCost = ib.getBaseValue(); + int total = (int) (baseCost + baseCost *(float).10); + + buildingWithdraw = BuildingManager.GetWithdrawAmountForRolling(forge, total); + + overdraft = BuildingManager.GetOverdraft(forge, total); + + if (overdraft > 0 && !useWarehouse){ + + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Not enough gold in building strongbox."+ ib.getName()); + return null; + } + + if (overdraft > 0 && cityWarehouse.isResourceLocked(ItemBase.GOLD_ITEM_BASE)){ + + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Warehouse gold is barred! Overdraft cannot be withdrawn from warehouse."+ ib.getName()); + return null; + } + + if (useWarehouse && overdraft > cityWarehouse.getResources().get(ItemBase.GOLD_ITEM_BASE)){ + + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse for overdraft."+ ib.getName()); + return null; + } } + + if (!forge.transferGold(-buildingWithdraw,false)){ + overdraft += buildingWithdraw; + + if (!useWarehouse){ + ErrorPopupMsg.sendErrorMsg(pc, "Building does not have enough gold to produce this item."+ ib.getName()); + return null; + }else{ + if (overdraft > cityWarehouse.getResources().get(ItemBase.GOLD_ITEM_BASE)){ + ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse to produce this item."+ ib.getName()); + return null; + } + } + } + + if (overdraft > 0) + if(!cityWarehouse.withdraw(npc, ItemBase.GOLD_ITEM_BASE, overdraft, false,true)){ + //ChatManager.chatGuildError(pc, "Failed to create Item"); + Logger.error( "Warehouse With UID of " + cityWarehouse.getUID() + " Failed to Create Item."+ ib.getName()); + return null; + } + + // ChatManager.chatGuildInfo(pc, "Gold Cost = " + total); + + if (galvorAmount > 0){ + if (!cityWarehouse.withdraw(npc, galvor, galvorAmount, false,true)){ + ErrorPopupMsg.sendErrorMsg(pc, "Failed to withdraw Galvor from warehouse!"+ ib.getName()); + Logger.error( "Warehouse with UID of" + cityWarehouse.getObjectUUID()+ "Failed to Withdrawl "); + return null; + } + } + + if (wormwoodAmount > 0){ + if (!cityWarehouse.withdraw(npc, wormwood, wormwoodAmount, false,true)){ + ErrorPopupMsg.sendErrorMsg(pc, "Failed to withdraw Wormwood from warehouse!"+ ib.getName()); + Logger.error("Warehouse with UID of" + cityWarehouse.getObjectUUID()+ "Failed to Withdrawl "); + return null; + } + } + + ml = new MobLoot(npc, ib, false); + + ml.containerType = Enum.ItemContainerType.FORGE; + ml.setName(customName); + + if (prefix != null){ + ml.addPermanentEnchantment(prefix.getIDString(), 0, 0, true); + ml.setPrefix(prefix.getIDString()); + prefixString = prefix.getIDString(); + } + + if (suffix != null){ + ml.addPermanentEnchantment(suffix.getIDString(), 0, 0, false); + ml.setSuffix(suffix.getIDString()); + suffixString = suffix.getIDString(); + } + + ml.loadEnchantments(); + //set value to 0 so magicvalue can be recalculated in getValue. + ml.setValue(0); + + + float time; + float rank = npc.getBuilding().getRank() - 1; + float rate = (float) (2.5 * rank); + time = (20 - rate); + time *= MBServerStatics.ONE_MINUTE; + + if (ml.getItemBase().getUUID() > 910010 && ml.getItemBase().getUUID() < 910019){ + rank = ml.getItemBaseID() - 910010; + time = rank * 60 * 60 * 3 * 1000; + } + + + // No job is submitted, as object's upgradetime field + // is used to determin whether or not an object has + // compelted rolling. The game object exists previously + // to this, not when 'compelte' is pressed. + long upgradeTime = System.currentTimeMillis() + (long)(time * MBServerStatics.PRODUCTION_TIME_MULTIPLIER) ; + + DateTime dateTime = new DateTime(); + dateTime = dateTime.withMillis(upgradeTime); + ml.setDateToUpgrade(upgradeTime); + + npc.addItemToForge(ml); + int playerID = 0; + + if (pc != null) + playerID = pc.getObjectUUID(); + + DbManager.NPCQueries.ADD_TO_PRODUCTION_LIST(ml.getObjectUUID(),npc.getObjectUUID(), ml.getItemBaseID(), dateTime, prefixString, suffixString, ml.getCustomName(), false,playerID); + ProducedItem pi = new ProducedItem(npc.getRolling().size(),npc.getObjectUUID(),ml.getItemBaseID(),dateTime,false,prefixString, suffixString, ml.getCustomName(),playerID); + pi.setProducedItemID(ml.getObjectUUID()); + pi.setAmount(itemsToRoll); + + ItemQueue produced = ItemQueue.borrow(pi, (long) (time * MBServerStatics.PRODUCTION_TIME_MULTIPLIER)); + ItemProductionManager.send(produced); + }catch(Exception e){ + Logger.error(e); + }finally{ + city.transactionLock.writeLock().unlock(); + } + + + + // npc.addItemToForge(item); + return ml; + + } + + + + public static Item randomRoll( NPC vendor, PlayerCharacter pc, int itemsToRoll, int itemID){ + byte itemModTable; + int prefixMod = 0; + int suffixMod = 0; + LootTable prefixLootTable; + LootTable suffixLootTable; + String suffix = ""; + String prefix = ""; + MobLoot toRoll; + + ItemBase ib = ItemBase.getItemBase(itemID); + + if (ib == null) + return null; + + if (!vendor.getCharItemManager().hasRoomInventory(ib.getWeight())){ + if (pc != null) + ChatManager.chatSystemInfo(pc, vendor.getName() + " " +vendor.getContract().getName() + " Inventory is full." ); + return null; + } + + float calculatedMobLevel; + calculatedMobLevel = vendor.getLevel(); + + if (calculatedMobLevel < 16) + calculatedMobLevel = 16; + + if (calculatedMobLevel > 49) + calculatedMobLevel = 49; + + itemModTable = (byte) ib.getModTable(); + + if (!vendor.getItemModTable().contains(itemModTable)){ + if (pc != null) + ErrorPopupMsg.sendErrorPopup(pc, 59); + return null; + } + + for (byte temp: vendor.getItemModTable()){ + if (itemModTable != temp) + continue; + prefixMod = vendor.getModTypeTable().get(vendor.getItemModTable().indexOf(temp)); + suffixMod = vendor.getModSuffixTable().get(vendor.getItemModTable().indexOf(temp)); + } + + if (prefixMod == 0 && suffixMod == 0){ + Logger.info( "Failed to find modTables for item " + ib.getName()); + return null; + } + + prefixLootTable = LootTable.getModGroup(prefixMod); + suffixLootTable = LootTable.getModGroup(suffixMod); + + if (prefixLootTable == null || suffixLootTable == null) + return null; + + int rollPrefix = ThreadLocalRandom.current().nextInt(100); + + if (rollPrefix < 80){ + int randomPrefix = ThreadLocalRandom.current().nextInt(100) + 1; + LootRow prefixLootRow = prefixLootTable.getLootRow(randomPrefix); + + if (prefixLootRow != null){ + LootTable prefixTypeTable = LootTable.getModTable(prefixLootRow.getValueOne()); + + int minRoll = (int) ((calculatedMobLevel - 5) * 5); + int maxRoll = (int) ((calculatedMobLevel + 15) * 5); + + if (minRoll < (int)prefixTypeTable.minRoll) + minRoll = (int)prefixTypeTable.minRoll; + + if (maxRoll < minRoll) + maxRoll = minRoll; + + if (maxRoll > prefixTypeTable.maxRoll) + maxRoll = (int) prefixTypeTable.maxRoll; + + if (maxRoll > 320) + maxRoll = 320; + + int randomPrefix1 = (int) ThreadLocalRandom.current().nextDouble(minRoll, maxRoll + 1); //Does not return Max, but does return min? + + if (randomPrefix1 < prefixTypeTable.minRoll) + randomPrefix1 = (int) prefixTypeTable.minRoll; + + if (randomPrefix1 > prefixTypeTable.maxRoll) + randomPrefix1 = (int) prefixTypeTable.maxRoll; + + LootRow prefixTypelootRow = prefixTypeTable.getLootRow(randomPrefix1); + + if (prefixTypelootRow == null) + prefixTypelootRow = prefixTypeTable.getLootRow((int) ((prefixTypeTable.maxRoll + prefixTypeTable.minRoll) * .05f)); + + if (prefixTypelootRow != null){ + prefix = prefixTypelootRow.getAction(); + } + } + } + + int rollSuffix = ThreadLocalRandom.current().nextInt(100); + + if (rollSuffix < 80){ + + int randomSuffix = ThreadLocalRandom.current().nextInt(100) + 1; + LootRow suffixLootRow = suffixLootTable.getLootRow(randomSuffix); + + if (suffixLootRow != null){ + + LootTable suffixTypeTable = LootTable.getModTable(suffixLootRow.getValueOne()); + + if (suffixTypeTable != null){ + int minRoll = (int) ((calculatedMobLevel - 5) * 5); + int maxRoll = (int) ((calculatedMobLevel + 15) * 5); + + if (minRoll < (int)suffixTypeTable.minRoll) + minRoll = (int)suffixTypeTable.minRoll; + + if (maxRoll < minRoll) + maxRoll = minRoll; + + if (maxRoll > suffixTypeTable.maxRoll) + maxRoll = (int) suffixTypeTable.maxRoll; + + if (maxRoll > 320) + maxRoll = 320; + + int randomSuffix1 = (int) ThreadLocalRandom.current().nextDouble(minRoll, maxRoll + 1); //Does not return Max, but does return min? + + if (randomSuffix1 < suffixTypeTable.minRoll) + randomSuffix1 = (int) suffixTypeTable.minRoll; + + if (randomSuffix1 > suffixTypeTable.maxRoll) + randomSuffix1 = (int) suffixTypeTable.maxRoll; + + LootRow suffixTypelootRow = suffixTypeTable.getLootRow(randomSuffix1); + + if (suffixTypelootRow != null){ + suffix = suffixTypelootRow.getAction(); + } + } + } + } + + if (prefix.isEmpty() && suffix.isEmpty()){ + + rollPrefix = ThreadLocalRandom.current().nextInt(100); + + if (rollPrefix < 50){ + + int randomPrefix = ThreadLocalRandom.current().nextInt(100) + 1; + LootRow prefixLootRow = prefixLootTable.getLootRow(randomPrefix); + + if (prefixLootRow != null){ + + LootTable prefixTypeTable = LootTable.getModTable(prefixLootRow.getValueOne()); + + int minRoll = (int) ((calculatedMobLevel) * 5); + int maxRoll = (int) ((calculatedMobLevel + 15) * 5); + + if (minRoll < (int)prefixTypeTable.minRoll) + minRoll = (int)prefixTypeTable.minRoll; + + if (maxRoll < minRoll) + maxRoll = minRoll; + + if (maxRoll > prefixTypeTable.maxRoll) + maxRoll = (int) prefixTypeTable.maxRoll; + + if (maxRoll > 320) + maxRoll = 320; + + int randomPrefix1 = (int) ThreadLocalRandom.current().nextDouble(minRoll, maxRoll + 1); //Does not return Max, but does return min? + + if (randomPrefix1 < prefixTypeTable.minRoll) + randomPrefix1 = (int) prefixTypeTable.minRoll; + + if (randomPrefix1 > prefixTypeTable.maxRoll) + randomPrefix1 = (int) prefixTypeTable.maxRoll; + + LootRow prefixTypelootRow = prefixTypeTable.getLootRow(randomPrefix1); + + if (prefixTypelootRow == null) + prefixTypelootRow = prefixTypeTable.getLootRow((int) ((prefixTypeTable.maxRoll + prefixTypeTable.minRoll) * .05f)); + + if (prefixTypelootRow != null){ + prefix = prefixTypelootRow.getAction(); + } + } + }else{ + int randomSuffix = ThreadLocalRandom.current().nextInt(100) + 1; + LootRow suffixLootRow = suffixLootTable.getLootRow(randomSuffix); + + if (suffixLootRow != null){ + + LootTable suffixTypeTable = LootTable.getModTable(suffixLootRow.getValueOne()); + + if (suffixTypeTable != null){ + + int minRoll = (int) ((calculatedMobLevel) * 5); + int maxRoll = (int) ((calculatedMobLevel + 15) * 5); + + if (minRoll < (int)suffixTypeTable.minRoll) + minRoll = (int)suffixTypeTable.minRoll; + + if (maxRoll < minRoll) + maxRoll = minRoll; + + if (maxRoll > suffixTypeTable.maxRoll) + maxRoll = (int) suffixTypeTable.maxRoll; + + if (maxRoll > 320) + maxRoll = 320; + + int randomSuffix1 = (int) ThreadLocalRandom.current().nextDouble(minRoll, maxRoll + 1); //Does not return Max, but does return min? + + if (randomSuffix1 < suffixTypeTable.minRoll) + randomSuffix1 = (int) suffixTypeTable.minRoll; + + if (randomSuffix1 > suffixTypeTable.maxRoll) + randomSuffix1 = (int) suffixTypeTable.maxRoll; + + LootRow suffixTypelootRow = suffixTypeTable.getLootRow(randomSuffix1); + + if (suffixTypelootRow != null) + suffix = suffixTypelootRow.getAction(); + } + } + } + } + + toRoll =ItemFactory.produceRandomRoll(vendor, pc,prefix,suffix, itemID); + + if (toRoll == null) + return null; + + toRoll.setValue(0); + + float time; + float rank = vendor.getBuilding().getRank() - 1; + float rate = (float) (2.5 * rank); + time = (20 - rate); + time *= MBServerStatics.ONE_MINUTE; + + if (toRoll.getItemBase().getUUID() > 910010 && toRoll.getItemBase().getUUID() < 910019){ + rank = toRoll.getItemBaseID() - 910010; + time = rank * 60 * 60 * 3 * 1000; + } + + // No job is submitted, as object's upgradetime field + // is used to determin whether or not an object has + // compelted rolling. The game object exists previously + // to this, not when 'compelte' is pressed. + long upgradeTime = System.currentTimeMillis() + (long)(time * MBServerStatics.PRODUCTION_TIME_MULTIPLIER) ; + + DateTime dateTime = new DateTime(); + dateTime = dateTime.withMillis(upgradeTime); + toRoll.setDateToUpgrade(upgradeTime); + + int playerID = 0; + + if (pc != null) + playerID = pc.getObjectUUID(); + DbManager.NPCQueries.ADD_TO_PRODUCTION_LIST(toRoll.getObjectUUID(),vendor.getObjectUUID(), toRoll.getItemBaseID(), dateTime, prefix, suffix, toRoll.getCustomName(), true,playerID); + ProducedItem pi = new ProducedItem(toRoll.getObjectUUID(),vendor.getObjectUUID(),toRoll.getItemBaseID(),dateTime,true,prefix, suffix, toRoll.getCustomName(),playerID); + pi.setProducedItemID(toRoll.getObjectUUID()); + pi.setAmount(itemsToRoll); + ItemQueue produced = ItemQueue.borrow(pi, (long) (time * MBServerStatics.PRODUCTION_TIME_MULTIPLIER)); + ItemProductionManager.send(produced); + return toRoll; + } + + public static MobLoot produceRandomRoll(NPC npc,PlayerCharacter pc,String prefixString, String suffixString, int itemID) { + + boolean useWarehouse = false; + + if(npc == null) + return null; + + ItemBase ib = ItemBase.getItemBase(itemID); + + if (ib == null) + return null; + + Building forge = npc.getBuilding(); + + if (forge == null) + return null; + + Zone zone = npc.getBuilding().getParentZone(); + + if (zone == null) + return null; + + City city = City.getCity(zone.getPlayerCityUUID()); + + if (city == null) + return null; + + MobLoot ml = null; + city.transactionLock.writeLock().lock(); + + try{ + + + + Warehouse cityWarehouse = city.getWarehouse(); + + if (cityWarehouse != null && forge.assetIsProtected()) + useWarehouse = true; + + ConcurrentHashMap resources = null; + + if (useWarehouse) + resources = cityWarehouse.getResources(); + + int galvorAmount = 0; + int wormwoodAmount = 0; + + if (ib.getType() == ItemType.WEAPON && ib.getPercentRequired() == 110){ + switch (ib.getSkillRequired()){ + case "Bow": + case "Crossbow": + case "Spear": + case "Pole Arm": + case "Staff": + wormwoodAmount = 22; + break; + case "Axe": + case "Dagger": + case "Sword": + case "Hammer": + case "Unarmed Combat": + + if (ib.isTwoHanded()) + galvorAmount = 22; + else + galvorAmount = 11; + break; + } + } + + ItemBase galvor = ItemBase.getItemBase(1580017); + ItemBase wormwood = ItemBase.getItemBase(1580018); + + //Cant roll 110% weapons that require resources if not allowed to use warehouse. + if (galvorAmount > 0 || wormwoodAmount > 0) + if (!useWarehouse) + return null; + + if (galvorAmount > 0){ + if (cityWarehouse.isResourceLocked(galvor)){ + ErrorPopupMsg.sendErrorMsg(pc, "Galvor is locked."+ ib.getName()); + return null; + } + + if (cityWarehouse.getResources().get(galvor) < galvorAmount){ + ErrorPopupMsg.sendErrorMsg(pc, "Not enough Galvor in warehouse to roll this item."+ ib.getName()); + return null; + } + } + + if (wormwoodAmount > 0){ + if (cityWarehouse.isResourceLocked(wormwood)){ + ErrorPopupMsg.sendErrorMsg(pc, "Galvor is locked."+ ib.getName()); + return null; + } + + if (cityWarehouse.getResources().get(wormwood) < wormwoodAmount){ + ErrorPopupMsg.sendErrorMsg(pc, "Not enough Galvor in warehouse to roll this item."+ ib.getName()); + return null; + } + } + + EffectsBase prefix = null; + + if (!prefixString.isEmpty()){ + prefix = PowersManager.getEffectByIDString(prefixString); + if (prefix == null) + return null; + } + + ItemBase goldIB = ItemBase.getGoldItemBase(); + + int baseCost = ib.getBaseValue(); + int total = (int) (baseCost + baseCost * .10); + + EffectsBase suffix = null; + + if (!suffixString.isEmpty()){ + suffix = PowersManager.getEffectByIDString(suffixString); + + if (suffix == null) + return null; + } + + //calculate gold costs and remove from the warehouse + if (prefix != null || suffix != null){ + int costToCreate = (int) (ib.getBaseValue() + ib.getBaseValue() *.10f); + int buildingWithdraw = BuildingManager.GetWithdrawAmountForRolling(forge, costToCreate); + int overdraft = BuildingManager.GetOverdraft(forge, costToCreate); + + if (overdraft > 0 && !useWarehouse){ + ErrorPopupMsg.sendErrorMsg(pc, "Not enough gold in building strongbox."+ ib.getName()); + return null; + } + + if (useWarehouse && overdraft > 0 && cityWarehouse.isResourceLocked(ItemBase.GOLD_ITEM_BASE)){ + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Warehouse gold is barred! Overdraft cannot be withdrawn from warehouse."+ ib.getName()); + return null; + } + + if (useWarehouse && overdraft > resources.get(goldIB)){ + ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse for overdraft."+ ib.getName()); + return null; + } + + if (!forge.transferGold(-buildingWithdraw,false)){ + overdraft += buildingWithdraw; + + if (!useWarehouse){ + ErrorPopupMsg.sendErrorMsg(pc, "Building does not have enough gold to produce this item."+ ib.getName()); + return null; + }else{ + if (overdraft > resources.get(goldIB)){ + ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse to produce this item."+ ib.getName()); + return null; + } + } + } + + // there was an overdraft, withdraw the rest from warehouse. + if (overdraft > 0){ + if (pc != null){ + if (!cityWarehouse.withdraw(pc, ItemBase.GOLD_ITEM_BASE, overdraft, false,true)){ + Logger.error("Warehouse with UID of" + cityWarehouse.getObjectUUID()+ "Failed to Withdrawl "); + return null; + } + }else{ + if (!cityWarehouse.withdraw(npc,ItemBase.GOLD_ITEM_BASE, overdraft, false,true)){ + Logger.error("Warehouse with UID of" + cityWarehouse.getObjectUUID()+ "Failed to Withdrawl "); + return null; + } + } + } + } + + if (prefix == null && suffix == null){ + + int buildingWithdraw = BuildingManager.GetWithdrawAmountForRolling(forge, total); + int overdraft = BuildingManager.GetOverdraft(forge, total); + + if (overdraft > 0 && !useWarehouse){ + ErrorPopupMsg.sendErrorMsg(pc, "Not enough gold in building strongbox."+ ib.getName()); + return null; + } + + if (useWarehouse && overdraft > 0 && cityWarehouse.isResourceLocked(ItemBase.GOLD_ITEM_BASE)){ + if (pc != null) + ErrorPopupMsg.sendErrorMsg(pc, "Warehouse gold is barred! Overdraft cannot be withdrawn from warehouse."+ ib.getName()); + return null; + } + + if (useWarehouse && overdraft > resources.get(goldIB)){ + ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse for overdraft."+ ib.getName()); + return null; + } + + if (!forge.transferGold(-buildingWithdraw,false)){ + overdraft += buildingWithdraw; + + if (!useWarehouse){ + ErrorPopupMsg.sendErrorMsg(pc, "Building does not have enough gold to produce this item."+ ib.getName()); + return null; + }else{ + if (overdraft > resources.get(goldIB)){ + ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse to produce this item."+ ib.getName()); + return null; + } + } + } + + if (overdraft > 0 && useWarehouse){ + + if (pc != null){ + if (!cityWarehouse.withdraw(pc, ItemBase.GOLD_ITEM_BASE, overdraft, false,true)){ + Logger.error("Warehouse with UID of" + cityWarehouse.getObjectUUID()+ "Failed to Withdrawl "); + return null; + } + }else{ + if (!cityWarehouse.withdraw(npc, ItemBase.GOLD_ITEM_BASE, overdraft, false,true)){ + Logger.error( "Warehouse with UID of" + cityWarehouse.getObjectUUID()+ "Failed to Withdrawl "); + return null; + } + } + } + } + + if (galvorAmount > 0 && useWarehouse){ + //ChatManager.chatGuildInfo(pc, "Withdrawing " + galvorAmount + " galvor from warehouse"); + if (!cityWarehouse.withdraw(npc, galvor, galvorAmount, false,true)){ + ErrorPopupMsg.sendErrorMsg(pc, "Failed to withdraw Galvor from warehouse!"+ ib.getName()); + Logger.error( "Warehouse with UID of" + cityWarehouse.getObjectUUID()+ "Failed to Withdrawl "); + return null; + } + } + + if (wormwoodAmount > 0 && useWarehouse){ + //ChatManager.chatGuildInfo(pc, "Withdrawing " + wormwoodAmount + " wormwood from warehouse"); + if (!cityWarehouse.withdraw(npc, wormwood, wormwoodAmount, false,true)){ + ErrorPopupMsg.sendErrorMsg(pc, "Failed to withdraw Wormwood from warehouse for " + ib.getName()); + Logger.error("Warehouse with UID of" + cityWarehouse.getObjectUUID()+ "Failed to Withdrawl "); + + return null; + } + } + + ml = new MobLoot(npc, ib, false); + + ml.containerType = Enum.ItemContainerType.FORGE; + + if (prefix != null){ + ml.addPermanentEnchantment(prefix.getIDString(), 0, 0, true); + ml.setPrefix(prefix.getIDString()); + } + + if (suffix != null){ + ml.addPermanentEnchantment(suffix.getIDString(), 0, 0, false); + ml.setSuffix(suffix.getIDString()); + } + + ml.loadEnchantments(); + + + ml.setValue(0); + ml.setRandom(true); + npc.addItemToForge(ml); + }catch(Exception e){ + Logger.error(e); + }finally{ + city.transactionLock.writeLock().unlock(); + } + return ml; + + } +} diff --git a/src/engine/objects/Kit.java b/src/engine/objects/Kit.java new file mode 100644 index 00000000..d2642b80 --- /dev/null +++ b/src/engine/objects/Kit.java @@ -0,0 +1,429 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum.ItemContainerType; +import engine.Enum.OwnerType; +import engine.gameManager.DbManager; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.net.UnknownHostException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; + + +public class Kit extends AbstractGameObject { + + private final int raceBaseClassID; + private final byte kitNumber; + private final int legs; + private final int chest; + private final int feet; + private final int offhand; + private final int weapon; + public static HashMap NoobGearIDS = new HashMap<>(); + public static HashMap> RaceClassIDMap = new HashMap<>(); + + + /** + * No Table ID Constructor + */ + public Kit(int raceBaseClassID, byte kitNumber, int legs, int chest, + int feet, int offhand, int weapon) { + super(); + this.raceBaseClassID = raceBaseClassID; + this.kitNumber = kitNumber; + this.legs = legs; + this.chest = chest; + this.feet = feet; + this.offhand = offhand; + this.weapon = weapon; + } + + /** + * Normal Constructor + */ + public Kit(int raceBaseClassID, byte kitNumber, int legs, int chest, + int feet, int offhand, int weapon, int newUUID) { + super(newUUID); + this.raceBaseClassID = raceBaseClassID; + this.kitNumber = kitNumber; + this.legs = legs; + this.chest = chest; + this.feet = feet; + this.offhand = offhand; + this.weapon = weapon; + } + + /** + * RecordSet Constructor + */ + public Kit(ResultSet rs) throws SQLException, UnknownHostException { + super(rs); + + this.raceBaseClassID = rs.getInt("RaceBaseClassesID"); + this.kitNumber = rs.getByte("kitNumber"); + + this.legs = rs.getInt("legs"); + this.chest = rs.getInt("chest"); + this.feet = rs.getInt("feet"); + this.offhand = rs.getInt("offhand"); + this.weapon = rs.getInt("weapon"); + if (Kit.RaceClassIDMap.containsKey(this.raceBaseClassID)){ + Kit.RaceClassIDMap.get(this.raceBaseClassID).add(this); + }else{ + ArrayList tempList = new ArrayList<>(); + tempList.add(this); + Kit.RaceClassIDMap.put(this.raceBaseClassID, tempList); + + } + + if (this.legs != 0) + Kit.NoobGearIDS.put(this.legs, true); + if (this.chest != 0) + Kit.NoobGearIDS.put(this.chest, true); + if (this.feet != 0) + Kit.NoobGearIDS.put(this.feet, true); + if (this.offhand != 0) + Kit.NoobGearIDS.put(this.offhand, true); + if (this.weapon != 0) + Kit.NoobGearIDS.put(this.weapon, true); + + } + + public static boolean IsNoobGear(int itemID){ + + return Kit.NoobGearIDS.containsKey(itemID); + + } + + /* + * Getters + */ + + public int getRaceBaseClassID() { + return raceBaseClassID; + } + + public byte getKitNumber() { + return kitNumber; + } + + public int getLegs() { + return legs; + } + + public int getChest() { + return chest; + } + + public int getFeet() { + return feet; + } + + public int getOffhand() { + return offhand; + } + + public int getWeapon() { + return weapon; + } + + public void equipPCwithKit(PlayerCharacter pc) { + if (weapon != 0) + kitItemCreator(pc, weapon, MBServerStatics.SLOT_MAINHAND); + if (offhand != 0) + kitItemCreator(pc, offhand, MBServerStatics.SLOT_OFFHAND); + if (chest != 0) + kitItemCreator(pc, chest, MBServerStatics.SLOT_CHEST); + if (legs != 0) + kitItemCreator(pc, legs, MBServerStatics.SLOT_LEGGINGS); + if (feet != 0) + kitItemCreator(pc, feet, MBServerStatics.SLOT_FEET); + } + + private static boolean kitItemCreator(PlayerCharacter pc, int itemBase, int slot) + { + ItemBase i = ItemBase.getItemBase(itemBase); + + Item temp = new Item( i, pc.getObjectUUID(), + OwnerType.PlayerCharacter, (byte) 0, (byte) 0, (short) 0, (short) 0, + false, false,ItemContainerType.EQUIPPED, (byte) slot, + new ArrayList<>(),""); + + try { + temp = DbManager.ItemQueries.ADD_ITEM(temp); + } catch (Exception e) { + Logger.error(e); + } + + if (temp == null) { + Logger.info("Ungoof this goof, something is wrong with our kit."); + } + return true; + } + + public static int GetKitIDByRaceClass(final int raceID, final int classID){ + switch (raceID){ + case 2000: + switch(classID){ + case 2500: + return 2; + case 2501: + return 3; + case 2502: + return 4; + case 2503: + return 5; + } + case 2001: + + switch(classID){ + case 2500: + return 6; + case 2501: + return 7; + case 2502: + return 8; + case 2503: + return 9; + } + case 2002: + switch(classID){ + case 2500: + return 10; + case 2501: + return 11; + case 2502: + return 12; + + } + case 2003: + switch(classID){ + case 2500: + return 13; + case 2501: + return 14; + case 2502: + return 15; + } + case 2004: + + switch(classID){ + case 2500: + return 16; + case 2501: + return 17; + } + case 2005: + switch(classID){ + case 2500: + return 18; + case 2501: + return 19; + } + case 2006: + + switch(classID){ + case 2500: + return 20; + case 2501: + return 21; + + } + case 2008: + + switch(classID){ + case 2500: + return 22; + case 2501: + return 23; + case 2502: + return 24; + case 2503: + return 25; + } + + case 2009: + + switch(classID){ + case 2500: + return 26; + case 2501: + return 27; + case 2502: + return 28; + case 2503: + return 29; + } + case 2010: + + switch(classID){ + case 2500: + return 30; + } + + case 2011: + + switch(classID){ + case 2500: + return 31; + case 2501: + return 32; + case 2502: + return 33; + case 2503: + return 34; + } + + case 2012: + + switch(classID){ + case 2500: + return 35; + case 2501: + return 36; + case 2502: + return 37; + case 2503: + return 38; + } + + case 2013: + + switch(classID){ + case 2500: + return 39; + case 2501: + return 40; + case 2502: + return 41; + case 2503: + return 42; + } + + case 2014: + + switch(classID){ + case 2500: + return 43; + case 2501: + return 44; + case 2502: + return 45; + case 2503: + return 46; + } + + case 2015: + + switch(classID){ + case 2500: + return 47; + case 2502: + return 48; + case 2503: + return 49; + + } + + case 2016: + + switch(classID){ + case 2500: + return 50; + case 2502: + return 51; + case 2503: + return 52; + + } + + case 2017: + + switch(classID){ + case 2500: + return 53; + case 2501: + return 54; + + } + + case 2025: + + switch(classID){ + case 2500: + return 55; + case 2501: + return 56; + case 2502: + return 57; + case 2503: + return 58; + } + + case 2026: + + switch(classID){ + case 2500: + return 59; + case 2501: + return 60; + case 2502: + return 61; + case 2503: + return 62; + } + + case 2027: + + switch(classID){ + case 2500: + return 63; + } + + case 2028: + + switch(classID){ + case 2500: + return 64; + + case 2502: + return 65; + case 2503: + return 66; + } + + case 2029: + + switch(classID){ + case 2500: + return 67; + + case 2502: + return 68; + case 2503: + return 69; + } + + + + } + return -1; + } + + @Override + public void updateDatabase() { + // TODO Create update logic. + } +} diff --git a/src/engine/objects/LevelDefault.java b/src/engine/objects/LevelDefault.java new file mode 100644 index 00000000..c66d6db3 --- /dev/null +++ b/src/engine/objects/LevelDefault.java @@ -0,0 +1,72 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + package engine.objects; + +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.concurrent.ConcurrentHashMap; + +public class LevelDefault { + + public final int level; + public final float health; + public final float mana; + public final float stamina; + public final float atr; + public final float def; + public final float minDamage; + public final float maxDamage; + public final int goldMin; + public final int goldMax; + + public static ConcurrentHashMap defaults = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + + /** + * ResultSet Constructor + */ + public LevelDefault(ResultSet rs) throws SQLException { + super(); + this.level = rs.getInt("level"); + this.health = rs.getFloat("health"); + this.mana = (float)rs.getInt("mana"); + this.stamina = (float)rs.getInt("stamina"); + this.atr = (float)rs.getInt("atr"); + this.def = (float)rs.getInt("def"); + this.minDamage = (float)rs.getInt("minDamage"); + this.maxDamage = (float)rs.getInt("maxDamage"); + this.goldMin = rs.getInt("goldMin"); + this.goldMax = rs.getInt("goldMax"); + } + + public static LevelDefault getLevelDefault(byte level) { + LevelDefault ret = null; + if (LevelDefault.defaults.containsKey(level)) + return LevelDefault.defaults.get(level); + + PreparedStatementShared ps = null; + try { + ps = new PreparedStatementShared("SELECT * FROM `static_npc_level_defaults` WHERE level = ?;"); + ps.setInt(1, (int)level); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + ret = new LevelDefault(rs); + LevelDefault.defaults.put(level, ret); + } + } catch (SQLException e) { + Logger.error("SQL Error number: " + e.getErrorCode() + ' ' + e.getMessage()); + } finally { + ps.release(); + } + return ret; + } +} \ No newline at end of file diff --git a/src/engine/objects/LootRow.java b/src/engine/objects/LootRow.java new file mode 100644 index 00000000..3a74c269 --- /dev/null +++ b/src/engine/objects/LootRow.java @@ -0,0 +1,63 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +public class LootRow { + + private int valueOne; + private int valueTwo; + private int valueThree; + private String action; + + + /** + * Generic Constructor + */ + public LootRow(int valueOne, int valueTwo, int valueThree, String action) { + this.valueOne = valueOne; + this.valueTwo = valueTwo; + this.valueThree = valueThree; + this.action = action; + + } + + public int getValueOne() { + return this.valueOne; + } + + public int getValueTwo() { + return this.valueTwo; + } + + public int getValueThree() { + return this.valueThree; + } + + public String getAction() { + return this.action; + } + + public void setValueOne(int value) { + this.valueOne = value; + } + + public void setValueTwo(int value) { + this.valueTwo = value; + } + + public void setValueThree(int value) { + this.valueThree = value; + } + + public void setAction(String value) { + this.action = value; + } + +} diff --git a/src/engine/objects/LootTable.java b/src/engine/objects/LootTable.java new file mode 100644 index 00000000..16a9523a --- /dev/null +++ b/src/engine/objects/LootTable.java @@ -0,0 +1,1381 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum.ItemContainerType; +import engine.Enum.ItemType; +import engine.Enum.OwnerType; +import engine.gameManager.DbManager; +import engine.gameManager.ZoneManager; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; + +public class LootTable { + + private static final ConcurrentHashMap lootGroups = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private static final ConcurrentHashMap lootTables = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private static final ConcurrentHashMap modTables = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private static final ConcurrentHashMap modGroups = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private static final ConcurrentHashMap statRuneChances = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private final ConcurrentHashMap lootTable = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + + + + private static final int oneDrop = 95; + private static final int twoDrop = 100; + private static final int noDropHotZone = 79; + private static final int oneDropHotZone = 98; + + public float minRoll = 320; + public float maxRoll = 1; + public static boolean initialized = false; + + public int lootTableID = 0; + + public static HashMap itemsDroppedMap = new HashMap<>(); + public static HashMap resourceDroppedMap = new HashMap<>(); + public static HashMap runeDroppedMap = new HashMap<>(); + public static HashMap contractDroppedMap = new HashMap<>(); + public static HashMap glassDroppedMap = new HashMap<>(); + + public static int rollCount = 0; + public static int dropCount = 0; + public static int runeCount = 0; + public static int contractCount = 0; + public static int resourceCount = 0; + public static int glassCount = 0; + + + /** + * Generic Constructor + */ + public LootTable(int lootTableID) { + this.lootTableID = lootTableID; + } + + public void addRow(float min, float max, int valueOne, int valueTwo, int valueThree, String action) { + + //hackey way to set the minimum roll for SHIAT! + if (min < this.minRoll) + this.minRoll = min; + + if (max > this.maxRoll) + this.maxRoll = max; + + int minInt = (int) min; + int maxInt = (int) max; + + //Round up min + if (minInt != min){ + min = minInt + 1; + } + + //Round down max; + if (maxInt != max) + max = maxInt; + + + + LootRow lootRow = new LootRow(valueOne, valueTwo, valueThree, action); + for (int i = (int) min; i <= max; i++) { + lootTable.put(i, lootRow); + } + } + + public static LootTable getLootGroup(int UUID) { + + if (lootGroups.containsKey(UUID)) + return lootGroups.get(UUID); + + LootTable lootGroup = new LootTable(UUID); + lootGroups.put(UUID, lootGroup); + return lootGroup; + } + + public static LootTable getLootTable(int UUID) { + + if (lootTables.containsKey(UUID)) + return lootTables.get(UUID); + + LootTable lootTable = new LootTable(UUID); + lootTables.put(UUID, lootTable); + return lootTable; + } + + /** + * @return the lootGroups + */ + public static ConcurrentHashMap getLootGroups() { + return lootGroups; + } + + /** + * @return the lootTables + */ + public static ConcurrentHashMap getLootTables() { + return lootTables; + } + + /** + * @return the modTables + */ + public static ConcurrentHashMap getModTables() { + return modTables; + } + + /** + * @return the modGroups + */ + public static ConcurrentHashMap getModGroups() { + return modGroups; + } + + + public static LootTable getModGroup(int UUID) { + if (modGroups.containsKey(UUID)) + return modGroups.get(UUID); + LootTable modTable = new LootTable(UUID); + modGroups.put(UUID, modTable); + return modTable; + } + + public static LootTable getModTable(int UUID) { + if (modTables.containsKey(UUID)) + return modTables.get(UUID); + LootTable modTypeTable = new LootTable(UUID); + modTables.put(UUID, modTypeTable); + return modTypeTable; + } + + + public LootRow getLootRow(int probability) { + if (lootTable.containsKey(probability)) + return lootTable.get(probability); + return null; + } + + //call this on server startup to populate the tables + public static void populateLootTables() { + DbManager.LootQueries.populateLootGroups(); + DbManager.LootQueries.populateLootTables(); + DbManager.LootQueries.populateModTables(); + DbManager.LootQueries.populateModGroups(); + + //preset chances for rune drops + populateStatRuneChances(); + } + + //Returns a list of random loot for a mob based on level, lootTable and hotzone + public static ArrayList getMobLoot(Mob mob, int mobLevel, int lootTable, boolean hotzone) { + + // Member variable declaration + ArrayList loot; + int calculatedLootTable; + int roll; + + // Member variable assignment + loot = new ArrayList<>(); + + // Setup default loot table if none exists + calculatedLootTable = lootTable; + + LootTable.rollCount++; + if (MobLootBase.MobLootSet.get(mob.getMobBase().getLoadID()).isEmpty()){ + + + roll = ThreadLocalRandom.current().nextInt(100); + if (roll > 90) + if (roll > LootTable.oneDropHotZone) + addMobLoot(mob, loot, mobLevel, calculatedLootTable, 1, true); + else + addMobLoot(mob, loot, mobLevel, calculatedLootTable, 1, true); + }else{ + for (MobLootBase mlb:MobLootBase.MobLootSet.get(mob.getMobBase().getLoadID())){ + + + float chance = mlb.getChance() *.01f; + + chance *= MBServerStatics.DROP_RATE_MOD; + + calculatedLootTable = mlb.getLootTableID(); + + + if (ThreadLocalRandom.current().nextFloat() > chance) + continue; + + addMobLoot(mob, loot, mobLevel, calculatedLootTable, 1, false); + + + } + } + + //calculatedLootTable = lootTable; + + if (calculatedLootTable <= 1) + calculatedLootTable = 1300; // GENERIC WORLD + + + + + //handle hotzone random loot + + if (hotzone) { + + LootTable.rollCount++; + + if (MobLootBase.MobLootSet.get(mob.getMobBase().getLoadID()).isEmpty()){ + + + roll = ThreadLocalRandom.current().nextInt(100); + if (roll > 90) + if (roll > LootTable.oneDropHotZone) + addMobLoot(mob, loot, mobLevel, calculatedLootTable + 1, 1, true); + else + addMobLoot(mob, loot, mobLevel, calculatedLootTable + 1, 1, true); + }else{ + for (MobLootBase mlb:MobLootBase.MobLootSet.get(mob.getMobBase().getLoadID())){ + if (!LootTable.lootGroups.containsKey(mlb.getLootTableID() + 1)) + continue; + calculatedLootTable = mlb.getLootTableID(); + break; + } + roll = ThreadLocalRandom.current().nextInt(100); + if (roll > 90) + if (roll > LootTable.oneDropHotZone) + addMobLoot(mob, loot, mobLevel, (calculatedLootTable + 1), 1, true); + else + addMobLoot(mob, loot, mobLevel, (calculatedLootTable + 1), 1, true); + + } + + } + + + + + //handle mob specific special loot + handleSpecialLoot(loot, mob, false); + + return loot; + } + + public static ArrayList getMobLootDeath(Mob mob, int mobLevel, int lootTable) { + ArrayList loot = new ArrayList<>(); + + if (mob == null) + return loot; + + //handle hotzone random loot + boolean hotzone = ZoneManager.inHotZone(mob.getLoc()); + if (hotzone) { + + if (MobLootBase.MobLootSet.get(mob.getMobBase().getLoadID()).isEmpty()){ + lootTable += 1; + + if (lootTable <= 1) + lootTable = 1301; // GENERIC WORLD + int roll = ThreadLocalRandom.current().nextInt(100); + if (roll > 90) + if (roll > LootTable.oneDropHotZone) + addMobLoot(mob, loot, mobLevel, lootTable, 1, true); + else + addMobLoot(mob, loot, mobLevel, lootTable, 1, true); + }else{ + for (MobLootBase mlb:MobLootBase.MobLootSet.get(mob.getMobBase().getLoadID())){ + lootTable = mlb.getLootTableID() + 1; + if (!LootTable.lootGroups.containsKey(lootTable)) + continue; + + int roll = ThreadLocalRandom.current().nextInt(100); + if (roll > 90) + if (roll > LootTable.oneDropHotZone) + addMobLoot(mob, loot, mobLevel, (lootTable), 1, true); + else + addMobLoot(mob, loot, mobLevel, (lootTable), 1, true); + + break; + } + } + + + if (loot.isEmpty()){ + + LootTable.rollCount++; //add another rollCount here. + int resourceRoll = ThreadLocalRandom.current().nextInt(100); + if (resourceRoll <=5) + addMobLootResources(mob, loot, mobLevel, (lootTable), 1, true); + } + + } + + + //handle mob specific special loot on death + handleSpecialLoot(loot, mob, true); + + return loot; + } + + private static void handleSpecialLoot(ArrayList loot, Mob mob, boolean onDeath) { + + if (SpecialLoot.LootMap.containsKey(mob.getLootSet())) { + ArrayList specialLoot = SpecialLoot.LootMap.get(mob.getLootSet()); + for (SpecialLoot sl : specialLoot) { + if ((onDeath && sl.dropOnDeath()) || (!onDeath && !sl.dropOnDeath())) + if (ThreadLocalRandom.current().nextInt(100) < sl.getDropChance()) { + ItemBase ib = ItemBase.getItemBase(sl.getItemID()); + if (ib != null) { + + switch (ib.getUUID()){ + case 19290: + continue; + case 19291: + continue; + case 19292: + continue; + case 27530: + continue; + case 973000: + continue; + case 973200: + continue; + case 26360: + continue; + } + MobLoot ml = new MobLoot(mob, ib, sl.noSteal()); + loot.add(ml); + + + + } + } + } + } + } + + + + //called by getMobLoot to add the actual loot + private static void addMobLoot(Mob mob, ArrayList loot, int mobLevel, int lootTableID, int cnt, boolean hotzone) { + + // Member variable declaration + float calculatedMobLevel; + int minSpawn; + int maxSpawn; + int spawnQuanity = 0; + int prefixValue = 0; + int suffixValue = 0; + int subTableID; + String modifierPrefix = ""; + String modifierSuffix = ""; + + // Lookup Table Variables + LootTable lootTable; + LootRow lootRow; + LootTable lootGroup; + LootRow groupRow = null; + LootTable modTable; + LootTable modGroup; + LootRow modRow = null; + + // Used for actual generation of items + int itemBaseUUID; + ItemBase itemBase = null; + MobLoot mobLoot; + + Zone zone = mob.getParentZone(); + // Member variable assignment + if (!LootTable.lootGroups.containsKey(lootTableID)) + return; + + lootGroup = LootTable.lootGroups.get(lootTableID); + + + + + + calculatedMobLevel = mobLevel; + + if (calculatedMobLevel > 49) + calculatedMobLevel = 49; + + + int roll = 0; + for (int i = 0; i < cnt; i++) { + + + Random random = new Random(); + + + roll = random.nextInt(100) + 1; //random roll between 1 and 100 + groupRow = lootGroup.getLootRow(roll); + + + + + + if (groupRow == null) + return; + + //get loot table for this group + if (!LootTable.lootTables.containsKey(groupRow.getValueOne())) + return; + + + lootTable = LootTable.lootTables.get(groupRow.getValueOne()); + + //get item ID //FUCK THIS RETARDED SHIT + // roll = gaussianLevel(calculatedMobLevel); + + + + + int minRoll = (int) ((calculatedMobLevel - 5) * 5); + int maxRoll = (int) ((calculatedMobLevel + 15) * 5); + + if (minRoll < (int)lootTable.minRoll){ + minRoll = (int)lootTable.minRoll; + } + + if (maxRoll < minRoll) + maxRoll = minRoll; + + if (maxRoll > lootTable.maxRoll) + maxRoll = (int) lootTable.maxRoll; + + + + if (maxRoll > 320) + maxRoll = 320; + + roll = (int) ThreadLocalRandom.current().nextDouble(minRoll, maxRoll + 1); //Does not return Max, but does return min? + + + lootRow = lootTable.getLootRow(roll); //get the item row from the bell's curve of level +-15 + + if (lootRow == null) + continue; //no item found for roll + + itemBaseUUID = lootRow.getValueOne(); + + + + if (lootRow.getValueOne() == 0) + continue; + + //handle quantities > 1 for resource drops + minSpawn = lootRow.getValueTwo(); + maxSpawn = lootRow.getValueThree(); + + // spawnQuanity between minspawn (inclusive) and maxspawn (inclusive) + if (maxSpawn > 1) + spawnQuanity = ThreadLocalRandom.current().nextInt((maxSpawn + 1 - minSpawn)) + minSpawn; + + + + //get modifierPrefix + + calculatedMobLevel = mobLevel; + + if (calculatedMobLevel < 16) + calculatedMobLevel = 16; + + if (calculatedMobLevel > 49) + calculatedMobLevel = 49; + + int chanceMod = ThreadLocalRandom.current().nextInt(100) + 1; + + if (chanceMod < 25){ + modGroup = LootTable.modGroups.get(groupRow.getValueTwo()); + + if (modGroup != null) { + + + for (int a = 0;a<10;a++){ + roll = ThreadLocalRandom.current().nextInt(100) + 1; + modRow = modGroup.getLootRow(roll); + if (modRow != null) + break; + } + + + if (modRow != null) { + subTableID = modRow.getValueOne(); + + if (LootTable.modTables.containsKey(subTableID)) { + + modTable = LootTable.modTables.get(subTableID); + + roll = gaussianLevel((int)calculatedMobLevel); + + if (roll < modTable.minRoll) + roll = (int) modTable.minRoll; + + if (roll > modTable.maxRoll) + roll = (int) modTable.maxRoll; + + + + modRow = modTable.getLootRow(roll); + + if (modRow != null) { + prefixValue = modRow.getValueOne(); + modifierPrefix = modRow.getAction(); + } + } + } + } + }else if(chanceMod < 50){ + modGroup = LootTable.modGroups.get(groupRow.getValueThree()); + + if (modGroup != null) { + + for (int a = 0;a<10;a++){ + roll = ThreadLocalRandom.current().nextInt(100) + 1; + modRow = modGroup.getLootRow(roll); + if (modRow != null) + break; + } + + if (modRow != null) { + + subTableID = modRow.getValueOne(); + + if (LootTable.modTables.containsKey(subTableID)) { + + modTable = LootTable.modTables.get(subTableID); + roll = gaussianLevel((int)calculatedMobLevel); + + if (roll < modTable.minRoll) + roll = (int) modTable.minRoll; + + if (roll > modTable.maxRoll) + roll = (int) modTable.maxRoll; + + modRow = modTable.getLootRow(roll); + + if (modRow == null){ + modRow = modTable.getLootRow((int) ((modTable.minRoll + modTable.maxRoll) *.05f)); + } + + if (modRow != null) { + suffixValue = modRow.getValueOne(); + modifierSuffix = modRow.getAction(); + } + } + } + } + }else{ + modGroup = LootTable.modGroups.get(groupRow.getValueTwo()); + + if (modGroup != null) { + + + for (int a = 0;a<10;a++){ + roll = ThreadLocalRandom.current().nextInt(100) + 1; + modRow = modGroup.getLootRow(roll); + if (modRow != null) + break; + } + + + if (modRow != null) { + subTableID = modRow.getValueOne(); + + if (LootTable.modTables.containsKey(subTableID)) { + + modTable = LootTable.modTables.get(subTableID); + + roll = gaussianLevel((int)calculatedMobLevel); + + if (roll < modTable.minRoll) + roll = (int) modTable.minRoll; + + if (roll > modTable.maxRoll) + roll = (int) modTable.maxRoll; + + + + modRow = modTable.getLootRow(roll); + + if (modRow == null){ + modRow = modTable.getLootRow((int) ((modTable.minRoll + modTable.maxRoll) *.05f)); + } + + if (modRow != null) { + prefixValue = modRow.getValueOne(); + modifierPrefix = modRow.getAction(); + } + } + } + } + + //get modifierSuffix + modGroup = LootTable.modGroups.get(groupRow.getValueThree()); + + if (modGroup != null) { + + for (int a = 0;a<10;a++){ + roll = ThreadLocalRandom.current().nextInt(100) + 1; + modRow = modGroup.getLootRow(roll); + if (modRow != null) + break; + } + + if (modRow != null) { + + subTableID = modRow.getValueOne(); + + if (LootTable.modTables.containsKey(subTableID)) { + + modTable = LootTable.modTables.get(subTableID); + roll = gaussianLevel((int)calculatedMobLevel); + + if (roll < modTable.minRoll) + roll = (int) modTable.minRoll; + + if (roll > modTable.maxRoll) + roll = (int) modTable.maxRoll; + + modRow = modTable.getLootRow(roll); + + if (modRow == null){ + modRow = modTable.getLootRow((int) ((modTable.minRoll + modTable.maxRoll) *.05f)); + } + + if (modRow != null) { + suffixValue = modRow.getValueOne(); + modifierSuffix = modRow.getAction(); + } + } + } + } + } + + + itemBase = ItemBase.getItemBase(itemBaseUUID); + + if (itemBase == null) + return; + + //Handle logging of drops + LootTable.HandleDropLogs(itemBase); + + + + + // Handle drop rates of resources/runes/contracts. + // We intentionally drop them in half + // if ((itemBase.getMessageType() == ItemType.CONTRACT) || + // (itemBase.getMessageType() == ItemType.RUNE) ){ + // if (ThreadLocalRandom.current().nextBoolean() == false) + // continue; + // } + + + + if (itemBase.getType() == ItemType.OFFERING) + spawnQuanity = 1; + + if (spawnQuanity > 0) + mobLoot = new MobLoot(mob, itemBase, spawnQuanity, false); + else + mobLoot = new MobLoot(mob, itemBase, false); + + if (!modifierPrefix.isEmpty()) + mobLoot.addPermanentEnchantment(modifierPrefix, 0, prefixValue, true); + + if (!modifierSuffix.isEmpty()) + mobLoot.addPermanentEnchantment(modifierSuffix, 0, suffixValue, false); + mobLoot.loadEnchantments(); + + loot.add(mobLoot); + + + + } + } + + private static void addMobLootResources(Mob mob, ArrayList loot, int mobLevel, int lootTableID, int cnt, boolean hotzone) { + + // Member variable declaration + float calculatedMobLevel; + int minSpawn; + int maxSpawn; + int spawnQuanity = 0; + int prefixValue = 0; + int suffixValue = 0; + int subTableID; + String modifierPrefix = ""; + String modifierSuffix = ""; + + // Lookup Table Variables + LootTable lootTable; + LootRow lootRow; + LootTable lootGroup; + LootRow groupRow = null; + LootTable modTable; + LootTable modGroup; + LootRow modRow = null; + + // Used for actual generation of items + int itemBaseUUID; + ItemBase itemBase; + MobLoot mobLoot; + + Zone zone = mob.getParentZone(); + // Member variable assignment + if (!LootTable.lootGroups.containsKey(lootTableID)) + return; + + lootGroup = LootTable.lootGroups.get(lootTableID); + + calculatedMobLevel = mobLevel; + + if (calculatedMobLevel > 49) + calculatedMobLevel = 49; + + int roll = 0; + for (int i = 0; i < cnt; i++) { + + + + if (lootTableID == 1901) + groupRow = lootGroup.getLootRow(66); + else if (lootTableID == 1501) + groupRow = lootGroup.getLootRow(98); + else + groupRow = lootGroup.getLootRow(80); + + + + + + if (groupRow == null) + return; + + //get loot table for this group + if (!LootTable.lootTables.containsKey(groupRow.getValueOne())) + return; + + + lootTable = LootTable.lootTables.get(groupRow.getValueOne()); + + //get item ID //FUCK THIS RETARDED SHIT + // roll = gaussianLevel(calculatedMobLevel); + + + + + int minRoll = (int) ((calculatedMobLevel-5) * 5); + int maxRoll = (int) ((calculatedMobLevel + 15) *5); + + if (minRoll < (int)lootTable.minRoll){ + minRoll = (int)lootTable.minRoll; + } + + if (maxRoll < minRoll) + maxRoll = minRoll; + + + + if (maxRoll > 320) + maxRoll = 320; + + roll = ThreadLocalRandom.current().nextInt(minRoll, maxRoll + 1); //Does not return Max, but does return min? + lootRow = lootTable.getLootRow(roll); //get the item row from the bell's curve of level +-15 + + if (lootRow == null) + continue; //no item found for roll + + itemBaseUUID = lootRow.getValueOne(); + + if (lootRow.getValueOne() == 0) + continue; + + //handle quantities > 1 for resource drops + minSpawn = lootRow.getValueTwo(); + maxSpawn = lootRow.getValueThree(); + + // spawnQuanity between minspawn (inclusive) and maxspawn (inclusive) + if (maxSpawn > 1) + spawnQuanity = ThreadLocalRandom.current().nextInt((maxSpawn + 1 - minSpawn)) + minSpawn; + + + itemBase = ItemBase.getItemBase(itemBaseUUID); + if (itemBase == null) + return; + LootTable.HandleDropLogs(itemBase); + + + switch (itemBase.getUUID()){ + case 19290: + continue; + case 19291: + continue; + case 19292: + continue; + case 27530: + continue; + case 973000: + continue; + case 973200: + continue; + + case 26360: + continue; + } + + // Handle drop rates of resources/runes/contracts. + // We intentionally drop them in half + + + + if (itemBase.getType() == ItemType.OFFERING) + spawnQuanity = 1; + + if (spawnQuanity > 0) + mobLoot = new MobLoot(mob, itemBase, spawnQuanity, false); + else + mobLoot = new MobLoot(mob, itemBase, false); + + loot.add(mobLoot); + + } + } + + public static int gaussianLevel(int level) { + int ret = -76; + + while (ret < -75 || ret > 75) { + ret = (int) (ThreadLocalRandom.current().nextGaussian() * 75); + } + + return (level * 5) + ret; + // float useLevel = (float)(level + (ThreadLocalRandom.current().nextGaussian() * 5)); + // + // if (useLevel < (level - 15)) + // useLevel = level - 15; + // else if (useLevel > (level + 15)) + // useLevel = level + 15; + // return (int)(useLevel * 5); + } + + + + + + //This set's the drop chances for stat runes. + public static void populateStatRuneChances() { + //+3, Increased + statRuneChances.put(250018, 60); + statRuneChances.put(250009, 60); + statRuneChances.put(250027, 60); + statRuneChances.put(250036, 60); + statRuneChances.put(250000, 60); + + //+5, Enhanced + statRuneChances.put(250019, 60); + statRuneChances.put(250010, 60); + statRuneChances.put(250028, 60); + statRuneChances.put(250037, 60); + statRuneChances.put(250001, 60); + + //+10 Exceptional + statRuneChances.put(250020, 60); + statRuneChances.put(250011, 60); + statRuneChances.put(250029, 60); + statRuneChances.put(250038, 60); + statRuneChances.put(250002, 60); + + //+15, Amazing + statRuneChances.put(250021, 60); + statRuneChances.put(250012, 60); + statRuneChances.put(250030, 60); + statRuneChances.put(250039, 60); + statRuneChances.put(250003, 60); + + //+20, Incredible + statRuneChances.put(250022, 60); + statRuneChances.put(250013, 60); + statRuneChances.put(250031, 60); + statRuneChances.put(250040, 60); + statRuneChances.put(250004, 60); + + //+25, Great + statRuneChances.put(250023, 60); + statRuneChances.put(250014, 60); + statRuneChances.put(250032, 60); + statRuneChances.put(250041, 60); + statRuneChances.put(250005, 60); + + //+30, Heroic + statRuneChances.put(250024, 60); + statRuneChances.put(250015, 60); + statRuneChances.put(250033, 60); + statRuneChances.put(250042, 60); + statRuneChances.put(250006, 60); + + //+35, Legendary + statRuneChances.put(250025, 60); + statRuneChances.put(250016, 60); + statRuneChances.put(250034, 60); + statRuneChances.put(250043, 60); + statRuneChances.put(250007, 60); + + //+40, of the Gods + statRuneChances.put(250026, 60); + statRuneChances.put(250017, 60); + statRuneChances.put(250035, 60); + statRuneChances.put(250044, 60); + statRuneChances.put(250008, 60); + } + + public ConcurrentHashMap getLootTable() { + return lootTable; + } + + private static void HandleDropLogs(ItemBase itemBase){ + + if (itemBase == null) + return; + + LootTable.dropCount++; //item dropped, add to all item count. + + + if (LootTable.itemsDroppedMap.get(itemBase) == null){ + LootTable.itemsDroppedMap.put(itemBase, 1); //First time dropping, make count 1. + }else{ + int dropCount = LootTable.itemsDroppedMap.get(itemBase); + dropCount++; + LootTable.itemsDroppedMap.put(itemBase, dropCount); + } + + switch (itemBase.getType()){ + case RESOURCE: + LootTable.resourceCount++; + + if (LootTable.resourceDroppedMap.get(itemBase) == null){ + LootTable.resourceDroppedMap.put(itemBase, 1); //First time dropping, make count 1. + }else{ + int dropCount = LootTable.resourceDroppedMap.get(itemBase); + dropCount++; + LootTable.resourceDroppedMap.put(itemBase, dropCount); + } + break; + case RUNE: + LootTable.runeCount++; + if (LootTable.runeDroppedMap.get(itemBase) == null){ + LootTable.runeDroppedMap.put(itemBase, 1); //First time dropping, make count 1. + }else{ + int dropCount = LootTable.runeDroppedMap.get(itemBase); + dropCount++; + LootTable.runeDroppedMap.put(itemBase, dropCount); + } + break; + case CONTRACT: + LootTable.contractCount++; + + if (LootTable.contractDroppedMap.get(itemBase) == null){ + LootTable.contractDroppedMap.put(itemBase, 1); //First time dropping, make count 1. + }else{ + int dropCount = LootTable.contractDroppedMap.get(itemBase); + dropCount++; + LootTable.contractDroppedMap.put(itemBase, dropCount); + } + + break; + case WEAPON: //Glass Drop + + if (itemBase.isGlass()){ + LootTable.glassCount++; + if (LootTable.glassDroppedMap.get(itemBase) == null){ + LootTable.glassDroppedMap.put(itemBase, 1); //First time dropping, make count 1. + }else{ + int dropCount = LootTable.glassDroppedMap.get(itemBase); + dropCount++; + LootTable.glassDroppedMap.put(itemBase, dropCount); + } + } + + break; + } + + } + + public static Item CreateGamblerItem(Item item, PlayerCharacter gambler){ + + if (item == null) + return null; + + int groupID = 0; + + switch (item.getItemBase().getUUID()){ + case 971050: //Wrapped Axe + groupID = 3000; + break; + case 971051://Wrapped Great Axe + groupID = 3005; + break; + case 971052://Wrapped Throwing Axe + groupID = 3010; + break; + case 971053:// Wrapped Bow + groupID = 3015; + break; + case 971054://Wrapped Crossbow + groupID = 3020; + break; + case 971055: //Wrapped Dagger + groupID = 3025; + break; + case 971056: // Wrapped Throwing Dagger + groupID = 3030; + break; + case 971057: // Wrapped Hammer + groupID = 3035; + break; + case 971058:// Wrapped Great Hammer + groupID = 3040; + break; + case 971059:// Wrapped Throwing Hammer + groupID = 3045; + break; + case 971060:// Wrapped Polearm + groupID = 3050; + break; + case 971061:// Wrapped Spear + groupID = 3055; + break; + case 971062:// Wrapped Staff + groupID = 3060; + break; + case 971063:// Wrapped Sword + groupID = 3065; + break; + case 971064:// Wrapped Great Sword + groupID = 3070; + break; + case 971065:// Wrapped Unarmed Weapon + groupID = 3075; + break; + case 971066:// Wrapped Cloth Armor + groupID = 3100; + break; + case 971067:// Wrapped Light Armor + groupID = 3105; + break; + case 971068:// Wrapped Medium Armor + groupID = 3110; + break; + case 971069:// Wrapped Heavy Armor + groupID = 3115; + break; + case 971070:// Wrapped Rune + groupID = 3200; + break; + case 971071:// Wrapped City Improvement + groupID = 3210; + break; + } + //couldnt find group + if (groupID == 0) + return null; + + + LootTable lootGroup = LootTable.lootGroups.get(groupID); + + if (lootGroup == null) + return null; + float calculatedMobLevel; + int minSpawn; + int maxSpawn; + int spawnQuanity = 0; + int prefixValue = 0; + int suffixValue = 0; + int subTableID; + String modifierPrefix = ""; + String modifierSuffix = ""; + + // Lookup Table Variables + LootTable lootTable; + LootRow lootRow; + LootRow groupRow = null; + LootTable modTable; + LootTable modGroup; + LootRow modRow = null; + + // Used for actual generation of items + int itemBaseUUID; + ItemBase itemBase = null; + MobLoot mobLoot; + + + + int roll = ThreadLocalRandom.current().nextInt(100) + 1; //Does not return Max, but does return min? + + groupRow = lootGroup.getLootRow(roll); + + lootTable = LootTable.lootTables.get(groupRow.getValueOne()); + roll = ThreadLocalRandom.current().nextInt(100) + 1; + lootRow = lootTable.getLootRow(roll + 220); //get the item row from the bell's curve of level +-15 + + if (lootRow == null) + return null; //no item found for roll + + itemBaseUUID = lootRow.getValueOne(); + + + + if (lootRow.getValueOne() == 0) + return null; + + //handle quantities > 1 for resource drops + minSpawn = lootRow.getValueTwo(); + maxSpawn = lootRow.getValueThree(); + + // spawnQuanity between minspawn (inclusive) and maxspawn (inclusive) + if (maxSpawn > 1) + spawnQuanity = ThreadLocalRandom.current().nextInt((maxSpawn + 1 - minSpawn)) + minSpawn; + + + + //get modifierPrefix + + calculatedMobLevel = 49; + + + + int chanceMod = ThreadLocalRandom.current().nextInt(100) + 1; + + if (chanceMod < 25){ + modGroup = LootTable.modGroups.get(groupRow.getValueTwo()); + + if (modGroup != null) { + + + for (int a = 0;a<10;a++){ + roll = ThreadLocalRandom.current().nextInt(100) + 1; + modRow = modGroup.getLootRow(roll); + if (modRow != null) + break; + } + + + if (modRow != null) { + subTableID = modRow.getValueOne(); + + if (LootTable.modTables.containsKey(subTableID)) { + + modTable = LootTable.modTables.get(subTableID); + + roll = gaussianLevel((int)calculatedMobLevel); + + if (roll < modTable.minRoll) + roll = (int) modTable.minRoll; + + if (roll > modTable.maxRoll) + roll = (int) modTable.maxRoll; + + + + modRow = modTable.getLootRow(roll); + + if (modRow != null) { + prefixValue = modRow.getValueOne(); + modifierPrefix = modRow.getAction(); + } + } + } + } + }else if(chanceMod < 50){ + modGroup = LootTable.modGroups.get(groupRow.getValueThree()); + + if (modGroup != null) { + + for (int a = 0;a<10;a++){ + roll = ThreadLocalRandom.current().nextInt(100) + 1; + modRow = modGroup.getLootRow(roll); + if (modRow != null) + break; + } + + if (modRow != null) { + + subTableID = modRow.getValueOne(); + + if (LootTable.modTables.containsKey(subTableID)) { + + modTable = LootTable.modTables.get(subTableID); + roll = gaussianLevel((int)calculatedMobLevel); + + if (roll < modTable.minRoll) + roll = (int) modTable.minRoll; + + if (roll > modTable.maxRoll) + roll = (int) modTable.maxRoll; + + modRow = modTable.getLootRow(roll); + + if (modRow == null){ + modRow = modTable.getLootRow((int) ((modTable.minRoll + modTable.maxRoll) *.05f)); + } + + if (modRow != null) { + suffixValue = modRow.getValueOne(); + modifierSuffix = modRow.getAction(); + } + } + } + } + }else{ + modGroup = LootTable.modGroups.get(groupRow.getValueTwo()); + + if (modGroup != null) { + + + for (int a = 0;a<10;a++){ + roll = ThreadLocalRandom.current().nextInt(100) + 1; + modRow = modGroup.getLootRow(roll); + if (modRow != null) + break; + } + + + if (modRow != null) { + subTableID = modRow.getValueOne(); + + if (LootTable.modTables.containsKey(subTableID)) { + + modTable = LootTable.modTables.get(subTableID); + + roll = gaussianLevel((int)calculatedMobLevel); + + if (roll < modTable.minRoll) + roll = (int) modTable.minRoll; + + if (roll > modTable.maxRoll) + roll = (int) modTable.maxRoll; + + + + modRow = modTable.getLootRow(roll); + + if (modRow == null){ + modRow = modTable.getLootRow((int) ((modTable.minRoll + modTable.maxRoll) *.05f)); + } + + if (modRow != null) { + prefixValue = modRow.getValueOne(); + modifierPrefix = modRow.getAction(); + } + } + } + } + + //get modifierSuffix + modGroup = LootTable.modGroups.get(groupRow.getValueThree()); + + if (modGroup != null) { + + for (int a = 0;a<10;a++){ + roll = ThreadLocalRandom.current().nextInt(100) + 1; + modRow = modGroup.getLootRow(roll); + if (modRow != null) + break; + } + + if (modRow != null) { + + subTableID = modRow.getValueOne(); + + if (LootTable.modTables.containsKey(subTableID)) { + + modTable = LootTable.modTables.get(subTableID); + roll = gaussianLevel((int)calculatedMobLevel); + + if (roll < modTable.minRoll) + roll = (int) modTable.minRoll; + + if (roll > modTable.maxRoll) + roll = (int) modTable.maxRoll; + + modRow = modTable.getLootRow(roll); + + if (modRow == null){ + modRow = modTable.getLootRow((int) ((modTable.minRoll + modTable.maxRoll) *.05f)); + } + + if (modRow != null) { + suffixValue = modRow.getValueOne(); + modifierSuffix = modRow.getAction(); + } + } + } + } + } + + + itemBase = ItemBase.getItemBase(itemBaseUUID); + byte charges = (byte) itemBase.getNumCharges(); + short dur = (short) itemBase.getDurability(); + + + + short weight = itemBase.getWeight(); + if (!gambler.getCharItemManager().hasRoomInventory(weight)) { + return null; + } + + + Item gambledItem = new Item(itemBase, gambler.getObjectUUID(), + OwnerType.PlayerCharacter, charges, charges, dur, dur, + true, false,ItemContainerType.INVENTORY,(byte) 0, + new ArrayList<>(),""); + + if (spawnQuanity == 0 && itemBase.getType().equals(ItemType.RESOURCE)) + spawnQuanity = 1; + + if (spawnQuanity > 0) + item.setNumOfItems(spawnQuanity); + + try { + gambledItem = DbManager.ItemQueries.ADD_ITEM(gambledItem); + + } catch (Exception e) { + Logger.error(e); + } + + if (gambledItem == null) { + + return null; + } + if (!modifierPrefix.isEmpty()) + gambledItem.addPermanentEnchantment(modifierPrefix, 0); + + if (!modifierSuffix.isEmpty()) + gambledItem.addPermanentEnchantment(modifierSuffix, 0); + + + + //add item to inventory + gambler.getCharItemManager().addItemToInventory(gambledItem); + + gambler.getCharItemManager().updateInventory(); + + return gambledItem; + } + +} diff --git a/src/engine/objects/MaxSkills.java b/src/engine/objects/MaxSkills.java new file mode 100644 index 00000000..00a12414 --- /dev/null +++ b/src/engine/objects/MaxSkills.java @@ -0,0 +1,79 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; + +public class MaxSkills { + + private int runeID; + private int skillToken; + private int skillLevel; + private int maxSkillPercent; + + + + public static HashMap> MaxSkillsSet = new HashMap<>(); + + + /** + * ResultSet Constructor + */ + + public MaxSkills(ResultSet rs) throws SQLException { + this.runeID = rs.getInt("runeID"); + this.skillToken =rs.getInt("skillToken"); + this.skillLevel = rs.getInt("skillLevel"); + this.maxSkillPercent = rs.getInt("maxSkillPercent"); + } + + public MaxSkills(int runeID, int skillToken, int skillLevel, int maxSkillPercent) { + super(); + this.runeID = runeID; + this.skillToken = skillToken; + this.skillLevel = skillLevel; + this.maxSkillPercent = maxSkillPercent; + } + + public int getRuneID() { + return runeID; + } + + public void setRuneID(int runeID) { + this.runeID = runeID; + } + + public int getSkillLevel() { + return skillLevel; + } + + public void setSkillLevel(int skillLevel) { + this.skillLevel = skillLevel; + } + + public int getSkillToken() { + return skillToken; + } + + public void setSkillToken(int skillToken) { + this.skillToken = skillToken; + } + + public int getMaxSkillPercent() { + return maxSkillPercent; + } + + public void setMaxSkillPercent(int maxSkillPercent) { + this.maxSkillPercent = maxSkillPercent; + } +} diff --git a/src/engine/objects/MenuOption.java b/src/engine/objects/MenuOption.java new file mode 100644 index 00000000..8363cac4 --- /dev/null +++ b/src/engine/objects/MenuOption.java @@ -0,0 +1,58 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class MenuOption extends AbstractGameObject { + + private final int menuID; + private final String message; + private final int optionID; + private final int prereq; + + /** + * ResultSet Constructor + */ + public MenuOption(ResultSet rs) throws SQLException { + super(rs); + this.menuID = rs.getInt("menuID"); + this.message = rs.getString("message"); + this.optionID = rs.getInt("optionID"); + this.prereq = rs.getInt("prereq"); + } + + /* + * Getters + */ + public int getMenuID() { + return this.menuID; + } + + public String getMessage() { + return this.message; + } + + public int getOptionID() { + return this.optionID; + } + + public int getPrereq() { + return this.prereq; + } + + /* + * Database + */ + @Override + public void updateDatabase() {} +} diff --git a/src/engine/objects/MeshBounds.java b/src/engine/objects/MeshBounds.java new file mode 100644 index 00000000..5266746b --- /dev/null +++ b/src/engine/objects/MeshBounds.java @@ -0,0 +1,49 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.gameManager.DbManager; +import engine.math.Bounds; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class MeshBounds { + + public int meshID; + public final float minX; + public final float minY; + public final float minZ; + public final float maxX; + public final float maxY; + public final float maxZ; + public final float radius; + + public MeshBounds(ResultSet rs) throws SQLException { + + meshID = rs.getInt("meshID"); + minX = rs.getFloat("minX"); + minY = rs.getFloat("minY"); + minZ = rs.getFloat("minZ"); + maxX = rs.getFloat("maxX"); + maxY = rs.getFloat("maxY"); + maxZ = rs.getFloat("maxZ"); + float radiusX = (int) maxX; + float radiusZ = (int) maxZ; + + radius = Math.max(radiusX,radiusZ); + } + + public static void InitializeBuildingBounds(){ + Bounds.meshBoundsCache = DbManager.BuildingQueries.LOAD_MESH_BOUNDS(); + } + + +} diff --git a/src/engine/objects/Mine.java b/src/engine/objects/Mine.java new file mode 100644 index 00000000..e53c6d86 --- /dev/null +++ b/src/engine/objects/Mine.java @@ -0,0 +1,791 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum; +import engine.InterestManagement.WorldGrid; +import engine.db.archive.DataWarehouse; +import engine.db.archive.MineRecord; +import engine.gameManager.*; +import engine.net.ByteBufferWriter; +import engine.net.client.msg.ErrorPopupMsg; +import engine.server.MBServerStatics; +import engine.session.SessionID; +import org.pmw.tinylog.Logger; + +import java.net.UnknownHostException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; + +import static engine.gameManager.DbManager.MineQueries; +import static engine.gameManager.DbManager.getObject; +import static engine.math.FastMath.sqr; + +public class Mine extends AbstractGameObject { + + private String zoneName; + private Resource production; + private boolean isActive = false; + private float latitude; + private float longitude; + private float altitude; + private Guild owningGuild; + private int lastClaimerID; + private SessionID lastClaimerSessionID; + private int flags; + private int buildingID; + private Zone parentZone; + private MineProduction mineType; + public LocalDateTime openDate; + + public boolean dirtyMine = false; + //flags 1: never been claimed (make active). + + + + + + // Not persisted to DB + private String guildName; + private GuildTag guildTag; + private String nationName; + private GuildTag nationTag; + public static ConcurrentHashMap mineMap = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + public static ConcurrentHashMap towerMap = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + + private static long lastChange = System.currentTimeMillis(); + public static LocalDateTime effectiveMineDate; + + /** + * ResultSet Constructor + */ + public Mine(ResultSet rs) throws SQLException, UnknownHostException { + super(rs); + + this.mineType = MineProduction.getByName(rs.getString("mine_type")); + + float offsetX = rs.getFloat("mine_offsetX"); + float offsetZ = rs.getFloat("mine_offsetZ"); + int ownerUID = rs.getInt("mine_ownerUID"); + this.buildingID = rs.getInt("mine_buildingUID"); + this.flags = rs.getInt("flags"); + int parent = rs.getInt("parent"); + this.parentZone = ZoneManager.getZoneByUUID(parent); + if (parentZone != null) { + this.latitude = parentZone.getLoc().x + offsetX; + this.longitude = parentZone.getLoc().z + offsetZ; + this.altitude = parentZone.getLoc().y; + if (this.parentZone.getParent() != null) + this.zoneName = this.parentZone.getParent().getName(); + else + this.zoneName = this.parentZone.getName(); + } else { + Logger.error( "Missing parentZone of ID " + parent); + this.latitude = -1000; + this.longitude = 1000; + this.altitude = 0; + this.zoneName = "Unknown Mine"; + } + + + + this.owningGuild = Guild.getGuild(ownerUID); + Guild nation = null; + if (this.owningGuild != null && !this.owningGuild.isErrant()) { + this.guildName = this.owningGuild.getName(); + this.guildTag = this.owningGuild.getGuildTag(); + nation = this.owningGuild.getNation(); + } else { + this.guildName = ""; + this.guildTag = GuildTag.ERRANT; + nation = Guild.getErrantGuild(); + this.owningGuild = Guild.getErrantGuild(); + } + + int mineTime = this.owningGuild.getMineTime(); + + if(!nation.isErrant()) { + this.nationName = nation.getName(); + this.nationTag = nation.getGuildTag(); + mineTime = nation.getMineTime(); + } else { + this.nationName = ""; + this.nationTag = GuildTag.ERRANT; + + } + this.setActive(false); + this.production = Resource.valueOf(rs.getString("mine_resource")); + + this.lastClaimerID = 0; + this.lastClaimerSessionID = null; + + java.sql.Timestamp mineTimeStamp = rs.getTimestamp("mine_openDate"); + + + Building building = BuildingManager.getBuildingFromCache(this.buildingID); + + + if (mineTimeStamp == null && (this.owningGuild == null || this.owningGuild.isErrant() || building.getRank() < 1)){ + if (building != null){ + String zoneName = building.getParentZone().getName(); + String parentZoneName = building.getParentZone().getParent().getName(); + Logger.info(zoneName + " in " + parentZoneName + " has a dirty mine, setting active."); + } + this.dirtyMine = true; + openDate = LocalDateTime.now().withMinute(0).withSecond(0).withNano(0); + return; + }else if (this.owningGuild.isErrant() || nation.isErrant()){ + openDate = LocalDateTime.now().withMinute(0).withSecond(0).withNano(0); + return; + }else if (mineTimeStamp == null){ + + this.openDate = LocalDateTime.now().withHour(mineTime).withMinute(0).withSecond(0).withNano(0); + + if (LocalDateTime.now().isAfter(this.openDate.plusHours(1))) + this.openDate = this.openDate.plusDays(1); + return; + }else{ + this.openDate = mineTimeStamp.toLocalDateTime().withHour(mineTime); + + if (LocalDateTime.now().isAfter(this.openDate.plusHours(1))){ + this.openDate = this.openDate.plusDays(1); + return; + } + } + + //after 1 day... + if(this.openDate.getDayOfYear() - LocalDateTime.now().getDayOfYear() > 1){ + this.openDate = this.openDate.withDayOfYear(LocalDateTime.now().getDayOfYear()); + if (LocalDateTime.now().isAfter(this.openDate.plusHours(1))) + this.openDate = this.openDate.plusDays(1); + return; + } + + } + + public static void SendMineAttackMessage(Building mine){ + + if (mine.getBlueprint() == null) + return; + + if (mine.getBlueprint().getBuildingGroup() != Enum.BuildingGroup.MINE) + return; + + + if (mine.getGuild().isErrant()) + return; + + if (mine.getGuild().getNation().isErrant()) + return; + + if (mine.getTimeStamp("MineAttack") > System.currentTimeMillis()) + return; + + mine.getTimestamps().put("MineAttack", System.currentTimeMillis() + MBServerStatics.ONE_MINUTE); + + ChatManager.chatNationInfo(mine.getGuild().getNation(), mine.getName() + " in " + mine.getParentZone().getParent().getName() + " is Under attack!"); + } + + private void setNextMineWindow() { + + //default time, 9 pm est + int mineHour = 21; + + if (this.owningGuild != null || this.owningGuild.isErrant() == false) + mineHour = this.owningGuild.getNation().getMineTime(); + int days = 1; + + //midnight hours + if (this.openDate.getHour() != 0 && this.openDate.getHour() != 24 && (this.openDate.getDayOfMonth() == LocalDateTime.now().getDayOfMonth())) + if (mineHour == 0 || mineHour == 24) + days = 2; + + LocalDateTime newTime = this.openDate.plusDays(days).withHour(mineHour).withMinute(0).withSecond(0).withNano(0); + + DbManager.MineQueries.CHANGE_MINE_TIME(this, newTime); + this.openDate = newTime; + } + + public static void loadAllMines() { + + // Set current mine effective date +try{ + + + effectiveMineDate = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0); + + //Load mine resources + MineProduction.addResources(); + + //pre-load all building sets + ArrayList serverMines = MineQueries.GET_ALL_MINES_FOR_SERVER(); + + for (Mine mine : serverMines) { + Mine.mineMap.put(mine, mine.buildingID); + Mine.towerMap.put(mine.buildingID, mine); + mine.initializeMineTime(); + } + +}catch (Exception e){ + e.printStackTrace(); +} + } + + /* + * Getters + */ + private void initializeMineTime(){ + + //Mine time has already been set at loading from the database. skip. + + if (this.openDate != null) + return; + + Guild nation = null; + + if (this.owningGuild != null) + nation = this.owningGuild.getNation(); + + int mineTime = (nation != null && !nation.isErrant()) ? nation.getMineTime() : MBServerStatics.MINE_EARLY_WINDOW; + + LocalDateTime openDate = LocalDateTime.now().withHour(mineTime).withMinute(0).withSecond(0).withNano(0); + + //Failed to Update Database, default mine time. + + if (!MineQueries.CHANGE_MINE_TIME(this, openDate)){ + Logger.info("Mine with UUID " + this.getObjectUUID() + " failed to set Mine Window. Defaulting to Earliest."); + openDate = openDate.withHour(MBServerStatics.MINE_EARLY_WINDOW).withMinute(0).withSecond(0).withNano(0); + this.openDate = openDate; + return; + } + + this.openDate = openDate; + + } + + public boolean changeProductionType(Resource resource){ + if (!this.validForMine(resource)) + return false; + //update resource in database; + if(!MineQueries.CHANGE_RESOURCE(this, resource)) + return false; + + this.production = resource; + return true; + } + + public MineProduction getMineType() { + return this.mineType; + } + + public String getZoneName() { + return this.zoneName; + } + + public Resource getProduction() { + return this.production; + } + + public boolean getIsActive() { + return this.isActive; + } + + public float getAltitude() { + return this.altitude; + } + + public Guild getOwningGuild() { + return this.owningGuild; + } + + public int getFlags() { + return flags; + } + + public void setFlags(int flags) { + this.flags = flags; + } + + public Zone getParentZone() { + return parentZone; + } + + public GuildTag getGuildTag() { + return guildTag; + } + + public void setMineType(String type) { + this.mineType = MineProduction.getByName(type); + } + + public void setActive(boolean isAc) { + + this.isActive = isAc; + Building building = BuildingManager.getBuildingFromCache(this.buildingID); + if (building != null && !this.isActive) + building.isDeranking.compareAndSet(true, false); + } + + public void setOwningGuild(Guild owningGuild) { + this.owningGuild = owningGuild; + } + + public static Mine getMineFromTower(int towerID) { + return Mine.towerMap.get(towerID); + } + + public boolean validForMine(Resource r) { + if (this.mineType == null) + return false; + return this.mineType.validForMine(r, this.isExpansion()); + } + + /* + * Serialization + */ + + public static void serializeForClientMsg(Mine mine,ByteBufferWriter writer) { + writer.putInt(mine.getObjectType().ordinal()); + writer.putInt(mine.getObjectUUID()); + writer.putInt(mine.getObjectUUID()); //actually a hash of mine + // writer.putInt(0x215C92BB); //this.unknown1); + writer.putString(mine.mineType.name); + writer.putString(mine.zoneName); + writer.putInt(mine.production.hash); + writer.putInt(mine.production.baseProduction); + writer.putInt(mine.getModifiedProductionAmount()); //TODO calculate range penalty here + writer.putInt(3600); //window in seconds + + LocalDateTime mw = mine.openDate; + + writer.putLocalDateTime(mw); + mw = mw.plusHours(1); + writer.putLocalDateTime(mw); + writer.put(mine.isActive ? (byte) 0x01 : (byte) 0x00); + + writer.putFloat(mine.latitude); + writer.putFloat(mine.altitude); + writer.putFloat(mine.longitude); + writer.putInt(mine.isExpansion() ? mine.mineType.xpacHash : mine.mineType.hash); + + writer.putString(mine.guildName); + GuildTag._serializeForDisplay(mine.guildTag,writer); + writer.putString(mine.nationName); + GuildTag._serializeForDisplay(mine.nationTag,writer); + } + + public void serializeForMineProduction(ByteBufferWriter writer) { + writer.putInt(this.getObjectType().ordinal()); + writer.putInt(this.getObjectUUID()); + writer.putInt(this.getObjectUUID()); //actually a hash of mine + // writer.putInt(0x215C92BB); //this.unknown1); + writer.putString(this.mineType.name); + writer.putString(this.zoneName); + writer.putInt(this.production.hash); + writer.putInt(this.production.baseProduction); + writer.putInt(this.getModifiedProductionAmount()); //TODO calculate range penalty here + writer.putInt(3600); //window in seconds + writer.putInt(this.isExpansion() ? this.mineType.xpacHash : this.mineType.hash); + } + + public static ArrayList getMinesForGuild(int guildID) { + ArrayList mineList = new ArrayList<>(); + for (Mine mine : Mine.mineMap.keySet()) { + if (mine.owningGuild != null && mine.owningGuild.getObjectUUID() == guildID) + mineList.add(mine); + } + return mineList; + } + + public static long getLastChange() { + return lastChange; + } + + public static void setLastChange(long lastChange) { + Mine.lastChange = lastChange; + } + + /* + * Database + */ + public static Mine getMine(int UID){ + return MineQueries.GET_MINE(UID); + + } + + public static ArrayList getMines() { + return new ArrayList<>(mineMap.keySet()); + } + + @Override + public void updateDatabase() { + // TODO Create update logic. + } + + public int getBuildingID() { + return buildingID; + } + + public void setBuildingID(int buildingID) { + this.buildingID = buildingID; + } + + public void handleStartMineWindow() { + + // Do not open errant mines until after woo + + // if ((this.getOwningGuild() == null) && + // (this.getOpenDate().isAfter(DateTime.now()))) + // return; + + this.lastClaimerID = 0; + this.setActive(true); + ChatManager.chatSystemChannel(this.zoneName + "'s Mine is now Active!"); + Logger.info(this.zoneName + "'s Mine is now Active!"); + } + + public static boolean validClaimer(PlayerCharacter pc) { + //verify the player exists + if (pc == null) + return false; + + //verify the player is in valid guild + Guild g = pc.getGuild(); + if (g == null) { + ChatManager.chatSystemError(pc, "Mine can only be claimed by a guild."); + return false; + } else if (g.isErrant()) { + ChatManager.chatSystemError(pc, "Guild cannot be Errant to claim.."); + return false; + } + + //verify the player is in nation + Guild n = g.getNation(); + if (n.isErrant()) { + ChatManager.chatSystemError(pc, "Must have a Nation"); + return false; + } + + + if (SessionManager.getPlayerCharacterByID(pc.getObjectUUID()) == null){ + return false; + } + //Get a count of nation mines, can't go over capital tol rank. + City capital = n.getOwnedCity(); + City guildCity = g.getOwnedCity(); + if (guildCity == null){ + ChatManager.chatSystemError(pc, "Guild must own city to claim."); + return false; + } + if (capital == null) { + ChatManager.chatSystemError(pc, "Guild must own city to claim."); + return false; + } + + if (guildCity.getWarehouse() == null){ + ChatManager.chatSystemError(pc, "City must own warehouse for to claim."); + return false; + } + + Building tol = capital.getTOL(); + + if (tol == null) { + ChatManager.chatSystemError(pc, "Tree of life not found for city."); + return false; + } + + int rank = tol.getRank(); + + if (rank < 1) { + ChatManager.chatSystemError(pc, "Tree of life is not yet sprouted."); + return false; + } + + int mineCnt = 0; + + mineCnt += Mine.getMinesForGuild(n.getObjectUUID()).size(); + for (Guild guild: n.getSubGuildList()){ + mineCnt += Mine.getMinesForGuild(guild.getObjectUUID()).size(); + } + + + if (mineCnt > rank) { + ChatManager.chatSystemError(pc, "Your Nation can only hold " + tol.getRank() + " mines. Your Nation already has" + mineCnt); + return false; + } + + return true; + } + + + public void handleDestroyMine() { + + if (!this.isActive) + return; + + //remove tags from mine + + this.guildName = ""; + this.nationName = ""; + this.owningGuild = null; + Mine.setLastChange(System.currentTimeMillis()); + + // remove hirelings + + Building building = (Building) getObject(Enum.GameObjectType.Building, this.buildingID); + BuildingManager.cleanupHirelings(building); + } + + public boolean handleEndMineWindow(){ + + Building mineBuilding = BuildingManager.getBuildingFromCache(this.buildingID); + + if (mineBuilding == null){ + Logger.debug( "Failed to Activate Mine with UID " + this.getObjectUUID() +". Unable to Load Building with UID " +this.buildingID); + return false; + } + + if (mineBuilding.getRank() > 0) { + //never knocked down, let's just move on. + //hasn't been claimed since server start. + if (this.dirtyMine && this.lastClaimerID == 0 && (this.owningGuild == null || this.owningGuild.isErrant())) + return false; + this.setActive(false); + setNextMineWindow(); + return true; + } + + PlayerCharacter claimer = PlayerCharacter.getFromCache(this.lastClaimerID); + + if (!validClaimer(claimer)){ + LocalDateTime resetTime = LocalDateTime.now().withDayOfMonth(LocalDateTime.now().getDayOfMonth()).withHour(LocalDateTime.now().getHour()).withMinute(0).withSecond(0).withNano(0); + this.openDate = resetTime; + return false; + } + + + // //verify the player hasn't logged out since claim + + // if (SessionManager.getSession(claimer) == null) + // return false; + // if (!SessionManager.getSession(claimer).getSessionID().equals(this.lastClaimerSessionID)) + // return false; + + if (this.owningGuild == null || this.owningGuild.isErrant() || this.owningGuild.getNation().isErrant()){ + LocalDateTime resetTime = LocalDateTime.now().withDayOfMonth(LocalDateTime.now().getDayOfMonth()).withHour(LocalDateTime.now().getHour()).withMinute(0).withSecond(0).withNano(0); + this.openDate = resetTime; + return false; + } + + + //Update ownership to map + + this.guildName = this.owningGuild.getName(); + this.guildTag = this.owningGuild.getGuildTag(); + Guild nation = this.owningGuild.getNation(); + this.nationName = nation.getName(); + this.nationTag = nation.getGuildTag(); + + LocalDateTime guildDate = LocalDateTime.now().withHour(this.owningGuild.getMineTime()).withMinute(0).withSecond(0).withNano(0); + + if (this.openDate.getDayOfMonth() == LocalDateTime.now().getDayOfMonth()) + if (this.owningGuild.getMineTime() == 0 || this.owningGuild.getMineTime() == 24) + guildDate = guildDate.plusDays(1); + + guildDate = guildDate.withHour(this.owningGuild.getMineTime()).withMinute(0).withSecond(0).withNano(0); + this.openDate = guildDate; + Mine.setLastChange(System.currentTimeMillis()); + + if (mineBuilding.getRank() < 1){ + + if (claimer == null){ + this.lastClaimerID = 0; + updateGuildOwner(null); + return false; + } + + this.dirtyMine = false; + + mineBuilding.rebuildMine(); + WorldGrid.updateObject(mineBuilding); + ChatManager.chatSystemChannel(claimer.getName() + " has claimed the mine in " + this.parentZone.getParent().getName() + " for " + this.owningGuild.getName() + ". The mine is no longer active."); + + // Warehouse this claim event + + MineRecord mineRecord = MineRecord.borrow(this, claimer, Enum.RecordEventType.CAPTURE); + DataWarehouse.pushToWarehouse(mineRecord); + + }else{ + mineBuilding.setRank(mineBuilding.getRank()); + } + + this.setActive(false); + setNextMineWindow(); + return true; + } + + public boolean claimMine(PlayerCharacter claimer){ + + if (claimer == null) + return false; + + if (!validClaimer(claimer)) + return false; + + if (!this.isActive) { + ErrorPopupMsg.sendErrorMsg(claimer, "Can not for to claim inactive mine."); + return false; + } + + if (!updateGuildOwner(claimer)) + return false; + + this.lastClaimerID = claimer.getObjectUUID(); + Mine.setLastChange(System.currentTimeMillis()); + return true; + } + public boolean depositMineResources(){ + + if (this.owningGuild == null) + return false; + + if (this.owningGuild.getOwnedCity() == null) + return false; + + if (this.owningGuild.getOwnedCity().getWarehouse() == null) + return false; + + ItemBase resourceIB = ItemBase.getItemBase(this.production.UUID); + return this.owningGuild.getOwnedCity().getWarehouse().depositFromMine(this,resourceIB, this.getModifiedProductionAmount()); + } + + public boolean updateGuildOwner(PlayerCharacter pc){ + + Building mineBuilding = BuildingManager.getBuildingFromCache(this.buildingID); + + //should never return null, but let's check just in case. + + if (mineBuilding == null){ + ChatManager.chatSystemError(pc, "Unable to find mine tower."); + Logger.debug("Failed to Update Mine with UID " + this.getObjectUUID() +". Unable to Load Building with UID " +this.buildingID ); + return false; + } + + if (pc == null) { + this.owningGuild = null; + this.guildName = "None"; + this.guildTag = GuildTag.ERRANT; + this.nationName = "None"; + this.nationTag = GuildTag.ERRANT; + //Update Building. + mineBuilding.setOwner(null); + WorldGrid.updateObject(mineBuilding); + return true; + } + + if (SessionManager.getSession(pc) != null) { + this.lastClaimerSessionID = SessionManager.getSession(pc).getSessionID(); + } else { + Logger.error("Failed to find session for player " + pc.getObjectUUID()); + + return false; + } + + Guild guild = pc.getGuild(); + + if (guild.getOwnedCity() == null) + return false; + + if (!MineQueries.CHANGE_OWNER(this, guild.getObjectUUID())) { + Logger.debug("Database failed to Change Ownership of Mine with UID " + this.getObjectUUID()); + ChatManager.chatSystemError(pc, "Failed to claim Mine."); + return false; + } + + + //All tests passed. + + //update mine. + this.owningGuild = guild; + // this.guildName = this.owningGuild.getName(); + // this.guildTag = this.owningGuild.getGuildTag(); + // + // //nation will never return null, read getNation() + // Guild nation = this.owningGuild.getNation(); + // this.nationName = nation.getName(); + // this.nationTag = nation.getGuildTag(); + + //Update Building. + PlayerCharacter guildLeader = (PlayerCharacter) Guild.GetGL(this.owningGuild); + if (guildLeader != null) + mineBuilding.setOwner(guildLeader); + WorldGrid.updateObject(mineBuilding); + return true; + } + + public boolean isExpansion(){ + return (this.flags & 2) != 0; + } + + public int getModifiedProductionAmount(){ + //TODO Calculate Distance modifications. + + //calculate base values. + int baseProduction = this.production.baseProduction; + float baseModValue = this.production.baseProduction * .1f; + float rankModValue = this.production.baseProduction * .0143f; + float totalModded = 0; + + //get Mine Building. + Building mineBuilding = BuildingManager.getBuilding(this.buildingID); + if (mineBuilding == null) + return this.production.baseProduction; + for (AbstractCharacter harvester:mineBuilding.getHirelings().keySet()){ + totalModded += baseModValue; + totalModded += rankModValue * harvester.getRank(); + } + //add base production on top; + totalModded += baseProduction; + //skip distance check for expansion. + if (this.isExpansion()) + return (int) totalModded; + + if (this.owningGuild != null){ + if(this.owningGuild.getOwnedCity() != null){ + float distanceSquared = this.owningGuild.getOwnedCity().getLoc().distanceSquared2D(mineBuilding.getLoc()); + + if (distanceSquared > sqr(10000 * 3)) + totalModded *=.25f; + else if (distanceSquared > sqr(10000 * 2)) + totalModded *= .50f; + else if (distanceSquared > sqr(10000)) + totalModded *= .75f; + } + } + return (int) totalModded; + + } + +} diff --git a/src/engine/objects/MineProduction.java b/src/engine/objects/MineProduction.java new file mode 100644 index 00000000..76c15f0a --- /dev/null +++ b/src/engine/objects/MineProduction.java @@ -0,0 +1,94 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import java.util.HashMap; + +public enum MineProduction { + + LUMBER("Lumber Camp", new HashMap<>(), Resource.WORMWOOD, 1618637196, 1663491950), + ORE("Ore Mine", new HashMap<>(), Resource.OBSIDIAN, 518103023, -788976428), + GOLD("Gold Mine", new HashMap<>(), Resource.GALVOR, -662193002, -1227205358), + MAGIC("Magic Mine", new HashMap<>(), Resource.BLOODSTONE, 504746863, -1753567069); + + public final String name; + public final HashMap resources; + public final Resource xpac; + public final int hash; + public final int xpacHash; + + MineProduction(String name, HashMapresources, Resource xpac, int hash, int xpacHash) { + this.name = name; + this.resources = resources; + this.xpac = xpac; + this.hash = hash; + this.xpacHash = xpacHash; + } + + public static void addResources() { + if (MineProduction.LUMBER.resources.size() == 0) { + MineProduction.LUMBER.resources.put(7, Resource.GOLD); + MineProduction.LUMBER.resources.put(1580004, Resource.LUMBER); + MineProduction.LUMBER.resources.put(1580005, Resource.OAK); + MineProduction.LUMBER.resources.put(1580006, Resource.BRONZEWOOD); + MineProduction.LUMBER.resources.put(1580007, Resource.MANDRAKE); + } + if (MineProduction.ORE.resources.size() == 0) { + MineProduction.ORE.resources.put(7, Resource.GOLD); + MineProduction.ORE.resources.put(1580000, Resource.STONE); + MineProduction.ORE.resources.put(1580001, Resource.TRUESTEEL); + MineProduction.ORE.resources.put(1580002, Resource.IRON); + MineProduction.ORE.resources.put(1580003, Resource.ADAMANT); + } + if (MineProduction.GOLD.resources.size() == 0) { + MineProduction.GOLD.resources.put(7, Resource.GOLD); + MineProduction.GOLD.resources.put(1580000, Resource.STONE); + MineProduction.GOLD.resources.put(1580008, Resource.COAL); + MineProduction.GOLD.resources.put(1580009, Resource.AGATE); + MineProduction.GOLD.resources.put(1580010, Resource.DIAMOND); + MineProduction.GOLD.resources.put(1580011, Resource.ONYX); + } + if (MineProduction.MAGIC.resources.size() == 0) { + MineProduction.MAGIC.resources.put(7, Resource.GOLD); + MineProduction.MAGIC.resources.put(1580012, Resource.AZOTH); + MineProduction.MAGIC.resources.put(1580013, Resource.ORICHALK); + MineProduction.MAGIC.resources.put(1580014, Resource.ANTIMONY); + MineProduction.MAGIC.resources.put(1580015, Resource.SULFUR); + MineProduction.MAGIC.resources.put(1580016, Resource.QUICKSILVER); + } + } + + public static MineProduction getByName(String name) { + if (name.toLowerCase().equals("lumber")) + return MineProduction.LUMBER; + else if (name.toLowerCase().equals("ore")) + return MineProduction.ORE; + else if (name.toLowerCase().equals("gold")) + return MineProduction.GOLD; + else + return MineProduction.MAGIC; + } + + public boolean validForMine(Resource r, boolean isXpac) { + if (r == null) + return false; + if (this.resources.containsKey(r.UUID)) + return true; + else return isXpac && r.UUID == this.xpac.UUID; + } + + + +//Name Xpac Resources +//Lumber Camp Wormwood Gold, Lumber, Oak, Bronzewood, Mandrake +//Ore Mine Obsidian Gold, Stone, Truesteal, Iron, Adamant +//Gold Mine Galvor Gold, Coal, Agate, Diamond, Onyx +//Magic Mine Bloodstone Gold, Orichalk, Azoth, Antimony, Quicksilver, Sulfer +} diff --git a/src/engine/objects/Mob.java b/src/engine/objects/Mob.java new file mode 100644 index 00000000..dfd30590 --- /dev/null +++ b/src/engine/objects/Mob.java @@ -0,0 +1,3011 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum; +import engine.Enum.*; +import engine.InterestManagement.HeightMap; +import engine.InterestManagement.WorldGrid; +import engine.ai.MobileFSM; +import engine.ai.MobileFSM.STATE; +import engine.exception.SerializationException; +import engine.gameManager.*; +import engine.job.JobContainer; +import engine.job.JobScheduler; +import engine.jobs.DeferredPowerJob; +import engine.jobs.UpgradeNPCJob; +import engine.math.Bounds; +import engine.math.Vector3fImmutable; +import engine.net.ByteBufferWriter; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.ManageCityAssetsMsg; +import engine.net.client.msg.PetMsg; +import engine.net.client.msg.PlaceAssetMsg; +import engine.powers.EffectsBase; +import engine.server.MBServerStatics; +import engine.server.world.WorldServer; +import org.joda.time.DateTime; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import static engine.net.client.msg.ErrorPopupMsg.sendErrorPopup; + +public class Mob extends AbstractIntelligenceAgent { + + protected int dbID; //the database ID + protected int loadID; + protected boolean isMob; + protected MobBase mobBase; + + //mob specific + + protected float spawnRadius; + protected int spawnTime; + + //used by static mobs + protected int parentZoneID; + protected Zone parentZone; + protected float statLat; + protected float statLon; + protected float statAlt; + protected Building building; + protected Contract contract; + private static ReentrantReadWriteLock createLock = new ReentrantReadWriteLock(); + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + + // Variables NOT to be stored in db + private static int staticID = 0; + private int currentID; + private int ownerUID = 0; //only used by pets + private boolean hasLoot = false; + private static ConcurrentHashMap mobMapByDBID = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private AbstractWorldObject fearedObject = null; + private int buildingID; + private boolean isSiege = false; + private boolean isPlayerGuard = false; + private long timeToSpawnSiege; + private AbstractCharacter npcOwner; + private Vector3fImmutable inBuildingLoc = null; + private final ConcurrentHashMap playerAgroMap = new ConcurrentHashMap<>(); + private boolean noAggro = false; + private STATE state = STATE.Disabled; + private int aggroTargetID = 0; + private boolean walkingHome = true; + private long lastAttackTime = 0; + private long deathTime = 0; + private ConcurrentHashMap siegeMinionMap = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + public ReentrantReadWriteLock minionLock = new ReentrantReadWriteLock(); + private int patrolPointIndex = 0; + private int lastMobPowerToken = 0; + private HashMap equip = null; + private String nameOverride = ""; + private Regions lastRegion = null; + private long despawnTime = 0; + private DeferredPowerJob weaponPower; + private DateTime upgradeDateTime = null; + private boolean lootSync = false; + private int fidalityID = 0; + private int equipmentSetID = 0; + private int lootSet = 0; + private boolean isGuard; + private ArrayList fidelityRunes = null; + + public boolean despawned = false; + public Vector3fImmutable destination = Vector3fImmutable.ZERO; + public Vector3fImmutable localLoc = Vector3fImmutable.ZERO; + + /** + * No Id Constructor + */ + public Mob(String firstName, String lastName, short statStrCurrent, short statDexCurrent, short statConCurrent, + short statIntCurrent, short statSpiCurrent, short level, int exp, boolean sit, boolean walk, boolean combat, Vector3fImmutable bindLoc, + Vector3fImmutable currentLoc, Vector3fImmutable faceDir, short healthCurrent, short manaCurrent, short stamCurrent, Guild guild, + byte runningTrains, int npcType, boolean isMob, Zone parent,Building building, int contractID) { + super( firstName, lastName, statStrCurrent, statDexCurrent, statConCurrent, statIntCurrent, statSpiCurrent, level, exp, sit, + walk, combat, bindLoc, currentLoc, faceDir, healthCurrent, manaCurrent, stamCurrent, guild, runningTrains); + + this.dbID = MBServerStatics.NO_DB_ROW_ASSIGNED_YET; + this.state = STATE.Idle; + this.loadID = npcType; + this.isMob = isMob; + this.mobBase = MobBase.getMobBase(loadID); + this.currentID = MBServerStatics.NO_DB_ROW_ASSIGNED_YET; + this.parentZone = parent; + this.parentZoneID = (parent != null) ? parent.getObjectUUID() : 0; + this.building = building; + + if (building != null) + this.buildingID = building.getObjectUUID(); + else this.buildingID = 0; + + if (contractID == 0) + this.contract = null; + else + this.contract = DbManager.ContractQueries.GET_CONTRACT(contractID); + + if (this.contract != null) + this.level = 10; + + //initializeMob(false, false); + clearStatic(); + } + + /** + * Normal Constructor + */ + public Mob(String firstName, String lastName, short statStrCurrent, short statDexCurrent, short statConCurrent, + short statIntCurrent, short statSpiCurrent, short level, int exp, boolean sit, boolean walk, boolean combat, Vector3fImmutable bindLoc, + Vector3fImmutable currentLoc, Vector3fImmutable faceDir, short healthCurrent, short manaCurrent, short stamCurrent, Guild guild, + byte runningTrains, int npcType, boolean isMob, Zone parent, int newUUID, Building building, int contractID) { + super( firstName, lastName, statStrCurrent, statDexCurrent, statConCurrent, statIntCurrent, statSpiCurrent, level, exp, sit, + walk, combat, bindLoc, currentLoc, faceDir, healthCurrent, manaCurrent, stamCurrent, guild, runningTrains, newUUID); + this.state = STATE.Idle; + this.dbID = newUUID; + this.loadID = npcType; + this.isMob = isMob; + + if (contractID == 0) + this.contract = null; + else + this.contract = DbManager.ContractQueries.GET_CONTRACT(contractID); + + this.mobBase = MobBase.getMobBase(loadID); + this.parentZone = parent; + this.parentZoneID = (parent != null) ? parent.getObjectUUID() : 0; + this.building = building; + initializeMob(false,false,false); + clearStatic(); + } + + /** + * Pet Constructor + */ + public Mob( MobBase mobBase, Guild guild, Zone parent, short level, PlayerCharacter owner, int tableID) { + super(mobBase.getFirstName(), "", (short) 0, (short) 0, (short) 0, (short) 0, (short) 0, level, 0, false, true, false, owner.getLoc(), owner.getLoc(), owner.getFaceDir(), (short) mobBase.getHealthMax(), (short) 0, (short) 0, guild, (byte) 0, tableID); + this.state = STATE.Idle; + this.dbID = tableID; + this.loadID = mobBase.getObjectUUID(); + this.isMob = true; + this.mobBase = mobBase; + this.parentZone = parent; + this.parentZoneID = (parent != null) ? parent.getObjectUUID() : 0; + this.ownerUID = owner.getObjectUUID(); + initializeMob(true,false,false); + clearStatic(); + } + //SIEGE CONSTRUCTOR + public Mob( MobBase mobBase, Guild guild, Zone parent, short level, Vector3fImmutable loc, int tableID,boolean isPlayerGuard) { + super( mobBase.getFirstName(), "", (short) 0, (short) 0, (short) 0, (short) 0, (short) 0, level, 0, false, true, false, loc, loc, Vector3fImmutable.ZERO, (short) mobBase.getHealthMax(), (short) 0, (short) 0, guild, (byte) 0, tableID); + this.dbID = tableID; + this.loadID = mobBase.getObjectUUID(); + this.isMob = true; + this.mobBase = mobBase; + this.parentZone = parent; + this.parentZoneID = (parent != null) ? parent.getObjectUUID() : 0; + this.ownerUID = 0; + this.equip = new HashMap<>(); + initializeMob(false,true, isPlayerGuard); + clearStatic(); + } + + /** + * ResultSet Constructor + */ + public Mob(ResultSet rs) throws SQLException { + + super(rs); + + try{ + this.dbID = rs.getInt(1); + this.state = STATE.Idle; + this.loadID = rs.getInt("mob_mobbaseID"); + this.gridObjectType = GridObjectType.DYNAMIC; + this.spawnRadius = rs.getFloat("mob_spawnRadius"); + this.spawnTime = rs.getInt("mob_spawnTime"); + this.isMob = true; + this.parentZone = null; + this.statLat = rs.getFloat("mob_spawnX"); + this.statAlt = rs.getFloat("mob_spawnY"); + this.statLon = rs.getFloat("mob_spawnZ"); + + this.localLoc = new Vector3fImmutable(this.statLat,this.statAlt,this.statLon); + + this.parentZoneID = rs.getInt("parent"); + this.level = (short) rs.getInt("mob_level"); + int buildingID = rs.getInt("mob_buildingID"); + + try { + this.building = BuildingManager.getBuilding(buildingID); + }catch(Exception e){ + this.building = null; + Logger.error(e.getMessage()); + } + + int contractID = rs.getInt("mob_contractID"); + + if (contractID == 0) + this.contract = null; + else + this.contract = DbManager.ContractQueries.GET_CONTRACT(contractID); + + if (this.contract != null) + if (NPC.ISGuardCaptain(contract.getContractID())){ + this.spawnTime = 60*15; + this.isPlayerGuard = true; + this.nameOverride = contract.getName(); + } + + int guildID = rs.getInt("mob_guildUID"); + + + if (this.fidalityID != 0){ + if (this.building != null) + this.guild = this.building.getGuild(); + else + this.guild = Guild.getGuild(guildID); + }else + if (this.building != null) + this.guild = this.building.getGuild(); + else + this.guild = Guild.getGuild(guildID); + + if (this.guild == null) + this.guild = Guild.getErrantGuild(); + + java.util.Date sqlDateTime; + sqlDateTime = rs.getTimestamp("upgradeDate"); + + if (sqlDateTime != null) + upgradeDateTime = new DateTime(sqlDateTime); + else + upgradeDateTime = null; + + // Submit upgrade job if NPC is currently set to rank. + + if (this.upgradeDateTime != null) + Mob.submitUpgradeJob(this); + + this.mobBase = MobBase.getMobBase(loadID); + + this.setObjectTypeMask(MBServerStatics.MASK_MOB | this.getTypeMasks()); + + if (this.mobBase != null && this.spawnTime == 0) + this.spawnTime = this.mobBase.getSpawnTime(); + + this.bindLoc = new Vector3fImmutable(this.statLat, this.statAlt,this.statLon); + + this.setParentZone(ZoneManager.getZoneByUUID(this.parentZoneID)); + + + this.fidalityID = rs.getInt("fidalityID"); + + this.equipmentSetID = rs.getInt("equipmentSet"); + + if (this.contract != null) + this.equipmentSetID = this.contract.getEquipmentSet(); + + this.lootSet = (rs.getInt("lootSet")); + + if (this.fidalityID != 0) + this.nameOverride = rs.getString("mob_name"); + + if (this.fidalityID != 0){ + + Zone parentZone = ZoneManager.getZoneByUUID(this.parentZoneID); + if (parentZone != null){ + this.fidelityRunes = WorldServer.ZoneFidelityMobRunes.get(parentZone.getLoadNum()).get(this.fidalityID); + + if (this.fidelityRunes != null) + for (Integer runeID : this.fidelityRunes){ + if (runeID == 252623 ){ + this.isGuard = true; + this.noAggro = true; + } + } + } + } + } catch(Exception e){ + Logger.error( currentID + ""); + } + + try{ + initializeMob(false,false,this.isPlayerGuard); + } catch(Exception e){ + Logger.error(e); + } + + } + + private void clearStatic() { + + if (this.parentZone != null) + this.parentZone.zoneMobSet.remove(this); + + this.parentZone = null; + this.statLat = 0f; + this.statLon = 0f; + this.statAlt = 0f; + } + + private void initializeMob(boolean isPet, boolean isSiege, boolean isGuard) { + + if (this.mobBase != null) { + + this.gridObjectType = GridObjectType.DYNAMIC; + this.healthMax = this.mobBase.getHealthMax(); + this.manaMax = 0; + this.staminaMax = 0; + this.setHealth(this.healthMax); + this.mana.set(this.manaMax); + this.stamina.set(this.staminaMax); + + if(!this.nameOverride.isEmpty()) + this.firstName = this.nameOverride; + else + this.firstName = this.mobBase.getFirstName(); + if (isPet) { + this.setObjectTypeMask(MBServerStatics.MASK_PET | this.getTypeMasks()); + if (ConfigManager.serverType.equals(ServerType.LOGINSERVER)) + this.setLoc(this.getLoc()); + } + if (!isPet && this.contract == null) { + this.level = (short) this.mobBase.getLevel(); + } + + } else + this.level = 1; + + //add this npc to building + if (this.building != null && this.loadID != 0 && this.fidalityID == 0) { + + int maxSlots; + maxSlots = building.getBlueprint().getSlotsForRank(this.building.getRank()); + + for (int slot = 1; slot < maxSlots + 1; slot++) { + if (!this.building.getHirelings().containsValue(slot)) { + this.building.getHirelings().put(this, slot); + break; + } + } + } + + //set bonuses + this.bonuses = new PlayerBonuses(this); + + //TODO set these correctly later + this.rangeHandOne = 8; + this.rangeHandTwo = -1; + this.minDamageHandOne = 0; + this.maxDamageHandOne = 0; + this.minDamageHandTwo = 1; + this.maxDamageHandTwo = 4; + this.atrHandOne = 300; + this.atrHandOne = 300; + this.defenseRating = (short) this.mobBase.getDefenseRating(); + this.isActive = true; + + this.charItemManager.load(); + + //load AI for general mobs. + + if (isPet || isSiege || (isGuard && this.contract == null)) + this.currentID = (--Mob.staticID); + else + this.currentID = this.dbID; + + if (!isPet && !isSiege && !this.isPlayerGuard) + loadInventory(); + + //store mobs by Database ID + + if (!isPet && !isSiege) + Mob.mobMapByDBID.put(this.dbID, this); + } + + private void initializeSkills() { + + if (this.mobBase.getMobBaseStats() == null) + return; + + long skillVector = this.mobBase.getMobBaseStats().getSkillSet(); + int skillValue = this.mobBase.getMobBaseStats().getSkillValue(); + + if (this.mobBase.getObjectUUID() >= 17233) { + for (CharacterSkills cs : CharacterSkills.values()) { + SkillsBase sb = DbManager.SkillsBaseQueries.GET_BASE_BY_TOKEN(cs.getToken()); + CharacterSkill css = new CharacterSkill(sb, this, 50); + this.skills.put(sb.getName(), css); + } + } else { + for (CharacterSkills cs : CharacterSkills.values()) { + if ((skillVector & cs.getFlag()) != 0) { + SkillsBase sb = DbManager.SkillsBaseQueries.GET_BASE_BY_TOKEN(cs.getToken()); + CharacterSkill css = new CharacterSkill(sb, this, skillValue); + this.skills.put(sb.getName(), css); + } + } + } + } + + private void initializeStaticEffects() { + + EffectsBase eb = null; + for (MobBaseEffects mbe : this.mobBase.getRaceEffectsList()) { + + eb = PowersManager.getEffectByToken(mbe.getToken()); + + if (eb == null) { + Logger.info( "EffectsBase Null for Token " + mbe.getToken()); + continue; + } + + //check to upgrade effects if needed. + if (this.effects.containsKey(Integer.toString(eb.getUUID()))) { + if (mbe.getReqLvl() > (int) this.level) + continue; + + Effect eff = this.effects.get(Integer.toString(eb.getUUID())); + + if (eff == null) + continue; + + if (eff.getTrains() > mbe.getRank()) + continue; + + //new effect is of a higher rank. remove old effect and apply new one. + eff.cancelJob(); + this.addEffectNoTimer(Integer.toString(eb.getUUID()), eb, mbe.getRank(), true); + } else { + if (mbe.getReqLvl() > (int) this.level) + continue; + + this.addEffectNoTimer(Integer.toString(eb.getUUID()), eb, mbe.getRank(), true); + } + } + + //Apply all rune effects. + // Only Captains have contracts + if (contract != null || this.isPlayerGuard){ + RuneBase guardRune = RuneBase.getRuneBase(252621); + for (MobBaseEffects mbe : guardRune.getEffectsList()) { + + eb = PowersManager.getEffectByToken(mbe.getToken()); + + if (eb == null) { + Logger.info( "EffectsBase Null for Token " + mbe.getToken()); + continue; + } + + //check to upgrade effects if needed. + if (this.effects.containsKey(Integer.toString(eb.getUUID()))) { + + if (mbe.getReqLvl() > (int) this.level) + continue; + + Effect eff = this.effects.get(Integer.toString(eb.getUUID())); + + if (eff == null) + continue; + + //Current effect is a higher rank, dont apply. + if (eff.getTrains() > mbe.getRank()) + continue; + + //new effect is of a higher rank. remove old effect and apply new one. + eff.cancelJob(); + this.addEffectNoTimer(Integer.toString(eb.getUUID()), eb, mbe.getRank(), true); + } else { + + if (mbe.getReqLvl() > (int) this.level) + continue; + + this.addEffectNoTimer(Integer.toString(eb.getUUID()), eb, mbe.getRank(), true); + } + } + + RuneBase WarriorRune = RuneBase.getRuneBase(2518); + for (MobBaseEffects mbe : WarriorRune.getEffectsList()) { + + eb = PowersManager.getEffectByToken(mbe.getToken()); + + if (eb == null) { + Logger.info( "EffectsBase Null for Token " + mbe.getToken()); + continue; + } + + //check to upgrade effects if needed. + if (this.effects.containsKey(Integer.toString(eb.getUUID()))) { + + if (mbe.getReqLvl() > (int) this.level) + continue; + + Effect eff = this.effects.get(Integer.toString(eb.getUUID())); + + if (eff == null) + continue; + + //Current effect is a higher rank, dont apply. + if (eff.getTrains() > mbe.getRank()) + continue; + + //new effect is of a higher rank. remove old effect and apply new one. + eff.cancelJob(); + this.addEffectNoTimer(Integer.toString(eb.getUUID()), eb, mbe.getRank(), true); + } else { + + if (mbe.getReqLvl() > (int) this.level) + continue; + + this.addEffectNoTimer(Integer.toString(eb.getUUID()), eb, mbe.getRank(), true); + } + } + } + + if (this.fidelityRunes != null){ + + for (int fidelityRune : this.fidelityRunes) { + + RuneBase rune = RuneBase.getRuneBase(fidelityRune); + + if (rune != null) + for (MobBaseEffects mbe : rune.getEffectsList()) { + + eb = PowersManager.getEffectByToken(mbe.getToken()); + if (eb == null) { + Logger.info("EffectsBase Null for Token " + mbe.getToken()); + continue; + } + + //check to upgrade effects if needed. + if (this.effects.containsKey(Integer.toString(eb.getUUID()))) { + if (mbe.getReqLvl() > (int) this.level) + continue; + + Effect eff = this.effects.get(Integer.toString(eb.getUUID())); + + if (eff == null) + continue; + + //Current effect is a higher rank, dont apply. + if (eff.getTrains() > mbe.getRank()) + continue; + + //new effect is of a higher rank. remove old effect and apply new one. + eff.cancelJob(); + this.addEffectNoTimer(Integer.toString(eb.getUUID()), eb, mbe.getRank(), true); + + } else { + + if (mbe.getReqLvl() > (int) this.level) + continue; + + this.addEffectNoTimer(Integer.toString(eb.getUUID()), eb, mbe.getRank(), true); + } + } + } + }else + for (RuneBase rune : this.mobBase.getRunes()) { + for (MobBaseEffects mbe : rune.getEffectsList()) { + + eb = PowersManager.getEffectByToken(mbe.getToken()); + if (eb == null) { + Logger.info( "EffectsBase Null for Token " + mbe.getToken()); + continue; + } + + //check to upgrade effects if needed. + if (this.effects.containsKey(Integer.toString(eb.getUUID()))) { + if (mbe.getReqLvl() > (int) this.level) + continue; + + Effect eff = this.effects.get(Integer.toString(eb.getUUID())); + + if (eff == null) + continue; + + //Current effect is a higher rank, dont apply. + if (eff.getTrains() > mbe.getRank()) + continue; + + //new effect is of a higher rank. remove old effect and apply new one. + eff.cancelJob(); + this.addEffectNoTimer(Integer.toString(eb.getUUID()), eb, mbe.getRank(), true); + } else { + + if (mbe.getReqLvl() > (int) this.level) + continue; + + this.addEffectNoTimer(Integer.toString(eb.getUUID()), eb, mbe.getRank(), true); + } + } + } + } + + /* + * Getters + */ + @Override + public int getDBID() { + return this.dbID; + } + + public int getLoadID() { + return loadID; + } + + @Override + public int getObjectUUID() { + return currentID; + } + + public float getSpawnX() { + return this.statLat; + } + + public float getSpawnY() { + return this.statAlt; + } + + public float getSpawnZ() { + return this.statLon; + } + + public float getSpawnRadius() { + return this.spawnRadius; + } + + public int getSpawnTime() { + + if (this.spawnTime == 0) + return MBServerStatics.RESPAWN_TIMER; + else + return this.spawnTime * 1000; + } + + //use getSpawnTime instead. This is just for init tables + public int getTrueSpawnTime() { + return this.spawnTime; + } + + public String getSpawnTimeAsString() { + if (this.spawnTime == 0) + return MBServerStatics.DEFAULT_SPAWN_TIME_MS / 1000 + " seconds (Default)"; + else + return this.spawnTime + " seconds"; + + } + + + public void setSpawnTime(int value) { + this.spawnTime = value; + } + + @Override + public MobBase getMobBase() { + return this.mobBase; + } + + public int getMobBaseID() { + + if (this.mobBase != null) + return this.mobBase.getObjectUUID(); + + return 0; + } + + public Vector3fImmutable getTrueBindLoc() { + return this.bindLoc; + } + + public Zone getParentZone() { + return this.parentZone; + } + + public int getParentZoneID() { + + if (this.parentZone != null) + return this.parentZone.getObjectUUID(); + + return 0; + } + + @Override + public int getGuildUUID() { + + if (this.guild == null) + return 0; + + return this.guild.getObjectUUID(); + } + + @Override + public PlayerCharacter getOwner() { + + if (!this.isPet()) + return null; + + if (this.ownerUID == 0) + return null; + + return PlayerCharacter.getFromCache(this.ownerUID); + } + + public void setOwner(PlayerCharacter value) { + + if (value == null) + this.ownerUID = 0; + else + this.ownerUID = value.getObjectUUID(); + } + + @Override + public AbstractWorldObject getFearedObject() { + return this.fearedObject; + } + + public void setFearedObject(AbstractWorldObject awo) { + this.fearedObject = awo; + } + + public void setParentZone(Zone zone) { + + if (this.parentZone == null){ + zone.zoneMobSet.add(this); + this.parentZone = zone; + } + + this.bindLoc = Mob.GetSpawnRadiusLocation(this); + this.lastBindLoc = bindLoc; + this.setLoc(bindLoc); + this.stopMovement(bindLoc); + } + + @Override + public Vector3fImmutable getBindLoc() { + + if(this.isPet() && !this.isSiege) + return this.getOwner() != null? this.getOwner().getLoc() : this.getLoc(); + return this.bindLoc; + } + + /* + * Serialization + */ + + public static void __serializeForClientMsg(Mob mob,ByteBufferWriter writer) throws SerializationException { + } + + + + public static void serializeMobForClientMsgOtherPlayer(Mob mob,ByteBufferWriter writer, boolean hideAsciiLastName) + throws SerializationException { + Mob.serializeForClientMsgOtherPlayer(mob,writer); + } + + + public static void serializeForClientMsgOtherPlayer(Mob mob, ByteBufferWriter writer) + throws SerializationException { + writer.putInt(0); + writer.putInt(0); + + int tid = (mob.mobBase != null) ? mob.mobBase.getLoadID() : 0; + int classID = MobBase.GetClassType(mob.mobBase.getObjectUUID()); + if (mob.isPet()) { + writer.putInt(2); + writer.putInt(3); + writer.putInt(0); + writer.putInt(2522); + writer.putInt(GameObjectType.NPCClassRune.ordinal()); + writer.putInt(mob.currentID); + } else if (tid == 100570) { //kur'adar + writer.putInt(3); + Mob.serializeRune(mob,writer, 3, GameObjectType.NPCClassRuneTwo.ordinal(), 2518); //warrior class + serializeRune(mob,writer, 5, GameObjectType.NPCClassRuneThree.ordinal(), 252621); //guard rune + } else if (tid == 100962 || tid == 100965) { //Spydraxxx the Mighty, Denigo Tantric + writer.putInt(2); + serializeRune(mob,writer, 5, GameObjectType.NPCClassRuneTwo.ordinal(), 252621); //guard rune + }else if (mob.contract != null || mob.isPlayerGuard){ + writer.putInt(3); + serializeRune(mob,writer, 3, GameObjectType.NPCClassRuneTwo.ordinal(),MobBase.GetClassType(mob.getMobBaseID())); //warrior class + serializeRune(mob,writer, 5, GameObjectType.NPCClassRuneThree.ordinal(), 252621); //guard rune + }else { + + writer.putInt(1); + } + + //Generate Race Rune + writer.putInt(1); + writer.putInt(0); + + if (mob.mobBase != null) + writer.putInt(mob.mobBase.getLoadID()); + else + writer.putInt(mob.loadID); + + writer.putInt(mob.getObjectType().ordinal()); + writer.putInt(mob.currentID); + + //Send Stats + writer.putInt(5); + writer.putInt(0x8AC3C0E6); //Str + writer.putInt(0); + writer.putInt(0xACB82E33); //Dex + writer.putInt(0); + writer.putInt(0xB15DC77E); //Con + writer.putInt(0); + writer.putInt(0xE07B3336); //Int + writer.putInt(0); + writer.putInt(0xFF665EC3); //Spi + writer.putInt(0); + + if (!mob.nameOverride.isEmpty()){ + writer.putString(mob.nameOverride); + writer.putInt(0); + } else { + writer.putString(mob.firstName); + writer.putString(mob.lastName); + + } + + + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + + writer.put((byte) 0); + writer.putInt(mob.getObjectType().ordinal()); + writer.putInt(mob.currentID); + + if (mob.mobBase != null) { + writer.putFloat(mob.mobBase.getScale()); + writer.putFloat(mob.mobBase.getScale()); + writer.putFloat(mob.mobBase.getScale()); + } else { + writer.putFloat(1.0f); + writer.putFloat(1.0f); + writer.putFloat(1.0f); + } + + //Believe this is spawn loc, ignore for now + writer.putVector3f(mob.getLoc()); + + //Rotation + writer.putFloat(mob.getRot().y); + + //Inventory Stuff + writer.putInt(0); + + // get a copy of the equipped items. + + + if (mob.equip != null){ + + writer.putInt(mob.equip.size()); + + for (MobEquipment me:mob.equip.values()){ + MobEquipment.serializeForClientMsg(me,writer); + } + }else{ + writer.putInt(0); + } + + writer.putInt(mob.getRank()); + writer.putInt(mob.getLevel()); + writer.putInt(mob.getIsSittingAsInt()); //Standing + writer.putInt(mob.getIsWalkingAsInt()); //Walking + writer.putInt(mob.getIsCombatAsInt()); //Combat + writer.putInt(2); //Unknown + writer.putInt(1); //Unknown - Headlights? + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.put((byte) 0); + writer.put((byte) 0); + writer.put((byte) 0); + writer.putInt(0); + + if (mob.contract != null && mob.npcOwner == null){ + writer.put((byte) 1); + writer.putLong(0); + writer.putLong(0); + + if (mob.contract != null) + writer.putInt(mob.contract.getIconID()); + else + writer.putInt(0); //npc icon ID + + } else + writer.put((byte)0); + + + if (mob.npcOwner != null){ + writer.put((byte) 1); + writer.putInt(GameObjectType.PlayerCharacter.ordinal()); + writer.putInt(131117009); + writer.putInt(mob.npcOwner.getObjectType().ordinal()); + writer.putInt(mob.npcOwner.getObjectUUID()); + writer.putInt(8); + }else + writer.put((byte)0); + + if (mob.isPet()) { + + writer.put((byte) 1); + + if (mob.getOwner() != null) { + writer.putInt(mob.getOwner().getObjectType().ordinal()); + writer.putInt(mob.getOwner().getObjectUUID()); + } else { + writer.putInt(0); //ownerType + writer.putInt(0); //ownerID + } + } else { + writer.put((byte) 0); + } + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + + if (!mob.isAlive() && !mob.isPet() && !mob.isNecroPet() && !mob.isSiege && !mob.isPlayerGuard) { + writer.putInt(0); + writer.putInt(0); + } + + writer.put((byte) 0); + Guild._serializeForClientMsg(mob.getGuild(),writer); + // writer.putInt(0); + // writer.putInt(0); + if (mob.mobBase != null && mob.mobBase.getObjectUUID() == 100570) { + writer.putInt(2); + writer.putInt(0x00008A2E); + writer.putInt(0x1AB84003); + } else if (mob.isSiege) { + writer.putInt(1); + writer.putInt(74620179); + } else + writer.putInt(0); + + // writer.putInt(1); + // writer.putInt(0); //0xAC13C5E9 - alternate textures + writer.putInt(0); //0xB8400300 + writer.putInt(0); + + //TODO Guard + writer.put((byte) 0); + // writer.put((byte)0); //Is guard.. + + writer.putFloat(mob.healthMax); + writer.putFloat(mob.health.get()); + + //TODO Peace Zone + writer.put((byte) 1); //0=show tags, 1=don't + + //DON't LOAD EFFECTS FOR DEAD MOBS. + + if (!mob.isAlive()) + writer.putInt(0); + else{ + int indexPosition = writer.position(); + writer.putInt(0); //placeholder for item cnt + int total = 0; + + // Logger.info("",""+ mob.getEffects().size()); + for (Effect eff : mob.getEffects().values()) { + if (eff.isStatic()) + continue; + if ( !eff.serializeForLoad(writer)) + continue; + ++total; + } + + writer.putIntAt(total, indexPosition); + } + + // // Effects + writer.put((byte) 0); + } + + private static void serializeRune(Mob mob,ByteBufferWriter writer, int type, int objectType, int runeID) { + writer.putInt(type); + writer.putInt(0); + writer.putInt(runeID); + writer.putInt(objectType); + writer.putInt(mob.currentID); + } + + + public void calculateModifiedStats() { + + float strVal = this.mobBase.getMobBaseStats().getBaseStr(); + float dexVal = this.mobBase.getMobBaseStats().getBaseDex(); + float conVal = 0; // I believe this will desync the Mobs Health if we call it. + float intVal = this.mobBase.getMobBaseStats().getBaseInt(); + float spiVal = this.mobBase.getMobBaseStats().getBaseSpi(); + + // TODO modify for equipment + if (this.bonuses != null) { + // modify for effects + strVal += this.bonuses.getFloat(ModType.Attr, SourceType.Strength); + dexVal += this.bonuses.getFloat(ModType.Attr, SourceType.Dexterity); + conVal += this.bonuses.getFloat(ModType.Attr, SourceType.Constitution); + intVal += this.bonuses.getFloat(ModType.Attr, SourceType.Intelligence); + spiVal += this.bonuses.getFloat(ModType.Attr, SourceType.Spirit); + + // apply dex penalty for armor + // modify percent amounts. DO THIS LAST! + strVal *= (1+this.bonuses.getFloatPercentAll(ModType.Attr, SourceType.Strength)); + dexVal *= (1+this.bonuses.getFloatPercentAll(ModType.Attr, SourceType.Dexterity)); + conVal *= (1+this.bonuses.getFloatPercentAll(ModType.Attr, SourceType.Constitution)); + intVal *= (1+this.bonuses.getFloatPercentAll(ModType.Attr, SourceType.Intelligence)); + spiVal *= (1+this.bonuses.getFloatPercentAll(ModType.Attr, SourceType.Spirit)); + } else { + // apply dex penalty for armor + } + + // Set current stats + this.statStrCurrent = (strVal < 1) ? (short) 1 : (short) strVal; + this.statDexCurrent = (dexVal < 1) ? (short) 1 : (short) dexVal; + this.statConCurrent = (conVal < 1) ? (short) 1 : (short) conVal; + this.statIntCurrent = (intVal < 1) ? (short) 1 : (short) intVal; + this.statSpiCurrent = (spiVal < 1) ? (short) 1 : (short) spiVal; + + } + + @Override + public float getSpeed() { + float bonus = 1; + if (this.bonuses != null) + // get rune and effect bonuses + bonus *= (1 + this.bonuses.getFloatPercentAll(ModType.Speed, SourceType.None)); + + if (this.isPlayerGuard){ + switch (this.mobBase.getLoadID()){ + case 2111: + if (this.isWalk()) + if (this.isCombat()) + return Enum.Guards.HumanArcher.getWalkCombatSpeed() * bonus; + else return Enum.Guards.HumanArcher.getWalkSpeed() * bonus; + else + return Enum.Guards.HumanArcher.getRunSpeed() * bonus; + + case 14103: + if (this.isWalk()) + if (this.isCombat()) + return Enum.Guards.UndeadArcher.getWalkCombatSpeed() * bonus; + else return Enum.Guards.UndeadArcher.getWalkSpeed() * bonus; + else + return Enum.Guards.UndeadArcher.getRunSpeed() * bonus; + } + } + //return combat speeds + if (this.isCombat()){ + if (this.isWalk()){ + if (this.mobBase.getWalkCombat() <= 0) + return MBServerStatics.MOB_SPEED_WALKCOMBAT * bonus; + return this.mobBase.getWalkCombat() * bonus; + }else{ + if (this.mobBase.getRunCombat() <= 0) + return MBServerStatics.MOB_SPEED_RUNCOMBAT * bonus; + return this.mobBase.getRunCombat() * bonus; + } + //not combat return normal speeds + }else{ + if (this.isWalk()){ + if (this.mobBase.getWalk() <= 0) + return MBServerStatics.MOB_SPEED_WALK * bonus; + return this.mobBase.getWalk() * bonus; + }else{ + if (this.mobBase.getRun() <= 0) + return MBServerStatics.MOB_SPEED_RUN * bonus; + return this.mobBase.getRun() * bonus; + } + } + + } + + @Override + public float getPassiveChance(String type, int AttackerLevel, boolean fromCombat) { + //TODO add this later for dodge + return 0f; + } + + /** + * @ Kill this Character + */ + @Override + public void killCharacter(AbstractCharacter attacker) { + + + this.stopMovement(this.getMovementLoc()); + + if (attacker != null){ + + if (attacker.getObjectType() == GameObjectType.PlayerCharacter) { + Group g = GroupManager.getGroup((PlayerCharacter) attacker); + + // Give XP, now handled inside the Experience Object + if (!this.isPet() && !this.isNecroPet() && !this.isSummonedPet() && !this.isPlayerGuard) + Experience.doExperience((PlayerCharacter) attacker, this, g); + } else if (attacker.getObjectType().equals(GameObjectType.Mob)){ + Mob mobAttacker = (Mob)attacker; + + if (mobAttacker.isPet()){ + + PlayerCharacter owner = mobAttacker.getOwner(); + + if (owner != null){ + + if (!this.isPet() && !this.isNecroPet() && !this.isSummonedPet() && !this.isPlayerGuard){ + Group g = GroupManager.getGroup(owner); + + // Give XP, now handled inside the Experience Object + Experience.doExperience(owner, this, g); + } + + } + } + } + } + killCleanup(); + } + + public void updateLocation(){ + + if (!this.isMoving()) + return; + + if (state == STATE.Disabled) + return; + + if ( this.isAlive() == false || this.getBonuses().getBool(ModType.Stunned, SourceType.None) || this.getBonuses().getBool(ModType.CannotMove, SourceType.None)) { + //Target is stunned or rooted. Don't move + + this.stopMovement(this.getMovementLoc()); + + return; + } + + Vector3fImmutable newLoc = this.getMovementLoc(); + + if (newLoc.equals(this.getEndLoc())){ + this.stopMovement(newLoc); + return; + //Next upda + } + + setLoc(newLoc); + //Next update will be end Loc, lets stop him here. + + } + + @Override + public void killCharacter(String reason) { + killCleanup(); + } + + private void killCleanup() { + Dispatch dispatch; + + try { + if (this.isSiege) { + this.deathTime = System.currentTimeMillis(); + this.state = STATE.Dead; + try { + this.clearEffects(); + }catch(Exception e){ + Logger.error( e.getMessage()); + } + this.combatTarget = null; + this.hasLoot = false; + this.playerAgroMap.clear(); + + this.timeToSpawnSiege = System.currentTimeMillis() + 60 * 15 * 1000; + + if (this.isPet()) { + + PlayerCharacter petOwner = this.getOwner(); + + if (petOwner != null){ + this.setOwner(null); + petOwner.setPet(null); + PetMsg petMsg = new PetMsg(5, null); + dispatch = Dispatch.borrow(this.getOwner(), petMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); + } + } + + } else if (this.isPet() || this.isNecroPet()) { + this.state = STATE.Disabled; + + this.combatTarget = null; + this.hasLoot = false; + + if (this.parentZone != null) + this.parentZone.zoneMobSet.remove(this); + + try { + this.clearEffects(); + }catch(Exception e){ + Logger.error( e.getMessage()); + } + this.playerAgroMap.clear(); + WorldGrid.RemoveWorldObject(this); + + DbManager.removeFromCache(this); + + // YEAH BONUS CODE! THANKS UNNAMED ASSHOLE! + //WorldServer.removeObject(this); + //WorldGrid.INSTANCE.removeWorldObject(this); + //owner.getPet().disableIntelligence(); + + PlayerCharacter petOwner = this.getOwner(); + + if (petOwner != null){ + this.setOwner(null); + petOwner.setPet(null); + PetMsg petMsg = new PetMsg(5, null); + dispatch = Dispatch.borrow(petOwner, petMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); + } + } else { + + //cleanup effects + + this.deathTime = System.currentTimeMillis(); + this.state = STATE.Dead; + + playerAgroMap.clear(); + + if (!this.isPlayerGuard){ + + ArrayList alml = LootTable.getMobLootDeath(this, this.getLevel(), this.getLootTable()); + + for (MobLoot ml : alml) { + this.charItemManager.addItemToInventory(ml); + } + + if (this.equip != null){ + + for (MobEquipment me: equip.values()){ + if (me.getDropChance() == 0) + continue; + + float chance = ThreadLocalRandom.current().nextFloat(); + + if (chance <= me.getDropChance()){ + MobLoot ml = new MobLoot(this, me.getItemBase(), false); + ml.setFidelityEquipID(me.getObjectUUID()); + this.charItemManager.addItemToInventory(ml); + } + } + } + } + + } + try { + this.clearEffects(); + }catch(Exception e){ + Logger.error( e.getMessage()); + } + + this.combat = false; + this.walkMode = true; + this.combatTarget = null; + + this.hasLoot = (this.charItemManager.getInventoryCount() > 0) ? true : false; + + } catch (Exception e) { + Logger.error(e); + } + } + + public void respawn() { + //Commenting out Mob ID rotation. + + this.despawned = false; + this.playerAgroMap.clear(); + this.setCombatTarget(null); + this.setHealth(this.healthMax); + this.stamina.set(this.staminaMax); + this.mana.set(this.manaMax); + this.combat = false; + this.walkMode = true; + this.combatTarget = null; + this.isAlive.set(true); + + if (!this.isSiege) + this.lastBindLoc = Mob.GetSpawnRadiusLocation(this); + else + this.lastBindLoc = this.bindLoc; + this.bindLoc = this.lastBindLoc; + this.setLoc(this.lastBindLoc); + this.stopMovement(this.lastBindLoc); + this.initializeStaticEffects(); + this.recalculateStats(); + + this.setHealth(this.healthMax); + + if (!this.isSiege && !this.isPlayerGuard && contract == null) + loadInventory(); + + // LoadJob.getInstance(); + // LoadJob.forceLoad(this); + } + + public void despawn() { + + this.despawned = true; + + //WorldServer.removeObject(this); + WorldGrid.RemoveWorldObject(this); + this.charItemManager.clearInventory(); + this.despawnTime = System.currentTimeMillis(); + // this.setLoc(Vector3fImmutable.ZERO); + } + + //Sets the relative position to a parent zone + public void setRelPos(Zone zone, float locX, float locY, float locZ) { + + //update mob zone map + + if (this.parentZone != null) + this.parentZone.zoneMobSet.remove(this); + + zone.zoneMobSet.add(this); + + this.statLat = locX; + this.statAlt = locY; + this.statLon = locZ; + this.parentZone = zone; + this.setBindLoc(new Vector3fImmutable(this.statLat + zone.absX, this.statAlt + zone.absY, this.statLon + zone.absZ)); + } + + public boolean canRespawn(){ + return System.currentTimeMillis() > this.despawnTime + 4000; + } + + @Override + public boolean canBeLooted() { + return !this.isAlive(); + } + + public int getTypeMasks() { + + if (this.mobBase == null) + return 0; + + return this.mobBase.getTypeMasks(); + } + + /** + * Clears and sets the inventory of the Mob. Must be called every time the + * mob is spawned or respawned. + */ + private void loadInventory() { + + if (!MBServerStatics.ENABLE_MOB_LOOT) + return; + + this.charItemManager.clearInventory(); + this.charItemManager.clearEquip(); + + if (isPlayerGuard) + return; + + int gold = Mob.randomGoldAmount(this); + + if (gold > 0 && this.getLootTable() != 0) { + addGoldToInventory(gold); + } + + //add random loot to mob + ArrayList alml = LootTable.getMobLoot(this, this.getLevel(), this.getLootTable(), false); //add hotzone check in later + + for (MobLoot ml : alml) { + this.charItemManager.addItemToInventory(ml); + } + + + + //add special loot to mob + } + + private int getLootTable() { + + if (this.mobBase == null) + return 0; + + return this.mobBase.getLootTable(); + } + + /** + * Sets the quantity of gold in the inventory. Calling this multiple times + * will overwrite the gold amount. + * + * @param quantity Quantity of gold. + */ + private void addGoldToInventory(int quantity) { + MobLoot gold = new MobLoot(this, quantity); + this.charItemManager.addItemToInventory(gold); + } + + /** + * Generate a random quantity of gold for this mob. + * + * @return Quantity of gold + */ + public static int randomGoldAmount( Mob mob) { + + // percentage chance to drop gold + + //R8 mobs have 100% gold drop. + if (mob.getLevel() < 80) + if ((ThreadLocalRandom.current().nextDouble() * 100d) > MBServerStatics.GOLD_DROP_PERCENTAGE_CHANCE) + return 0; + + + int level = (int) mob.getLevel(); + level = (level < 0) ? 0 : level; + level = (level > 50) ? 50 : level; + + double minGold; + double maxGold; + + if (mob.mobBase != null) { + minGold = mob.mobBase.getMinGold(); + maxGold = mob.mobBase.getMaxGold(); + } else { + minGold = MBServerStatics.GOLD_DROP_MINIMUM_PER_MOB_LEVEL[level]; + maxGold = MBServerStatics.GOLD_DROP_MAXIMUM_PER_MOB_LEVEL[level]; + } + + double gold = (ThreadLocalRandom.current().nextDouble() * (maxGold - minGold) + minGold); + + + //server specific gold multiplier + double goldMod = MBServerStatics.GOLD_RATE_MOD; + gold *= goldMod; + + //modify for hotzone + + if (ZoneManager.inHotZone(mob.getLoc())) + gold *= MBServerStatics.HOT_GOLD_RATE_MOD; + + gold *= MBServerStatics.GOLD_RATE_MOD; + + return (int) gold; + } + + public static Mob createMob(int loadID, Vector3fImmutable spawn, Guild guild, boolean isMob, Zone parent,Building building, int contractID) { + + Mob mobWithoutID = new Mob("", "", (short) 0, (short) 0, (short) 0, (short) 0, + (short) 0, (short) 1, 0, false, false, false, spawn, spawn, Vector3fImmutable.ZERO, + (short) 1, (short) 1, (short) 1, guild, (byte) 0, loadID, isMob, parent,building,contractID); + + if (parent != null) { + mobWithoutID.setRelPos(parent, spawn.x - parent.absX, spawn.y - parent.absY, spawn.z - parent.absZ); + } + + + if (mobWithoutID.mobBase == null) { + return null; + } + Mob mob; + try { + mob = DbManager.MobQueries.ADD_MOB(mobWithoutID, isMob); + mob.setObjectTypeMask(MBServerStatics.MASK_MOB | mob.getTypeMasks()); + mob.setMob(); + mob.setParentZone(parent); + } catch (Exception e) { + Logger.error("SQLException:" + e.getMessage()); + mob = null; + } + return mob; + } + + public static Mob createPet( int loadID, Guild guild, Zone parent, PlayerCharacter owner, short level) { + MobBase mobBase = MobBase.getMobBase(loadID); + Mob mob = null; + if (mobBase == null || owner == null) { + return null; + } + createLock.writeLock().lock(); + level += 20; + try { + mob = new Mob( mobBase, guild, parent, level, owner, 0); + if (mob.mobBase == null) { + return null; + } + mob.runAfterLoad(); + + Vector3fImmutable loc = owner.getLoc(); + if (parent != null) { + mob.setRelPos(parent, loc.x - parent.absX, loc.y - parent.absY, loc.z - parent.absZ); + } + DbManager.addToCache(mob); + mob.setPet(owner, true); + mob.setWalkMode(false); + mob.state = STATE.Awake; + + } catch (Exception e) { + Logger.error(e); + } finally { + createLock.writeLock().unlock(); + } + + return mob; + } + + + + public static int nextStaticID() { + int id = Mob.staticID; + Mob.staticID++; + return id; + } + + /* + * Database + */ + + + public static Mob getMob(int id) { + + if (id == 0) + return null; + + Mob mob = (Mob) DbManager.getFromCache(GameObjectType.Mob, id); + if (mob != null) + return mob; + return DbManager.MobQueries.GET_MOB(id); + } + + public static Mob getFromCache(int id) { + + + return (Mob) DbManager.getFromCache(GameObjectType.Mob, id); + } + + public static Mob getFromCacheDBID(int id) { + if (Mob.mobMapByDBID.containsKey(id)) { + return Mob.mobMapByDBID.get(id); + } + return null; + } + + @Override + public void updateDatabase() { + // DbManager.MobQueries.updateDatabase(this); + } + + public int removeFromDatabase() { + return DbManager.MobQueries.DELETE_MOB(this); + } + + public void refresh() { + if (this.isAlive()) + WorldGrid.updateObject(this); + } + + public void recalculateStats() { + + try { + calculateModifiedStats(); + } catch (Exception e) { + Logger.error(e.getMessage()); + } + + try { + calculateAtrDefenseDamage(); + } catch (Exception e) { + Logger.error( this.getMobBaseID() + " /" + e.getMessage()); + } + try { + calculateMaxHealthManaStamina(); + } catch (Exception e) { + Logger.error( e.getMessage()); + } + + Resists.calculateResists(this); + } + + public void calculateMaxHealthManaStamina() { + float h = 1f; + float m = 0f; + float s = 0f; + + h = this.mobBase.getHealthMax(); + m = this.statSpiCurrent; + s = this.statConCurrent; + + // Apply any bonuses from runes and effects + if (this.bonuses != null) { + h += this.bonuses.getFloat(ModType.HealthFull, SourceType.None); + m += this.bonuses.getFloat(ModType.ManaFull,SourceType.None); + s += this.bonuses.getFloat(ModType.StaminaFull, SourceType.None); + + //apply effects percent modifiers. DO THIS LAST! + h *= (1 + this.bonuses.getFloatPercentAll(ModType.HealthFull,SourceType.None)); + m *= (1 + this.bonuses.getFloatPercentAll(ModType.ManaFull,SourceType.None)); + s *= (1 + this.bonuses.getFloatPercentAll(ModType.StaminaFull,SourceType.None)); + } + + // Set max health, mana and stamina + if (h > 0) + this.healthMax = h; + else + this.healthMax = 1; + + if (m > -1) + this.manaMax = m; + else + this.manaMax = 0; + + if (s > -1) + this.staminaMax = s; + else + this.staminaMax = 0; + + // Update health, mana and stamina if needed + if (this.getHealth() > this.healthMax) + this.setHealth(this.healthMax); + + if (this.mana.get() > this.manaMax) + this.mana.set(this.manaMax); + + if (this.stamina.get() > this.staminaMax) + this.stamina.set(staminaMax); + + } + + public void calculateAtrDefenseDamage() { + + if (this.charItemManager == null || this.equip == null) { + Logger.error("Player " + currentID + " missing skills or equipment"); + defaultAtrAndDamage(true); + defaultAtrAndDamage(false); + this.defenseRating = 0; + return; + } + + try { + calculateAtrDamageForWeapon( + this.equip.get(MBServerStatics.SLOT_MAINHAND), true, this.equip.get(MBServerStatics.SLOT_OFFHAND)); + } catch (Exception e) { + + this.atrHandOne = (short) this.mobBase.getAttackRating(); + this.minDamageHandOne = (short) this.mobBase.getMinDmg(); + this.maxDamageHandOne = (short) this.mobBase.getMaxDmg(); + this.rangeHandOne = 6.5f; + this.speedHandOne = 20; + Logger.info("Mobbase ID " + this.getMobBaseID() + " returned an error. setting to default ATR and Damage." + e.getMessage()); + } + + try { + calculateAtrDamageForWeapon(this.equip.get(MBServerStatics.SLOT_OFFHAND), false, this.equip.get(MBServerStatics.SLOT_MAINHAND)); + + } catch (Exception e) { + + this.atrHandTwo = (short) this.mobBase.getAttackRating(); + this.minDamageHandTwo = (short) this.mobBase.getMinDmg(); + this.maxDamageHandTwo = (short) this.mobBase.getMaxDmg(); + this.rangeHandTwo = 6.5f; + this.speedHandTwo = 20; + Logger.info( "Mobbase ID " + this.getMobBaseID() + " returned an error. setting to default ATR and Damage." + e.getMessage()); + } + + try { + float defense = this.mobBase.getDefenseRating(); + defense += getShieldDefense(equip.get(MBServerStatics.SLOT_OFFHAND)); + defense += getArmorDefense(equip.get(MBServerStatics.SLOT_HELMET)); + defense += getArmorDefense(equip.get(MBServerStatics.SLOT_CHEST)); + defense += getArmorDefense(equip.get(MBServerStatics.SLOT_ARMS)); + defense += getArmorDefense(equip.get(MBServerStatics.SLOT_GLOVES)); + defense += getArmorDefense(equip.get(MBServerStatics.SLOT_LEGGINGS)); + defense += getArmorDefense(equip.get(MBServerStatics.SLOT_FEET)); + defense += getWeaponDefense(equip); + + if (this.bonuses != null) { + // add any bonuses + defense += (short) this.bonuses.getFloat(ModType.DCV, SourceType.None); + + // Finally multiply any percent modifiers. DO THIS LAST! + float pos_Bonus = 1 + this.bonuses.getFloatPercentPositive(ModType.DCV, SourceType.None); + + + + defense = (short) (defense * pos_Bonus); + + //Lucky rune applies next + + float neg_Bonus = this.bonuses.getFloatPercentNegative(ModType.DCV, SourceType.None); + defense = (short) (defense *(1 + neg_Bonus)); + + + + } else { + // TODO add error log here + Logger.error( "Error: missing bonuses"); + } + + defense = (defense < 1) ? 1 : defense; + this.defenseRating = (short) (defense + 0.5f); + } catch (Exception e) { + Logger.info("Mobbase ID " + this.getMobBaseID() + " returned an error. Setting to Default Defense." + e.getMessage()); + this.defenseRating = (short) this.mobBase.getDefense(); + } + // calculate defense for equipment + } + + private float getWeaponDefense(HashMap equipped) { + MobEquipment weapon = equipped.get(MBServerStatics.SLOT_MAINHAND); + ItemBase wb = null; + CharacterSkill skill, mastery; + float val = 0; + boolean unarmed = false; + if (weapon == null) { + weapon = equipped.get(MBServerStatics.SLOT_OFFHAND); + + if (weapon == null) + unarmed = true; + else + wb = weapon.getItemBase(); + + } else + wb = weapon.getItemBase(); + + if (wb == null) + unarmed = true; + + if (unarmed) { + skill = null; + mastery = null; + } else { + skill = this.skills.get(wb.getSkillRequired()); + mastery = this.skills.get(wb.getMastery()); + } + + if (skill != null) + val += (int) skill.getModifiedAmount() / 2f; + + if (mastery != null) + val += (int) mastery.getModifiedAmount() / 2f; + + return val; + } + + private float getShieldDefense(MobEquipment shield) { + + if (shield == null) + return 0; + + ItemBase ab = shield.getItemBase(); + + if (ab == null || !ab.isShield()) + return 0; + + CharacterSkill blockSkill = this.skills.get("Block"); + float skillMod; + + if (blockSkill == null) { + skillMod = CharacterSkill.getQuickMastery(this, "Block"); + + if (skillMod == 0f) + return 0; + + } else + skillMod = blockSkill.getModifiedAmount(); + + // // Only fighters and healers can block + // if (this.baseClass != null && (this.baseClass.getUUID() == 2500 || this.baseClass.getUUID() == 2501)) + // this.bonuses.setBool("Block", true); + + float def = ab.getDefense(); + //apply item defense bonuses + // float val = ((float)ab.getDefense()) * (1 + (skillMod / 100)); + return (def * (1 + ((int) skillMod / 100f))); + } + + private float getArmorDefense(MobEquipment armor) { + + if (armor == null) + return 0; + + ItemBase ib = armor.getItemBase(); + + if (ib == null) + return 0; + + if (!ib.getType().equals(ItemType.ARMOR)) + return 0; + + if (ib.getSkillRequired().isEmpty()) + return ib.getDefense(); + + CharacterSkill armorSkill = this.skills.get(ib.getSkillRequired()); + + if (armorSkill == null) + return ib.getDefense(); + + float def = ib.getDefense(); + + //apply item defense bonuses + + return (def * (1 + ((int) armorSkill.getModifiedAmount() / 50f))); + } + + private void calculateAtrDamageForWeapon(MobEquipment weapon, boolean mainHand, MobEquipment otherHand) { + + int baseStrength = 0; + + float skillPercentage, masteryPercentage; + float mastDam; + + // make sure weapon exists + boolean noWeapon = false; + ItemBase wb = null; + + if (weapon == null) + noWeapon = true; + else { + + ItemBase ib = weapon.getItemBase(); + + if (ib == null) + noWeapon = true; + else { + + if (ib.getType().equals(ItemType.WEAPON) == false) { + defaultAtrAndDamage(mainHand); + return; + } else + wb = ib; + } + } + float min, max; + float speed = 20f; + boolean strBased = false; + + // get skill percentages and min and max damage for weapons + + if (noWeapon) { + + if (mainHand) + this.rangeHandOne = this.mobBase.getAttackRange(); + else + this.rangeHandTwo = -1; // set to do not attack + + skillPercentage = getModifiedAmount(this.skills.get("Unarmed Combat")); + masteryPercentage = getModifiedAmount(this.skills.get("Unarmed Combat Mastery")); + + if (masteryPercentage == 0f) + mastDam = CharacterSkill.getQuickMastery(this, "Unarmed Combat Mastery"); + else + mastDam = masteryPercentage; + + // TODO Correct these + min = this.mobBase.getMinDmg(); + max = this.mobBase.getMaxDmg(); + } else { + + if (mainHand) + this.rangeHandOne = weapon.getItemBase().getRange() * (1 + (baseStrength / 600)); + else + this.rangeHandTwo = weapon.getItemBase().getRange() * (1 + (baseStrength / 600)); + + skillPercentage = getModifiedAmount(this.skills.get(wb.getSkillRequired())); + masteryPercentage = getModifiedAmount(this.skills.get(wb.getMastery())); + + if (masteryPercentage == 0f) + mastDam = 0f; + else + mastDam = masteryPercentage; + + min = (float) wb.getMinDamage(); + max = (float) wb.getMaxDamage(); + strBased = wb.isStrBased(); + } + + // calculate atr + float atr = this.mobBase.getAttackRating(); + + atr += ((int) skillPercentage * 4f); //<-round down skill% - + atr += ((int) masteryPercentage * 3f); + + if (this.statStrCurrent > this.statDexCurrent) + atr += statStrCurrent / 2; + else + atr += statDexCurrent / 2; + + // add in any bonuses to atr + if (this.bonuses != null) { + // Add any base bonuses + atr += this.bonuses.getFloat(ModType.OCV, SourceType.None); + + // Finally use any multipliers. DO THIS LAST! + float pos_Bonus = 1 + this.bonuses.getFloatPercentPositive(ModType.OCV, SourceType.None); + + + atr *= pos_Bonus; + + // next precise + +// atr *= (1 + ((float) this.bonuses.getShort("rune.Attack") / 100)); + + //and negative percent modifiers + //TODO DO DEBUFFS AFTER?? wILL TEst when finished + float neg_Bonus = this.bonuses.getFloatPercentNegative(ModType.OCV, SourceType.None); + + + + atr *= (1 + neg_Bonus); + } + + atr = (atr < 1) ? 1 : atr; + + // set atr + if (mainHand) + this.atrHandOne = (short) (atr + 0.5f); + else + this.atrHandTwo = (short) (atr + 0.5f); + + //calculate speed + + if (wb != null) + speed = wb.getSpeed(); + else + speed = 20f; //unarmed attack speed + + if (this.bonuses != null && this.bonuses.getFloat(ModType.AttackDelay, SourceType.None) != 0f) //add effects speed bonus + speed *= (1 + this.bonuses.getFloatPercentAll(ModType.AttackDelay, SourceType.None)); + + if (speed < 10) + speed = 10; + + //add min/max damage bonuses for weapon **REMOVED + + //if duel wielding, cut damage by 30% + // calculate damage + float minDamage; + float maxDamage; + float pri = (strBased) ? (float) this.statStrCurrent : (float) this.statDexCurrent; + float sec = (strBased) ? (float) this.statDexCurrent : (float) this.statStrCurrent; + + minDamage = (float) (min * ((0.0315f * Math.pow(pri, 0.75f)) + (0.042f * Math.pow(sec, 0.75f)) + (0.01f * ((int) skillPercentage + (int) mastDam)))); + maxDamage = (float) (max * ((0.0785f * Math.pow(pri, 0.75f)) + (0.016f * Math.pow(sec, 0.75f)) + (0.0075f * ((int) skillPercentage + (int) mastDam)))); + + minDamage = (float) ((int) (minDamage + 0.5f)); //round to nearest decimal + maxDamage = (float) ((int) (maxDamage + 0.5f)); //round to nearest decimal + // Logger.info("MobCalculateDamage", "Mob with ID "+ this.getObjectUUID() + " and MOBBASE with ID " + this.getMobBaseID() + " returned " + minDamage + "/" + maxDamage + " modified Damage."); + + //add Base damage last. + float minDamageMod = this.mobBase.getDamageMin(); + float maxDamageMod = this.mobBase.getDamageMax(); + + minDamage += minDamageMod; + maxDamage += maxDamageMod; + + // add in any bonuses to damage + if (this.bonuses != null) { + // Add any base bonuses + minDamage += this.bonuses.getFloat(ModType.MinDamage, SourceType.None); + maxDamage += this.bonuses.getFloat(ModType.MaxDamage, SourceType.None); + + // Finally use any multipliers. DO THIS LAST! + minDamage *= (1 + this.bonuses.getFloatPercentAll(ModType.MinDamage, SourceType.None)); + maxDamage *= (1 + this.bonuses.getFloatPercentAll(ModType.MaxDamage, SourceType.None)); + } + + // set damages + if (mainHand) { + this.minDamageHandOne = (short) minDamage; + this.maxDamageHandOne = (short) maxDamage; + this.speedHandOne = 30; + } else { + this.minDamageHandTwo = (short) minDamage; + this.maxDamageHandTwo = (short) maxDamage; + this.speedHandTwo = 30; + } + } + + private static float getModifiedAmount(CharacterSkill skill) { + + if (skill == null) + return 0f; + + return skill.getModifiedAmount(); + } + + private void defaultAtrAndDamage(boolean mainHand) { + + if (mainHand) { + this.atrHandOne = 0; + this.minDamageHandOne = 0; + this.maxDamageHandOne = 0; + this.rangeHandOne = -1; + this.speedHandOne = 20; + } else { + this.atrHandTwo = 0; + this.minDamageHandTwo = 0; + this.maxDamageHandTwo = 0; + this.rangeHandTwo = -1; + this.speedHandTwo = 20; + } + } + + public static int getBuildingSlot(Mob mob){ + int slot = -1; + + if (mob.building == null) + return -1; + + + + BuildingModelBase buildingModel = BuildingModelBase.getModelBase(mob.building.getMeshUUID()); + + if (buildingModel == null) + return -1; + + + if (mob.building.getHirelings().containsKey(mob)) + slot = (mob.building.getHirelings().get(mob)); + + + if (buildingModel.getNPCLocation(slot) == null) + return -1; + + + return slot; + } + + public void setInBuildingLoc(Building inBuilding, AbstractCharacter ac) { + + Mob mob = null; + + NPC npc = null; + + + if (ac.getObjectType().equals(GameObjectType.Mob)) + mob = (Mob)ac; + + else if (ac.getObjectType().equals(GameObjectType.NPC)) + npc = (NPC)ac; + + // *** Refactor : Need to take a look at this, make sure + // npc's are loaded in correct spots. + + BuildingModelBase buildingModel = BuildingModelBase.getModelBase(inBuilding.getMeshUUID()); + + Vector3fImmutable slotLocation = Vector3fImmutable.ZERO; + + if (buildingModel != null){ + + + int putSlot = -1; + BuildingLocation buildingLocation = null; + + //-1 slot means no slot available in building. + + if (npc != null){ + if (npc.getSiegeMinionMap().containsKey(this)) + putSlot = npc.getSiegeMinionMap().get(this); + }else if (mob != null) + if (mob.getSiegeMinionMap().containsKey(this)) + putSlot = mob.getSiegeMinionMap().get(this); + + int count = 0; + + for (BuildingLocation slotLoc: buildingModel.getLocations()) + if (slotLoc.getType() == 6) + count++; + + + buildingLocation = buildingModel.getSlotLocation((count) - putSlot); + + if (buildingLocation != null){ + slotLocation = buildingLocation.getLoc(); + } + + } + + this.inBuildingLoc = slotLocation; + + } + + public Vector3fImmutable getInBuildingLoc() { + return inBuildingLoc; + } + + public ItemBase getWeaponItemBase(boolean mainHand) { + + if (this.equipmentSetID != 0){ + + if (equip != null) { + MobEquipment me = null; + + if (mainHand) + me = equip.get(1); //mainHand + else + me = equip.get(2); //offHand + + if (me != null) { + + ItemBase ib = me.getItemBase(); + + if (ib != null) + return ib; + + } + } + } + MobBase mb = this.mobBase; + + if (mb != null) { + + if (equip != null) { + + MobEquipment me = null; + + if (mainHand) + me = equip.get(1); //mainHand + else + me = equip.get(2); //offHand + + if (me != null) { + + ItemBase ib = me.getItemBase(); + + if (ib != null) + return ib; + } + } + } + return null; + } + + @Override + public void runAfterLoad() { + + try{ + if (this.equipmentSetID != 0) + this.equip = MobBase.loadEquipmentSet(this.equipmentSetID); + else + this.equip = new HashMap<>(); + + } catch(Exception e){ + Logger.error( e.getMessage()); + } + + if (this.equip == null) { + Logger.error("Null equipset returned for uuid " + currentID); + this.equip = new HashMap<>(0); + } + + try{ + this.initializeStaticEffects(); + + try { + this.initializeSkills(); + } catch (Exception e) { + Logger.error( e.getMessage()); + } + + recalculateStats(); + this.setHealth(this.healthMax); + + // Set bounds for this mobile + Bounds mobBounds = Bounds.borrow(); + mobBounds.setBounds(this.getLoc()); + this.setBounds(mobBounds); + + } catch (Exception e){ + Logger.error(e.getMessage()); + } + } + + @Override + protected ConcurrentHashMap initializePowers() { + return new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + } + + public boolean canSee(PlayerCharacter target) { + return this.mobBase.getSeeInvis() >= target.getHidden(); + } + + public int getBuildingID() { + return buildingID; + } + + public void setBuildingID(int buildingID) { + this.buildingID = buildingID; + } + + public boolean isSiege() { + return isSiege; + } + + public void setSiege(boolean isSiege) { + this.isSiege = isSiege; + } + + public long getTimeToSpawnSiege() { + return timeToSpawnSiege; + } + + public void setTimeToSpawnSiege(long timeToSpawnSiege) { + this.timeToSpawnSiege = timeToSpawnSiege; + } + + public AbstractCharacter getNpcOwner() { + return npcOwner; + } + + public void setNpcOwner(AbstractCharacter npcOwner) { + this.npcOwner = npcOwner; + } + + public boolean isNecroPet() { + return this.mobBase.isNecroPet(); + } + + public static void HandleAssistedAggro(PlayerCharacter source, PlayerCharacter target) { + + HashSet mobsInRange = WorldGrid.getObjectsInRangePartial(source, MBServerStatics.AI_DROP_AGGRO_RANGE, MBServerStatics.MASK_MOB); + + for (AbstractWorldObject awo : mobsInRange) { + Mob mob = (Mob) awo; + + //Mob is not attacking anyone, skip. + if (mob.getCombatTarget() == null) + continue; + + //Mob not attacking target's target, let's not be failmu and skip this target. + if (mob.getCombatTarget() != target) + continue; + + //target is mob's combat target, LETS GO. + if (source.getHateValue() > target.getHateValue()) { + mob.setCombatTarget(source); + MobileFSM.setAggro(mob, source.getObjectUUID()); + } + } + } + + public void handleDirectAggro(AbstractCharacter ac) { + + if (ac.getObjectType().equals(GameObjectType.PlayerCharacter) == false) + return; + + PlayerCharacter player = (PlayerCharacter)ac; + + if (this.getCombatTarget() == null) { + MobileFSM.setAggro(this, player.getObjectUUID()); + return; + } + + if (player.getObjectUUID() == this.getCombatTarget().getObjectUUID()) + return; + + if (this.getCombatTarget().getObjectType() == GameObjectType.PlayerCharacter) { + + if (ac.getHateValue() > ((PlayerCharacter) this.getCombatTarget()).getHateValue()) { + this.setCombatTarget(player); + MobileFSM.setAggro(this, player.getObjectUUID()); + } + } + } + + public boolean remove(Building building) { + + // Remove npc from it's building + this.state = STATE.Disabled; + + try { + this.clearEffects(); + }catch(Exception e){ + Logger.error(e.getMessage()); + } + + if (this.parentZone != null) + this.parentZone.zoneMobSet.remove(this); + + if (building != null) { + building.getHirelings().remove(this); + this.removeMinions(); + } + + // Delete npc from database + + if (DbManager.MobQueries.DELETE_MOB(this) == 0) + return false; + + // Remove npc from the simulation + + this.removeFromCache(); + DbManager.removeFromCache(this); + WorldGrid.RemoveWorldObject(this); + WorldGrid.removeObject(this); + return true; + } + + public void removeMinions() { + + for (Mob toRemove : this.siegeMinionMap.keySet()) { + + toRemove.state = STATE.Disabled; + + if (this.isMoving()){ + + this.stopMovement(this.getLoc()); + this.state = STATE.Disabled; + + if (toRemove.parentZone != null) + toRemove.parentZone.zoneMobSet.remove(toRemove); + } + + try { + toRemove.clearEffects(); + } catch(Exception e){ + Logger.error(e.getMessage()); + } + + if (toRemove.parentZone != null) + toRemove.parentZone.zoneMobSet.remove(toRemove); + + WorldGrid.RemoveWorldObject(toRemove); + WorldGrid.removeObject(toRemove); + DbManager.removeFromCache(toRemove); + + PlayerCharacter petOwner = toRemove.getOwner(); + + if (petOwner != null) { + + petOwner.setPet(null); + toRemove.setOwner(null); + + PetMsg petMsg = new PetMsg(5, null); + Dispatch dispatch = Dispatch.borrow(petOwner, petMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); + } + } + } + + public static void submitUpgradeJob(Mob mob) { + + JobContainer jc; + + if (mob.getUpgradeDateTime() == null) { + Logger.error("Failed to get Upgrade Date"); + return; + } + + // Submit upgrade job for future date or current instant + + if (mob.getUpgradeDateTime().isAfter(DateTime.now())) + jc = JobScheduler.getInstance().scheduleJob(new UpgradeNPCJob(mob), + mob.getUpgradeDateTime().getMillis()); + else + JobScheduler.getInstance().scheduleJob(new UpgradeNPCJob(mob), 0); + + } + + public void setRank(int newRank) { + + DbManager.MobQueries.SET_PROPERTY(this, "mob_level", newRank); + this.level = (short) newRank; + + } + + public static int getUpgradeTime(Mob mob) { + + if (mob.getRank() < 7) + return (mob.getRank() * 8); + + return 0; + } + + public static int getUpgradeCost(Mob mob) { + + int upgradeCost; + + upgradeCost = Integer.MAX_VALUE; + + if (mob.getRank() < 7) + return (mob.getRank() * 100650) + 21450; + + return upgradeCost; + } + + public boolean isRanking() { + + return this.upgradeDateTime != null; + } + + public boolean isNoAggro() { + return noAggro; + } + + public void setNoAggro(boolean noAggro) { + this.noAggro = noAggro; + } + + public STATE getState() { + return state; + } + + public void setState(STATE state) { + this.state = state; + } + + public int getAggroTargetID() { + return aggroTargetID; + } + + public void setAggroTargetID(int aggroTargetID) { + this.aggroTargetID = aggroTargetID; + } + + public boolean isWalkingHome() { + return walkingHome; + } + + public void setWalkingHome(boolean walkingHome) { + this.walkingHome = walkingHome; + } + + public long getLastAttackTime() { + return lastAttackTime; + } + + public void setLastAttackTime(long lastAttackTime) { + this.lastAttackTime = lastAttackTime; + } + + public ConcurrentHashMap getPlayerAgroMap() { + return playerAgroMap; + } + + public long getDeathTime() { + return deathTime; + } + + public boolean isHasLoot() { + return hasLoot; + } + public void setDeathTime(long deathTime) { + this.deathTime = deathTime; + } + + public DeferredPowerJob getWeaponPower() { + return weaponPower; + } + + public void setWeaponPower(DeferredPowerJob weaponPower) { + this.weaponPower = weaponPower; + } + public ConcurrentHashMap getSiegeMinionMap() { + return siegeMinionMap; + } + + public Building getBuilding() { + return this.building; + } + + public DateTime getUpgradeDateTime() { + + lock.readLock().lock(); + + try { + return upgradeDateTime; + } finally { + lock.readLock().unlock(); + } + } + + public synchronized Mob createGuardMob(int loadID, Guild guild, Zone parent, Vector3fImmutable loc, short level, String pirateName) { + + MobBase minionMobBase; + Mob mob; + int maxSlots = 1; + + switch (this.getRank()){ + case 1: + case 2: + maxSlots = 1; + break; + case 3: + maxSlots = 2; + break; + case 4: + case 5: + maxSlots = 3; + break; + case 6: + maxSlots = 4; + break; + case 7: + maxSlots = 5; + break; + default: + maxSlots = 1; + + } + + if (siegeMinionMap.size() == maxSlots) + return null; + + minionMobBase = this.mobBase; + + if (minionMobBase == null) + return null; + + mob = new Mob(minionMobBase, guild, parent, level,new Vector3fImmutable(1,1,1), 0,true); + + mob.despawned = true; + + mob.setLevel(level); + //grab equipment and name from minionbase. + if (this.contract != null){ + MinionType minionType = MinionType.ContractToMinionMap.get(this.contract.getContractID()); + if (minionType != null){ + mob.equipmentSetID = minionType.getEquipSetID(); + String rank = ""; + + if (this.getRank() < 3) + rank = MBServerStatics.JUNIOR; + else if (this.getRank() < 6) + rank = ""; + else if (this.getRank() == 6) + rank = MBServerStatics.VETERAN; + else + rank = MBServerStatics.ELITE; + + if (rank.isEmpty()) + mob.nameOverride = pirateName + " " + minionType.getRace() + " " + minionType.getName(); + else + mob.nameOverride = pirateName + " " + minionType.getRace() + " " + rank + " " + minionType.getName(); + } + } + + + + if (parent != null) + mob.setRelPos(parent, loc.x - parent.absX, loc.y - parent.absY, loc.z - parent.absZ); + + mob.setObjectTypeMask(MBServerStatics.MASK_MOB | mob.getTypeMasks()); + + // mob.setMob(); + mob.isPlayerGuard = true; + mob.setParentZone(parent); + DbManager.addToCache(mob); + mob.runAfterLoad(); + + + + RuneBase guardRune = RuneBase.getRuneBase(252621); + + for (MobBaseEffects mbe : guardRune.getEffectsList()) { + + EffectsBase eb = PowersManager.getEffectByToken(mbe.getToken()); + + if (eb == null) { + Logger.info( "EffectsBase Null for Token " + mbe.getToken()); + continue; + } + + //check to upgrade effects if needed. + if (mob.effects.containsKey(Integer.toString(eb.getUUID()))) { + if (mbe.getReqLvl() > (int) mob.level) { + continue; + } + + Effect eff = mob.effects.get(Integer.toString(eb.getUUID())); + + if (eff == null) + continue; + + //Current effect is a higher rank, dont apply. + if (eff.getTrains() > mbe.getRank()) + continue; + + //new effect is of a higher rank. remove old effect and apply new one. + eff.cancelJob(); + mob.addEffectNoTimer(Integer.toString(eb.getUUID()), eb, mbe.getRank(), true); + } else { + + if (mbe.getReqLvl() > (int) mob.level) + continue; + + mob.addEffectNoTimer(Integer.toString(eb.getUUID()), eb, mbe.getRank(), true); + } + } + + int slot = 0; + slot += siegeMinionMap.size() + 1; + + siegeMinionMap.put(mob, slot); + mob.setInBuildingLoc(this.building, this); + mob.setBindLoc(loc.add(mob.inBuildingLoc)); + mob.deathTime = System.currentTimeMillis(); + mob.spawnTime = 900; + mob.npcOwner = this; + mob.state = STATE.Respawn; + + return mob; + } + + public static void setUpgradeDateTime(Mob mob,DateTime upgradeDateTime) { + + if (!DbManager.MobQueries.updateUpgradeTime(mob, upgradeDateTime)){ + Logger.error("Failed to set upgradeTime for building " + mob.currentID); + return; + } + mob.upgradeDateTime = upgradeDateTime; + } + + public Contract getContract() { + return contract; + } + + public void setContract(Contract contract) { + this.contract = contract; + } + + public boolean isPlayerGuard() { + return isPlayerGuard; + } + + public void setPlayerGuard(boolean isPlayerGuard) { + this.isPlayerGuard = isPlayerGuard; + } + + public int getPatrolPointIndex() { + return patrolPointIndex; + } + + public void setPatrolPointIndex(int patrolPointIndex) { + this.patrolPointIndex = patrolPointIndex; + } + + public int getLastMobPowerToken() { + return lastMobPowerToken; + } + + public void setLastMobPowerToken(int lastMobPowerToken) { + this.lastMobPowerToken = lastMobPowerToken; + } + + public Regions getLastRegion() { + return lastRegion; + } + + public void setLastRegion(Regions lastRegion) { + this.lastRegion = lastRegion; + } + + public boolean isLootSync() { + return lootSync; + } + + public void setLootSync(boolean lootSync) { + this.lootSync = lootSync; + } + + public int getFidalityID() { + return fidalityID; + } + + public HashMap getEquip() { + return equip; + } + + public int getEquipmentSetID() { + return equipmentSetID; + } + + public int getLootSet() { + return lootSet; + } + + public boolean isGuard(){ + return this.isGuard; + } + + public String getNameOverride() { + return nameOverride; + } + + public static Vector3fImmutable GetSpawnRadiusLocation(Mob mob){ + + Vector3fImmutable returnLoc = Vector3fImmutable.ZERO; + + if (mob.fidalityID != 0 && mob.building != null){ + + + Vector3fImmutable spawnRadiusLoc = Vector3fImmutable.getRandomPointInCircle(mob.localLoc, mob.spawnRadius); + + Vector3fImmutable buildingWorldLoc = ZoneManager.convertLocalToWorld(mob.building, spawnRadiusLoc); + + return buildingWorldLoc; + + + + }else{ + + boolean run = true; + + while(run){ + Vector3fImmutable localLoc = new Vector3fImmutable(mob.statLat + mob.parentZone.absX, mob.statAlt + mob.parentZone.absY, mob.statLon + mob.parentZone.absZ); + Vector3fImmutable spawnRadiusLoc = Vector3fImmutable.getRandomPointInCircle(localLoc, mob.spawnRadius); + + //not a roaming mob, just return the random loc. + if (mob.spawnRadius < 12000) + return spawnRadiusLoc; + + Zone spawnZone = ZoneManager.findSmallestZone(spawnRadiusLoc); + //dont spawn roaming mobs in npc cities + if (spawnZone.isNPCCity()) + continue; + + //dont spawn roaming mobs in player cities. + if (spawnZone.isPlayerCity()) + continue; + + //don't spawn mobs in water. + if (HeightMap.isLocUnderwater(spawnRadiusLoc)) + continue; + + run = false; + + return spawnRadiusLoc; + + } + + } + + //shouldn't ever get here. + + return returnLoc; + } + + public void processUpgradeMob(PlayerCharacter player){ + + lock.writeLock().lock(); + + try{ + + building = this.getBuilding(); + + // Cannot upgrade an npc not within a building + + if (building == null) + return; + + // Cannot upgrade an npc at max rank + + if (this.getRank() == 7) + return; + + // Cannot upgrade an npc who is currently ranking + + if (this.isRanking()) + return; + + int rankCost = Mob.getUpgradeCost(this); + + // SEND NOT ENOUGH GOLD ERROR + + if (rankCost > building.getStrongboxValue()) { + sendErrorPopup(player, 127); + return; + } + + try { + + if (!building.transferGold(-rankCost,false)){ + return; + } + + DateTime dateToUpgrade = DateTime.now().plusHours(Mob.getUpgradeTime(this)); + Mob.setUpgradeDateTime(this,dateToUpgrade); + + // Schedule upgrade job + + Mob.submitUpgradeJob(this); + + } catch (Exception e) { + PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + } + + }catch(Exception e){ + Logger.error(e); + }finally{ + lock.writeLock().unlock(); + } + } + + public void processRedeedMob(ClientConnection origin) { + + // Member variable declaration + PlayerCharacter player; + Contract contract; + CharacterItemManager itemMan; + ItemBase itemBase; + Item item; + + this.lock.writeLock().lock(); + + try{ + + player = SessionManager.getPlayerCharacter(origin); + itemMan = player.getCharItemManager(); + + + contract = this.getContract(); + + if (!player.getCharItemManager().hasRoomInventory((short)1)){ + ErrorPopupMsg.sendErrorPopup(player, 21); + return; + } + + + if (!building.getHirelings().containsKey(this)) + return; + + if (!this.remove(building)) { + PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + return; + } + + building.getHirelings().remove(this); + + itemBase = ItemBase.getItemBase(contract.getContractID()); + + if (itemBase == null) { + Logger.error( "Could not find Contract for npc: " + this.getObjectUUID()); + return; + } + + boolean itemWorked = false; + + item = new Item( itemBase, player.getObjectUUID(), OwnerType.PlayerCharacter, (byte) ((byte) this.getRank() - 1), (byte) ((byte) this.getRank() - 1), + (short) 1, (short) 1, true, false, Enum.ItemContainerType.INVENTORY, (byte) 0, + new ArrayList<>(),""); + item.setNumOfItems(1); + item.containerType = Enum.ItemContainerType.INVENTORY; + + try { + item = DbManager.ItemQueries.ADD_ITEM(item); + itemWorked = true; + } catch (Exception e) { + Logger.error(e); + } + if (itemWorked) { + itemMan.addItemToInventory(item); + itemMan.updateInventory(); + } + + ManageCityAssetsMsg mca = new ManageCityAssetsMsg(); + mca.actionType = NPC.SVR_CLOSE_WINDOW; + mca.setTargetType(building.getObjectType().ordinal()); + mca.setTargetID(building.getObjectUUID()); + origin.sendMsg(mca); + + + }catch(Exception e){ + Logger.error(e); + }finally{ + this.lock.writeLock().unlock(); + } + + } + public void dismiss() { + + if (this.isPet()) { + + if (this.isSummonedPet()) { //delete summoned pet + + WorldGrid.RemoveWorldObject(this); + DbManager.removeFromCache(this); + if (this.getObjectType() == GameObjectType.Mob){ + ((Mob)this).setState(STATE.Disabled); + if (((Mob)this).getParentZone() != null) + ((Mob)this).getParentZone().zoneMobSet.remove(this); + } + + } else { //revert charmed pet + this.setMob(); + this.setCombatTarget(null); + // if (this.isAlive()) + // WorldServer.updateObject(this); + } + //clear owner + PlayerCharacter owner = this.getOwner(); + + //close pet window + if (owner != null) { + Mob pet = owner.getPet(); + PetMsg pm = new PetMsg(5, null); + Dispatch dispatch = Dispatch.borrow(owner, pm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + if (pet != null && pet.getObjectUUID() == this.getObjectUUID()) + owner.setPet(null); + + if (this.getObjectType().equals(GameObjectType.Mob)) + ((Mob)this).setOwner(null); + } + + + } + } + + public void dismissNecroPet(boolean updateOwner) { + + this.state = STATE.Disabled; + + this.combatTarget = null; + this.hasLoot = false; + + if (this.parentZone != null) + this.parentZone.zoneMobSet.remove(this); + + try { + this.clearEffects(); + }catch(Exception e){ + Logger.error( e.getMessage()); + } + this.playerAgroMap.clear(); + WorldGrid.RemoveWorldObject(this); + + DbManager.removeFromCache(this); + + // YEAH BONUS CODE! THANKS UNNAMED ASSHOLE! + //WorldServer.removeObject(this); + //WorldGrid.INSTANCE.removeWorldObject(this); + //owner.getPet().disableIntelligence(); + + PlayerCharacter petOwner = this.getOwner(); + + if (petOwner != null){ + ((Mob)this).setOwner(null); + petOwner.setPet(null); + + if (updateOwner == false) + return; + PetMsg petMsg = new PetMsg(5, null); + Dispatch dispatch = Dispatch.borrow(petOwner, petMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); + } + } + + + + +} diff --git a/src/engine/objects/MobBase.java b/src/engine/objects/MobBase.java new file mode 100644 index 00000000..2c4bf960 --- /dev/null +++ b/src/engine/objects/MobBase.java @@ -0,0 +1,396 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import ch.claude_martin.enumbitset.EnumBitSet; +import engine.Enum; +import engine.gameManager.DbManager; +import engine.server.MBServerStatics; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; + +public class MobBase extends AbstractGameObject { + + private final int loadID; + private final String firstName; + private final byte level; + private float healthMax; + private int attackRating; + private int defenseRating; + private float damageMin; + private float damageMax; + private float hitBoxRadius; + private final int lootTable; + private final float scale; + + private int minGold; + private int maxGold; + + private EnumBitSet flags; + private EnumBitSet noAggro; + private int mask; + + private int goldMod; + private int seeInvis; + private int spawnTime = 0; + private int defense = 0; + private int atr = 0; + private float minDmg = 0; + private float maxDmg = 0; + private ArrayList raceEffectsList; + private float attackRange; + private boolean isNecroPet = false; + + private MobBaseStats mobBaseStats; + private ArrayList runes; + private HashMap staticPowers; + + private float walk = 0; + private float run = 0; + private float walkCombat = 0; + private float runCombat = 0; + + /** + * ResultSet Constructor + */ + public MobBase(ResultSet rs) throws SQLException { + super(rs, rs.getInt("ID")); + + this.loadID = rs.getInt("loadID"); + + this.firstName = rs.getString("name"); + this.level = rs.getByte("level"); + this.lootTable = rs.getInt("lootTableID"); + + this.goldMod = rs.getInt("goldMod"); + this.spawnTime = rs.getInt("spawnTime"); + + LevelDefault levelDefault = LevelDefault.getLevelDefault(this.level); + this.healthMax = rs.getInt("health"); + this.damageMin = rs.getFloat("minDmg"); + this.damageMax = rs.getFloat("maxDmg"); + + this.attackRating = rs.getInt("atr"); + this.defenseRating = rs.getInt("defense"); + this.attackRange = rs.getFloat("attackRange"); + + if (MobbaseGoldEntry.MobbaseGoldMap.containsKey(this.loadID)){ + MobbaseGoldEntry goldEntry = MobbaseGoldEntry.MobbaseGoldMap.get(this.loadID); + + if (goldEntry != null){ + this.minGold = goldEntry.getMin(); + this.maxGold = goldEntry.getMax(); + } + } + else + if (levelDefault != null) { + this.minGold = (levelDefault.goldMin * this.goldMod / 100); + this.maxGold = (levelDefault.goldMax * this.goldMod / 100); + } else { + this.minGold = 10; + this.maxGold = 30; + } + + this.flags = EnumBitSet.asEnumBitSet(rs.getLong("flags"), Enum.MobFlagType.class); + this.noAggro = EnumBitSet.asEnumBitSet(rs.getLong("noaggro"), Enum.AggroType.class); + + this.seeInvis = rs.getInt("seeInvis"); + this.scale = rs.getFloat("scale"); + this.hitBoxRadius = 5f; + this.mask = 0; + + if (this.getObjectUUID() == 12021 || this.getObjectUUID() == 12022) { + this.isNecroPet = true; + } + + if (Enum.MobFlagType.HUMANOID.elementOf(this.flags)) + this.mask += MBServerStatics.MASK_HUMANOID; + + if (Enum.MobFlagType.UNDEAD.elementOf(this.flags)) + this.mask += MBServerStatics.MASK_UNDEAD; + + if (Enum.MobFlagType.BEAST.elementOf(this.flags)) + this.mask += MBServerStatics.MASK_BEAST; + + if (Enum.MobFlagType.DRAGON.elementOf(this.flags)) + this.mask += MBServerStatics.MASK_DRAGON; + + if (Enum.MobFlagType.RAT.elementOf(this.flags)) + this.mask += MBServerStatics.MASK_RAT; + + this.runes = DbManager.MobBaseQueries.LOAD_RUNES_FOR_MOBBASE(this.loadID); + this.raceEffectsList = DbManager.MobBaseQueries.LOAD_STATIC_EFFECTS(this.loadID); + this.mobBaseStats = DbManager.MobBaseQueries.LOAD_STATS(this.loadID); + DbManager.MobBaseQueries.LOAD_ALL_MOBBASE_LOOT(this.loadID); + DbManager.MobBaseQueries.LOAD_ALL_MOBBASE_SPEEDS(this); + + } + + public static HashMap loadEquipmentSet(int equipmentSetID){ + + ArrayList equipList; + HashMap equip = new HashMap<>(); + + if (equipmentSetID == 0) + return equip; + + equipList = EquipmentSetEntry.EquipmentSetMap.get(equipmentSetID); + + if (equipList == null) + return equip; + + for (EquipmentSetEntry equipmentSetEntry : equipList) { + + MobEquipment mobEquipment = new MobEquipment(equipmentSetEntry.getItemID(), equipmentSetEntry.getDropChance()); + ItemBase itemBase = mobEquipment.getItemBase(); + + if (itemBase != null) { + if (itemBase.getType().equals(Enum.ItemType.WEAPON)) + if (mobEquipment.getSlot() == 1 && itemBase.getEquipFlag() == 2) + mobEquipment.setSlot(2); + + equip.put(mobEquipment.getSlot(), mobEquipment); + } + } + + return equip; + } + + public HashMap getStaticPowers() { + return staticPowers; + } + + public void updateStaticEffects() { + this.raceEffectsList = DbManager.MobBaseQueries.LOAD_STATIC_EFFECTS(this.getObjectUUID()); + } + + public void updateRunes() { + this.runes = DbManager.MobBaseQueries.LOAD_RUNES_FOR_MOBBASE(this.getObjectUUID()); + } + + public void updatePowers() { + this.staticPowers = DbManager.MobBaseQueries.LOAD_STATIC_POWERS(this.getObjectUUID()); + } + + public void updateSpeeds(float walk, float walkCombat,float run, float runCombat){ + this.walk = walk; + this.walkCombat = walkCombat; + this.run = run; + this.runCombat = runCombat; + + } + + /* + * Getters + */ + public String getFirstName() { + return this.firstName; + } + + public int getLoadID() { + return this.loadID; + } + + public int getLevel() { + return this.level; + } + + public int getLootTable() { + return this.lootTable; + } + + public float getHealthMax() { + return this.healthMax; + } + + public float getDamageMin() { + return this.damageMin; + } + + public float getDamageMax() { + return this.damageMax; + } + + public int getAttackRating() { + return this.attackRating; + } + + public int getDefenseRating() { + return this.defenseRating; + } + + public int getMinGold() { + return this.minGold; + } + + public int getMaxGold() { + return this.maxGold; + } + + public EnumBitSet getFlags() { + return this.flags; + } + + public EnumBitSet getNoAggro() { + return this.noAggro; + } + + public int getGoldMod() { + return this.goldMod; + } + + public float getScale() { + return this.scale; + } + + public int getTypeMasks() { + return this.mask; + } + + public int getSeeInvis() { + return this.seeInvis; + } + + public int getSpawnTime() { + return this.spawnTime; + } + + + + /* + * Database + */ + public static MobBase getMobBase(int id) { + return MobBase.getMobBase(id, false); + } + + public static MobBase getMobBase(int id, boolean forceDB) { + return DbManager.MobBaseQueries.GET_MOBBASE(id, forceDB); + } + + public static MobBase copyMobBase(MobBase mobbase, String name) { + return DbManager.MobBaseQueries.COPY_MOBBASE(mobbase, name); + } + + public static boolean renameMobBase(int ID, String newName) { + return DbManager.MobBaseQueries.RENAME_MOBBASE(ID, newName); + } + + @Override + public void updateDatabase() { + // TODO Create update logic. + } + + public float getHitBoxRadius() { + if (this.hitBoxRadius < 0f) { + return 0f; + } else { + return this.hitBoxRadius; + } + } + + public MobBaseStats getMobBaseStats() { + return mobBaseStats; + } + + public float getMaxDmg() { + return maxDmg; + } + + public float getMinDmg() { + return minDmg; + } + + public int getAtr() { + return atr; + } + + public void setAtr(int atr) { + this.atr = atr; + } + + public int getDefense() { + return defense; + } + + public void setDefense(int defense) { + this.defense = defense; + } + + /** + * @return the raceEffectsList + */ + public ArrayList getRaceEffectsList() { + return raceEffectsList; + } + + /** + * @return the runes + */ + public ArrayList getRunes() { + return runes; + } + + public float getAttackRange() { + return attackRange; + } + + public boolean isNecroPet() { + return isNecroPet; + } + + public static int GetClassType(int mobbaseID){ + + switch (mobbaseID){ + case 17235: + case 17233: + case 17256: + case 17259: + case 17260: + case 17261: + return 2518; + case 17258: + case 17257: + case 17237: + case 17234: + return 2521; + default: + return 2518; + } + } + + public float getWalk() { + return walk; + } + + public void setWalk(float walk) { + this.walk = walk; + } + + public float getRun() { + return run; + } + + public void setRun(float run) { + this.run = run; + } + + public float getWalkCombat() { + return walkCombat; + } + + public float getRunCombat() { + return runCombat; + } + +} diff --git a/src/engine/objects/MobBaseEffects.java b/src/engine/objects/MobBaseEffects.java new file mode 100644 index 00000000..d8c08172 --- /dev/null +++ b/src/engine/objects/MobBaseEffects.java @@ -0,0 +1,66 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class MobBaseEffects { + + private int mobBaseID; + private int token; + private int rank; + private int reqLvl; + + + + /** + * ResultSet Constructor + */ + public MobBaseEffects(ResultSet rs) throws SQLException { + this.token = rs.getInt("token"); + this.rank = rs.getInt("rank"); + this.reqLvl = rs.getInt("reqLvl"); + } + + + /** + * @return the mobBaseID + */ + public int getMobBaseID() { + return mobBaseID; + } + + + + public void setMobBaseID(int mobBaseID) { + this.mobBaseID = mobBaseID; + } + + + public int getToken() { + return token; + } + + + public int getRank() { + return rank; + } + + + + public int getReqLvl() { + return reqLvl; + } + + + +} diff --git a/src/engine/objects/MobBaseStats.java b/src/engine/objects/MobBaseStats.java new file mode 100644 index 00000000..60a4681a --- /dev/null +++ b/src/engine/objects/MobBaseStats.java @@ -0,0 +1,95 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import java.sql.ResultSet; +import java.sql.SQLException; + + + +public class MobBaseStats { + + private final int baseStr; + private final int baseInt; + private final int baseCon; + private final int baseSpi; + private final int baseDex; + private final long skillSet; + private final int skillValue; + public static MobBaseStats mbs = null; + + + /** + * ResultSet Constructor + */ + public MobBaseStats(ResultSet rs) throws SQLException { + this.baseStr = rs.getInt("Strength"); + this.baseInt = rs.getInt("Intelligence"); + this.baseCon = rs.getInt("Constitution"); + this.baseSpi = rs.getInt("Spirit"); + this.baseDex = rs.getInt("Dexterity"); + this.skillSet = rs.getLong("baseSkills"); + this.skillValue = rs.getInt("skillAmount"); + } + + /** + * Generic Constructor + */ + + public MobBaseStats() { + this.baseStr = 0; + this.baseInt = 0; + this.baseCon = 0; + this.baseSpi = 0; + this.baseDex = 0; + this.skillSet = 0; + this.skillValue = 0; + } + public int getBaseStr() { + return baseStr; + } + + + public int getBaseInt() { + return baseInt; + } + + + public int getBaseCon() { + return baseCon; + } + + + public int getBaseSpi() { + return baseSpi; + } + + + public int getBaseDex() { + return baseDex; + } + + public long getSkillSet() { + return skillSet; + } + + public int getSkillValue() { + return skillValue; + } + + public static MobBaseStats GetGenericStats(){ + if (mbs != null) + return mbs; + mbs = new MobBaseStats(); + return mbs; + } + + +} diff --git a/src/engine/objects/MobEquipment.java b/src/engine/objects/MobEquipment.java new file mode 100644 index 00000000..deb9d405 --- /dev/null +++ b/src/engine/objects/MobEquipment.java @@ -0,0 +1,396 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.exception.SerializationException; +import engine.gameManager.PowersManager; +import engine.net.ByteBufferWriter; +import engine.powers.EffectsBase; +import engine.powers.poweractions.AbstractPowerAction; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.concurrent.atomic.AtomicInteger; + +public class MobEquipment extends AbstractGameObject { + + private static AtomicInteger equipCounter = new AtomicInteger(0); + private final ItemBase itemBase; + private int slot; + private int parentID; + + //effects + private boolean enchanted; + private boolean isID = false; + private AbstractPowerAction prefix; + private AbstractPowerAction suffix; + private int pValue; + private int sValue; + private int magicValue; + + private float dropChance = 0; + + /** + * No Id Constructor + */ + public MobEquipment(ItemBase itemBase, int slot, int parentID) { + super(MobEquipment.getNewID()); + this.itemBase = itemBase; + this.slot = slot; + this.parentID = parentID; + this.enchanted = false; + this.prefix = null; + this.suffix = null; + this.pValue = 0; + this.sValue = 0; + setMagicValue(); + } + + public MobEquipment(ItemBase itemBase, int slot, int parentID, String pIDString, String sIDString, int pValue, int sValue) { + super(MobEquipment.getNewID()); + this.itemBase = itemBase; + this.slot = slot; + this.parentID = parentID; + + //add effects + this.prefix = PowersManager.getPowerActionByIDString(pIDString); + this.suffix = PowersManager.getPowerActionByIDString(sIDString); + + this.pValue = pValue; + this.sValue = sValue; + this.enchanted = this.prefix == null || this.suffix == null; + setMagicValue(); + } + + /** + * ResultSet Constructor + */ + public MobEquipment(ResultSet rs) throws SQLException { + super(MobEquipment.getNewID()); + int itemBaseID = rs.getInt("ItemID"); + this.itemBase = ItemBase.getItemBase(itemBaseID); + this.slot = rs.getInt("slot"); + this.parentID = rs.getInt("mobID"); + setMagicValue(); + } + + + public MobEquipment(int itemBaseID,float dropChance) { + super(MobEquipment.getNewID()); + this.itemBase = ItemBase.getItemBase(itemBaseID); + + if (this.itemBase != null) + this.slot = this.itemBase.getValidSlot(); + else{ + Logger.error("Failed to find Itembase for ID : " + itemBaseID); + this.slot = 0; + } + + this.dropChance = dropChance; + + this.parentID = 0; + setMagicValue(); + } + + public ItemBase getItemBase() { + return itemBase; + } + + public int getSlot() { + return this.slot; + } + + public void setSlot(int value) { + this.slot = value; + } + + public static int getNewID() { + return MobEquipment.equipCounter.incrementAndGet(); + } + + public static void serializeForVendor(MobEquipment mobEquipment,ByteBufferWriter writer, float percent) throws SerializationException { + _serializeForClientMsg(mobEquipment,writer, false); + int baseValue = mobEquipment.itemBase.getBaseValue() + mobEquipment.itemBase.getMagicValue(); + writer.putInt(mobEquipment.magicValue); + writer.putInt(mobEquipment.magicValue); + } + + + public static void serializeForClientMsg(MobEquipment mobEquipment,ByteBufferWriter writer) throws SerializationException { + _serializeForClientMsg(mobEquipment,writer, true); + } + + public static void _serializeForClientMsg(MobEquipment mobEquipment,ByteBufferWriter writer, boolean useSlot) throws SerializationException { + + if (useSlot) + writer.putInt(mobEquipment.slot); + writer.putInt(0); // Pad + writer.putInt(mobEquipment.itemBase.getUUID()); + writer.putInt(mobEquipment.getObjectType().ordinal()); + writer.putInt(mobEquipment.getObjectUUID()); + + // Unknown statics + for (int i = 0; i < 3; i++) { + writer.putInt(0); // Pad + } + for (int i = 0; i < 4; i++) { + writer.putInt(0x3F800000); // Static + } + for (int i = 0; i < 5; i++) { + writer.putInt(0); // Pad + } + for (int i = 0; i < 2; i++) { + writer.putInt(0xFFFFFFFF); // Static + } + + writer.putInt(0); + + writer.put((byte) 1); // End Datablock byte + writer.putInt(0); // Unknown. pad? + writer.put((byte) 1); // End Datablock byte + + writer.putFloat(mobEquipment.itemBase.getDurability()); + writer.putFloat(mobEquipment.itemBase.getDurability()); + + writer.put((byte) 1); // End Datablock byte + + writer.putInt(0); // Pad + writer.putInt(0); // Pad + + writer.putInt(mobEquipment.itemBase.getBaseValue()); + writer.putInt(mobEquipment.magicValue); + + serializeEffects(mobEquipment,writer); + + writer.putInt(0x00000000); + + //name color, think mobEquipment is where mobEquipment goes + if (mobEquipment.enchanted) + if (mobEquipment.isID) + writer.putInt(36); + else + writer.putInt(40); + else + writer.putInt(4); + + writer.putInt(0); + writer.putInt(0); // Pad + writer.putInt(1); + writer.putShort((short) 0); + writer.put((byte) 0); + } + + public final void setMagicValue() { + float value = 1; + if (itemBase != null) + value = itemBase.getBaseValue(); + if (this.prefix != null) { + if (this.prefix.getEffectsBase() != null) + value += this.prefix.getEffectsBase().getValue(); + if (this.prefix.getEffectsBase2() != null) + value += this.prefix.getEffectsBase2().getValue(); + } + if (this.suffix != null) { + if (this.suffix.getEffectsBase() != null) + value += this.suffix.getEffectsBase().getValue(); + if (this.suffix.getEffectsBase2() != null) + value += this.suffix.getEffectsBase2().getValue(); + } + + if (itemBase != null) + + for (Integer token : itemBase.getBakedInStats().keySet()) { + + EffectsBase effect = PowersManager.getEffectByToken(token); + + AbstractPowerAction apa = PowersManager.getPowerActionByIDString(effect.getIDString()); + if (apa.getEffectsBase() != null) + if (apa.getEffectsBase().getValue() > 0){ + //System.out.println(apa.getEffectsBase().getValue()); + value += apa.getEffectsBase().getValue(); + } + + + if (apa.getEffectsBase2() != null) + value += apa.getEffectsBase2().getValue(); + } + + this.magicValue = (int) value; + } + + public int getMagicValue() { + + if (!this.isID) { + return itemBase.getBaseValue(); + } + return this.magicValue; + } + + + public static void serializeEffects(MobEquipment mobEquipment,ByteBufferWriter writer) { + + //skip sending effects if not IDed + if (!mobEquipment.isID) { + writer.putInt(0); + return; + } + + //handle effect count + int cnt = 0; + EffectsBase pre = null; + EffectsBase suf = null; + if (mobEquipment.prefix != null) { + pre = PowersManager.getEffectByIDString(mobEquipment.prefix.getIDString()); + if (pre != null) + cnt++; + } + if (mobEquipment.suffix != null) { + suf = PowersManager.getEffectByIDString(mobEquipment.suffix.getIDString()); + if (suf != null) + cnt++; + } + + writer.putInt(cnt); + + //serialize prefix + if (pre != null) + serializeEffect(mobEquipment,writer, pre, mobEquipment.pValue); + + //serialize suffix + if (suf != null) + serializeEffect(mobEquipment,writer, suf, mobEquipment.sValue); + } + + public static void serializeEffect(MobEquipment mobEquipment,ByteBufferWriter writer, EffectsBase eb, int rank) { + String name; + if (eb.isPrefix()) { + if (mobEquipment.itemBase == null) + name = eb.getName(); + else + name = eb.getName() + ' ' + mobEquipment.itemBase.getName(); + } + else if (eb.isSuffix()) { + if (mobEquipment.itemBase == null) + name = eb.getName(); + else + name = mobEquipment.itemBase.getName() + ' ' + eb.getName(); + } + else { + if (mobEquipment.itemBase == null) + name = ""; + else + name = mobEquipment.itemBase.getName(); + } + + writer.putInt(eb.getToken()); + writer.putInt(rank); + writer.putInt(1); + writer.put((byte) 1); + writer.putInt(mobEquipment.getObjectType().ordinal()); + writer.putInt(mobEquipment.getObjectUUID()); + writer.putString(name); + writer.putFloat(-1000f); + } + + public void setPrefix(String pIDString, int pValue) { + AbstractPowerAction apa = PowersManager.getPowerActionByIDString(pIDString); + if (apa != null) { + this.prefix = apa; + this.pValue = pValue; + } else + this.prefix = null; + + this.enchanted = this.prefix != null || this.suffix != null; + + setMagicValue(); + } + + public void setSuffix(String sIDString, int sValue) { + AbstractPowerAction apa = PowersManager.getPowerActionByIDString(sIDString); + if (apa != null) { + this.suffix = apa; + this.sValue = sValue; + } else + this.suffix = null; + + this.enchanted = this.prefix != null || this.suffix != null; + + setMagicValue(); + } + + public void setIsID(boolean value) { + this.isID = value; + } + + public boolean isID() { + return this.isID; + } + + public void transferEnchants(Item item) { + if (this.prefix != null) { + String IDString = this.prefix.getIDString(); + item.addPermanentEnchantment(IDString, this.pValue); + } + if (this.suffix != null) { + String IDString = this.suffix.getIDString(); + item.addPermanentEnchantment(IDString, this.sValue); + } + if (this.isID) + item.setIsID(true); + } + + + + /* + * Database + */ + @Override + public void updateDatabase() { + } + + + public void persistObject() { + PreparedStatementShared ps = null; + try { + ps = prepareStatement("INSERT INTO static_npc_mobequipment (`mobID`, `slot`, `itemID`) VALUES (?, ?, ?)"); + ps.setInt(1, this.parentID, true); + ps.setInt(2, this.slot); + ps.setInt(3, this.itemBase.getUUID(), true); + ps.executeUpdate(); + } catch (SQLException e) { + Logger.error( e.toString()); + } finally { + ps.release(); + } + } + + public static void removeObject(int UUID, int slot) { + PreparedStatementShared ps = null; + try { + ps = prepareStatement("DELETE FROM `static_npc_mobequipment` WHERE `mobID`=? AND slot=?"); + ps.setInt(1, UUID); + ps.setInt(2, slot); + ps.executeUpdate(); + } catch (SQLException e) { + Logger.error( e.toString()); + } finally { + ps.release(); + } + } + + public float getDropChance() { + return dropChance; + } + + public void setDropChance(float dropChance) { + this.dropChance = dropChance; + } +} diff --git a/src/engine/objects/MobLoot.java b/src/engine/objects/MobLoot.java new file mode 100644 index 00000000..2a9c18ba --- /dev/null +++ b/src/engine/objects/MobLoot.java @@ -0,0 +1,408 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum; +import engine.Enum.ItemType; +import engine.Enum.OwnerType; +import engine.gameManager.DbManager; +import engine.gameManager.PowersManager; +import engine.powers.poweractions.AbstractPowerAction; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * An immutable, non-persistant implementation of Item + * + * @author Burfo + */ +public final class MobLoot extends Item { + + private static final AtomicInteger LastUsedId = new AtomicInteger(0); + + private boolean isDeleted = false; + private boolean noSteal; + private String prefix = ""; + private String suffix = ""; + + private int fidelityEquipID = 0; + + + /** + * Create a new MobLoot. + * Do not use this to create Gold. + * + * @param mob Mob that owns this item + * @param ib ItemBase + */ + public MobLoot(AbstractCharacter mob, ItemBase ib, boolean noSteal) { + this(mob, ib, 0, noSteal); + } + + /** + * Create a new MobLoot item to hold Gold for the Mob. + * + * @param mob Mob that owns this item + * @param qtyOfGold Quantity of gold + */ + public MobLoot(AbstractCharacter mob, int qtyOfGold) { + this(mob, ItemBase.getGoldItemBase(), qtyOfGold, false); + } + + /** + * Create a new MobLoot. + * Primarily used for stackable items that have a quantity. + * + * @param mob Mob that owns this item + * @param ib ItemBase + * @param quantity Quantity of the item + */ + public MobLoot(AbstractCharacter mob, ItemBase ib, int quantity, boolean noSteal) { + super( ib, mob.getObjectUUID(), + OwnerType.Mob, (byte) 0, (byte) 0, (short) 0, + (short) 0, true, false, false, false, true, + false, (byte) 0, new ArrayList<>(), generateId()); + + if (quantity == 0 && ib.getType() == ItemType.RESOURCE) + quantity = 1; + + + if (quantity > 0) + this.setNumOfItems(quantity); + + this.noSteal = noSteal; + this.setIsID(this.getItemBase().isAutoID()); + + // Class is 'final'; passing 'this' should be okay at the end of the constructor + + DbManager.addToCache(this); + } + + /** + * Converts this MotLoot to a persistable Item. Used when a MotLoot is + * looted + * from a Mob to a Player. Do not call for a Gold item. + * + * @return An orphaned Item, ready to be moved to the Player's inventory. + */ + public synchronized Item promoteToItem(PlayerCharacter looter) { + + if (looter == null) + return null; + + if (isDeleted) + return null; + + if (this.getItemBase().getType().equals(ItemType.GOLD)) + return null; + + + Item item = this; + + item.setOwner(looter); + //item.setIsID(false); + + item.containerType = Enum.ItemContainerType.INVENTORY; + item.setValue(0); + item.setName(this.getCustomName()); + item.setIsID(this.isID()); + + if (this.getNumOfItems() > 1) + item.setNumOfItems(this.getNumOfItems()); + + try { + item = DbManager.ItemQueries.ADD_ITEM(item); + } catch (Exception e) { + Logger.error("e"); + return null; + } + + // for (String effectName : this.effectNames) + // item.addPermanentEnchantment(effectName, 0); + //transfer enchantments to item + if (this.prefix.length() != 0) + item.addPermanentEnchantment(this.prefix, 0); + if (this.suffix.length() != 0) + item.addPermanentEnchantment(this.suffix, 0); + + this.junk(); + return item; + } + + public synchronized Item promoteToItemForNPC(NPC looter) { + + if (looter == null) + return null; + + if (isDeleted) + return null; + + if (this.getItemBase().getType().equals(ItemType.GOLD)) + return null; + + Item item = this; + item.setOwner(looter); + item.containerType = Enum.ItemContainerType.INVENTORY; + item.setIsID(true); + + if (this.getNumOfItems() > 1) + item.setNumOfItems(this.getNumOfItems()); + + try { + item = DbManager.ItemQueries.ADD_ITEM(item); + } catch (Exception e) { + Logger.error(e); + return null; + } + item.containerType = Enum.ItemContainerType.INVENTORY; + + // for (String effectName : this.effectNames) + // item.addPermanentEnchantment(effectName, 0); + //transfer enchantments to item + try{ + for (String enchant:this.getEffectNames()){ + item.addPermanentEnchantment(enchant, 0); + } + }catch(Exception e){ + Logger.error(e.getMessage()); + } + + DbManager.NPCQueries.REMOVE_FROM_PRODUCTION_LIST(this.getObjectUUID(),looter.getObjectUUID()); + looter.removeItemFromForge(this); + this.junk(); + return item; + } + + public synchronized void recycle(NPC vendor){ + + //remove from production list for npc in db + + DbManager.NPCQueries.REMOVE_FROM_PRODUCTION_LIST(this.getObjectUUID(),vendor.getObjectUUID()); + this.removeFromCache(); + isDeleted = true; + } + + /** + * Junks the item and marks it as deleted + */ + @Override + protected synchronized void junk() { + this.removeFromCache(); + isDeleted = true; + } + + /** + * Get the MobLoot object from its Id number + * + * @param id Id Number + * @return MobLoot object + */ + public static MobLoot getFromCache(int id) { + return (MobLoot) DbManager.getFromCache(Enum.GameObjectType.MobLoot, id); + } + + /** + * Determines if this object has been marked as deleted. + * + * @return True if deleted. + */ + public boolean isDeleted() { + return this.isDeleted; + } + + public boolean noSteal() { + return this.noSteal; + } + + public void addPermanentEnchantment(String enchantID, int rank, int value, boolean prefix) { + AbstractPowerAction apa = PowersManager.getPowerActionByIDString(enchantID); + if (apa == null) + return; + apa.applyEffectForItem(this, rank); + + //limit to 2 effects + // if (this.effectNames.size() < 2) + // this.effectNames.add(enchantID); + if (prefix) + this.prefix = enchantID; + else + this.suffix = enchantID; + + this.getEffectNames().add(enchantID); + } + + /** + * Get the next available Id number. + * + * @return Id number + */ + private static int generateId() { + int id = LastUsedId.decrementAndGet(); + + //TODO Add a way to reclaim disposed IDs if this becomes a problem + if (id == (-10000)) + Logger.warn("Only 10,000 Id numbers remain useable. Server restart suggested."); + else if (id < Integer.MIN_VALUE + 1000) + Logger.warn("Only " + (Integer.MIN_VALUE + id) + + " Id numbers remain useable! Server restart suggested."); + else if (id == Integer.MIN_VALUE) + throw new UnsupportedOperationException("MobLoot has no remaining Id numbers! Restart server immediately!"); + else if ((id % 10000) == 0) + Logger.info( id + " of " + Integer.MIN_VALUE + " Id numbers consumed."); + + return id; + } + + /* ***** + * All of the following methods are overridden from + * the superclass and intentionally not implemented. + * ***** + */ + /** + * Not implemented + */ + @Override + @Deprecated + public void setOwnerID(int id) { + } + + /** + * Not implemented + */ + @Override + @Deprecated + public synchronized void decrementChargesRemaining() { + } + + /** + * Not implemented + */ + @Override + @Deprecated + protected boolean equipItem(NPC npc, byte slot) { + return false; + } + + /** + * Not implemented + */ + @Override + @Deprecated + protected boolean equipItem(PlayerCharacter pc, byte slot) { + return false; + } + + /** + * Not implemented + */ + @Override + @Deprecated + protected boolean moveItemToBank(NPC npc) { + return false; + } + + /** + * Not implemented + */ + @Override + @Deprecated + protected boolean moveItemToBank(PlayerCharacter pc) { + return false; + } + + /** + * Not implemented + */ + @Override + @Deprecated + protected boolean moveItemToInventory(Corpse corpse) { + return false; + } + + /** + * Not implemented + */ + @Override + @Deprecated + protected boolean moveItemToInventory(NPC npc) { + return false; + } + + /** + * Not implemented + */ + @Override + @Deprecated + protected boolean moveItemToInventory(PlayerCharacter pc) { + return false; + } + + /** + * Not implemented + */ + @Override + @Deprecated + protected boolean moveItemToVault(Account a) { + return false; + } + + /** + * Not implemented + */ + @Override + @Deprecated + public void setLastOwner(AbstractWorldObject value) { + } + + /** + * Not implemented + */ + @Override + @Deprecated + public void updateDatabase() { + } + + /** + * Not implemented + */ + @Override + @Deprecated + protected void validateItemContainer() { + } + + public String getPrefix() { + return prefix; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + public String getSuffix() { + return suffix; + } + + public void setSuffix(String suffix) { + this.suffix = suffix; + } + + public int getFidelityEquipID() { + return fidelityEquipID; + } + + public void setFidelityEquipID(int fidelityEquipID) { + this.fidelityEquipID = fidelityEquipID; + } + + + +} diff --git a/src/engine/objects/MobLootBase.java b/src/engine/objects/MobLootBase.java new file mode 100644 index 00000000..f3ce7925 --- /dev/null +++ b/src/engine/objects/MobLootBase.java @@ -0,0 +1,59 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; + +public class MobLootBase { + + private int mobBaseID; + private int lootTableID; + private float chance; + + public static HashMap> MobLootSet = new HashMap<>(); + + + /** + * ResultSet Constructor + */ + + public MobLootBase(ResultSet rs) throws SQLException { + this.mobBaseID = rs.getInt("mobBaseID"); + this.lootTableID = rs.getInt("lootTable"); + this.chance = rs.getFloat("chance"); + } + + public MobLootBase(int mobBaseID, int lootTableID, int chance) { + super(); + this.mobBaseID = mobBaseID; + this.lootTableID = lootTableID; + this.chance = chance; + + } + + public int getMobBaseID() { + return mobBaseID; + } + public float getChance() { + return chance; + } + + public int getLootTableID() { + return lootTableID; + } + + public void setLootTableID(int lootTableID) { + this.lootTableID = lootTableID; + } + +} diff --git a/src/engine/objects/MobbaseGoldEntry.java b/src/engine/objects/MobbaseGoldEntry.java new file mode 100644 index 00000000..5a6a1dbd --- /dev/null +++ b/src/engine/objects/MobbaseGoldEntry.java @@ -0,0 +1,57 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.gameManager.DbManager; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; + +public class MobbaseGoldEntry { + + private float chance; + private int min; + private int max; + + public static HashMap MobbaseGoldMap = new HashMap<>(); + + /** + * ResultSet Constructor + */ + + public MobbaseGoldEntry(ResultSet rs) throws SQLException { + + this.chance = rs.getFloat("chance"); + this.min = rs.getInt("min"); + this.max = (rs.getInt("max")); + } + + public static void LoadMobbaseGold() { + MobbaseGoldMap = DbManager.MobBaseQueries.LOAD_GOLD_FOR_MOBBASE(); + } + + public float getChance() { + return chance; + } + + public int getMin() { + return min; + } + + public int getMax() { + return max; + } + + + + + +} diff --git a/src/engine/objects/NPC.java b/src/engine/objects/NPC.java new file mode 100644 index 00000000..e6609400 --- /dev/null +++ b/src/engine/objects/NPC.java @@ -0,0 +1,1866 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum; +import engine.Enum.*; +import engine.InterestManagement.WorldGrid; +import engine.ai.MobileFSM.STATE; +import engine.exception.SerializationException; +import engine.gameManager.*; +import engine.job.JobContainer; +import engine.job.JobScheduler; +import engine.jobs.UpgradeNPCJob; +import engine.math.Bounds; +import engine.math.Vector3f; +import engine.math.Vector3fImmutable; +import engine.net.ByteBufferWriter; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.*; +import engine.server.MBServerStatics; +import org.joda.time.DateTime; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import static engine.net.client.msg.ErrorPopupMsg.sendErrorPopup; +import static engine.objects.MobBase.loadEquipmentSet; + +public class NPC extends AbstractCharacter { + + //This is called every 10 minutes to remove items from static npc inventory to make room to buy more. + private static int NUM_ITEMS_TO_JUNK = 30; + + // Used for thread safety + public final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + + protected int loadID; + protected boolean isMob; + protected MobBase mobBase; + protected String name; + protected Building building; + protected Contract contract; + protected int dbID; + protected int currentID; + private DateTime upgradeDateTime = null; + + //used by static npcs + protected Zone parentZone; + protected float statLat; + protected float statLon; + protected float statAlt; + protected float sellPercent; //also train percent + protected float buyPercent; + protected int vendorID; + protected ArrayList modTypeTable; + protected ArrayList modSuffixTable; + protected ArrayList itemModTable; + protected int symbol; + public static int SVR_CLOSE_WINDOW = 4; + + public static ArrayListOprhans = new ArrayList<>(); + + // Variables NOT to be stored in db + protected boolean isStatic = false; + private ArrayList rolling = new ArrayList<>(); + private ArrayList siegeMinions = new ArrayList<>(); + private ConcurrentHashMap siegeMinionMap = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private HashSet canRoll = null; + public static HashMap> _pirateNames = new HashMap<>(); + + public ReentrantReadWriteLock minionLock = new ReentrantReadWriteLock(); + + private int parentZoneID; + + public ArrayList forgedItems = new ArrayList<>(); + private int fidalityID; + private int buildingLevel; + private int buildingFloor; + public HashMap equip = null; + private String nameOverride = ""; + private int equipmentSetID = 0; + private int slot; + private Regions region = null; + + public Vector3fImmutable inBuildingLoc = Vector3fImmutable.ZERO; + private int repairCost = 5; + + public int classID = 0; + public int professionID = 0; + public int extraRune = 0; + public int extraRune2 = 0; + + + /** + * No Id Constructor + */ + public NPC( String name, short statStrCurrent, short statDexCurrent, short statConCurrent, + short statIntCurrent, short statSpiCurrent, short level, int exp, boolean sit, boolean walk, boolean combat, Vector3fImmutable bindLoc, + Vector3fImmutable currentLoc, Vector3fImmutable faceDir, short healthCurrent, short manaCurrent, short stamCurrent, Guild guild, + byte runningTrains, int npcType, boolean isMob, Building building, int contractID, Zone parent) { + super(name, "", statStrCurrent, statDexCurrent, statConCurrent, statIntCurrent, statSpiCurrent, level, exp, + bindLoc, currentLoc, faceDir, guild, runningTrains); + this.loadID = npcType; + this.isMob = isMob; + this.contract = DbManager.ContractQueries.GET_CONTRACT(contractID); + + if (this.contract != null) + this.mobBase = MobBase.getMobBase(this.contract.getMobbaseID()); + else + this.mobBase = MobBase.getMobBase(loadID); + + this.name = name; + this.buyPercent = 0.33f; + this.sellPercent = 1f; + this.building = building; + + this.parentZone = parent; + + initializeMob(); + clearStatic(); + + this.dbID = MBServerStatics.NO_DB_ROW_ASSIGNED_YET; + this.currentID = MBServerStatics.NO_DB_ROW_ASSIGNED_YET; + } + + /** + * Normal Constructor + */ + public NPC( String name, short statStrCurrent, short statDexCurrent, short statConCurrent, + short statIntCurrent, short statSpiCurrent, short level, int exp, boolean sit, boolean walk, boolean combat, Vector3fImmutable bindLoc, + Vector3fImmutable currentLoc, Vector3fImmutable faceDir, short healthCurrent, short manaCurrent, short stamCurrent, Guild guild, + byte runningTrains, int npcType, boolean isMob, Building building, int contractID, Zone parent, int newUUID) { + super( name, "", statStrCurrent, statDexCurrent, statConCurrent, statIntCurrent, statSpiCurrent, level, exp, + bindLoc, currentLoc, faceDir, guild, runningTrains, newUUID); + this.loadID = npcType; + this.isMob = isMob; + + if (this.contract != null) + this.mobBase = MobBase.getMobBase(this.contract.getMobbaseID()); + else + this.mobBase = MobBase.getMobBase(loadID); + + this.building = building; + this.name = name; + this.buyPercent = 0.33f; + this.sellPercent = 1f; + + this.parentZone = parent; + this.dbID = newUUID; + this.currentID = newUUID; + + initializeMob(); + clearStatic(); + } + + /** + * ResultSet Constructor + */ + public NPC(ResultSet rs) throws SQLException { + + super(rs); + + java.util.Date sqlDateTime; + + try{ + this.dbID = rs.getInt(1); + this.currentID = this.dbID; + this.setObjectTypeMask(MBServerStatics.MASK_NPC); + int contractID = rs.getInt("npc_contractID"); + this.parentZoneID = rs.getInt("parent"); + + this.gridObjectType = GridObjectType.STATIC; + this.contract = DbManager.ContractQueries.GET_CONTRACT(contractID); + this.fidalityID = (rs.getInt("fidalityID")); + this.equipmentSetID = rs.getInt("equipmentSet"); + + if (this.equipmentSetID == 0 && this.contract != null) + this.equipmentSetID = this.contract.equipmentSet; + + if (this.contract != null) + this.loadID = this.contract.getMobbaseID(); + else + this.loadID = 2011; //default to human + + int mobBaseOverride = rs.getInt("npc_raceID"); + + if ((this.fidalityID != 0) || (mobBaseOverride != 0)) + this.loadID = mobBaseOverride; + + this.mobBase = MobBase.getMobBase(this.loadID); + this.level = rs.getByte("npc_level"); + this.isMob = false; + int buildingID = rs.getInt("npc_buildingID"); + + try{ + this.building = BuildingManager.getBuilding(buildingID); + + if (this.building != null) + this.building.fidelityNpcs.put(currentID, this.building.fidelityNpcs.size() + 1); + + }catch(Exception e){ + this.building = null; + Logger.error( e.getMessage()); + } + + this.name = rs.getString("npc_name"); + this.buyPercent = rs.getFloat("npc_buyPercent"); + + if (this.buyPercent == 1) + this.buyPercent = .33f; + + this.buyPercent = .33f; + this.sellPercent = 1; + + this.setRot(new Vector3f(0, rs.getFloat("npc_rotation"), 0)); + + this.statLat = rs.getFloat("npc_spawnX"); + this.statAlt = rs.getFloat("npc_spawnY"); + this.statLon = rs.getFloat("npc_spawnZ"); + + this.slot = rs.getInt("npc_slot"); + + if (this.contract != null) { + this.symbol = this.contract.getIconID(); + this.modTypeTable = this.contract.getNPCModTypeTable(); + this.modSuffixTable = this.contract.getNpcModSuffixTable(); + this.itemModTable = this.contract.getItemModTable(); + int VID = this.contract.getVendorID(); + + if (VID != 0) + this.vendorID = VID; + else + this.vendorID = 1; //no vendor items + } + + int guildID = rs.getInt("npc_guildID"); + + if (this.fidalityID != 0){ + if (this.building != null) + this.guild = this.building.getGuild(); + else + this.guild = Guild.getGuild(guildID); + }else + if (this.building != null) + this.guild = this.building.getGuild(); + else + this.guild = Guild.getGuild(guildID); + + if (guildID != 0 && (this.guild == null || this.guild.isErrant())) + NPC.Oprhans.add(currentID); + else if(this.building == null && buildingID > 0) { + NPC.Oprhans.add(currentID); + } + + if (this.guild == null) + this.guild = Guild.getErrantGuild(); + + // Set upgrade date JodaTime DateTime object + // if one exists in the database. + + sqlDateTime = rs.getTimestamp("upgradeDate"); + + if (sqlDateTime != null) + upgradeDateTime = new DateTime(sqlDateTime); + else + upgradeDateTime = null; + + // Submit upgrade job if NPC is currently set to rank. + + if (this.upgradeDateTime != null) + submitUpgradeJob(); + this.buildingFloor = (rs.getInt("npc_buildingFloor")); + this.buildingLevel = (rs.getInt("npc_buildingLevel")); + this.setParentZone(ZoneManager.getZoneByUUID(this.parentZoneID)); + + + if (this.fidalityID != 0) + this.nameOverride = rs.getString("npc_name"); + + }catch(Exception e){ + Logger.error(e); + e.printStackTrace(); + } + + try{ + initializeMob(); + }catch(Exception e){ + Logger.error( e.toString()); + } + + } + + //This method restarts an upgrade timer when a building is loaded from the database. + // Submit upgrade job for this building based upon it's current upgradeDateTime + + public final void submitUpgradeJob() { + + JobContainer jc; + + if (this.getUpgradeDateTime() == null) { + Logger.error("Attempt to submit upgrade job for non-ranking NPC"); + return; + } + + // Submit upgrade job for future date or current instant + + if (this.getUpgradeDateTime().isAfter(DateTime.now())) + jc = JobScheduler.getInstance().scheduleJob(new UpgradeNPCJob(this), + this.getUpgradeDateTime().getMillis()); + else + JobScheduler.getInstance().scheduleJob(new UpgradeNPCJob(this), 0); + + } + + public void setRank(int newRank) { + + DbManager.NPCQueries.SET_PROPERTY(this, "npc_level", newRank); + this.level = (short) newRank; + } + + public int getDBID() { + return this.dbID; + } + + @Override + public int getObjectUUID() { + return currentID; + } + + private void clearStatic() { + this.parentZone = null; + this.statLat = 0f; + this.statLon = 0f; + this.statAlt = 0f; + } + + private void initializeMob() { + + if (this.mobBase != null) { + this.healthMax = this.mobBase.getHealthMax(); + this.manaMax = 0; + this.staminaMax = 0; + this.setHealth(this.healthMax); + this.mana.set(this.manaMax); + this.stamina.set(this.staminaMax); + //this.firstName = this.minionMobBase.getFirstName(); + //this.lastName = ""; + //this.level = (short)(this.minionMobBase.getLevel()); + } + + //add this npc to building + if (this.building != null && this.loadID != 0 && this.fidalityID == 0) { + + if (building.getBlueprint() != null){ + + int maxSlots; + maxSlots = building.getBlueprint().getSlotsForRank(this.building.getRank()); + + for (int slot = 1; slot < maxSlots + 1; slot++) { + + if (!this.building.getHirelings().containsValue(slot)) { + this.building.getHirelings().put(this, slot); + break; + } + } + //npc created by an admin without a blueprint, just default max slot size to 10. + }else{ + + int maxSlots = 10; + + for (int slot = 1; slot < maxSlots + 1; slot++) { + if (!this.building.getHirelings().containsValue(slot)) { + this.building.getHirelings().put(this, slot); + break; + } + } + } + } + + //TODO set these correctly later + this.rangeHandOne = 8; + this.rangeHandTwo = -1; + this.minDamageHandOne = 1; + this.maxDamageHandOne = 4; + this.minDamageHandTwo = 1; + this.maxDamageHandTwo = 4; + this.atrHandOne = 300; + this.atrHandOne = 300; + this.defenseRating = 200; + this.isActive = true; + + this.charItemManager.load(); + } + + public static NPC getFromCache(int id) { + return (NPC) DbManager.getFromCache(GameObjectType.NPC, id); + } + + /* + * Getters + */ + public int getLoadID() { + return loadID; + } + + public boolean isMob() { + return this.isMob; + } + + public MobBase getMobBase() { + return this.mobBase; + } + + public Contract getContract() { + return this.contract; + } + + public int getContractID() { + + if (this.contract != null) + return this.contract.getObjectUUID(); + + return 0; + } + + public boolean isStatic() { + return this.isStatic; + } + + public Building getBuilding() { + return this.building; + } + + public void setName(String value) { + this.name = value; + } + + public static boolean UpdateName(NPC npc, String value) { + + if (!DbManager.NPCQueries.UPDATE_NAME(npc, value)) + return false; + + npc.name = value; + return true; + + } + + public void setBuilding(Building value) { + this.building = value; + } + + @Override + public String getFirstName() { + return this.name; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public String getLastName() { + return ""; + } + + @Override + public Vector3fImmutable getBindLoc() { + return this.bindLoc; + } + + @Override + public int getGuildUUID() { + + if (this.guild == null) + return 0; + + return this.guild.getObjectUUID(); + } + + /* + * Serialization + */ + + public static void __serializeForClientMsg(NPC npc,ByteBufferWriter writer) throws SerializationException { + } + + + public static void serializeNpcForClientMsgOtherPlayer(NPC npc,ByteBufferWriter writer, boolean hideAsciiLastName) + throws SerializationException { + serializeForClientMsgOtherPlayer(npc,writer); + } + + + public static void serializeForClientMsgOtherPlayer(NPC npc,ByteBufferWriter writer) + throws SerializationException { + + writer.putInt(0); + writer.putInt(0); + + //num Runes + int cnt = 3; + boolean isVamp = false, isHealer = false, isArcher = false, isTrainer = false; + int contractID = 0, classID = 0; + int extraRune = 0; + + if (npc.contract != null) { + contractID = npc.contract.getContractID(); + classID = npc.contract.getClassID(); + extraRune = npc.contract.getExtraRune(); + + if (extraRune == contractID) + extraRune = 0; + + } + + if ((contractID > 252642 && contractID < 252647) || contractID == 252652) { + isVamp = true; + cnt++; + } + + if (contractID == 252582 || contractID == 252579 || contractID == 252581 + || contractID == 252584 || contractID == 252597 || contractID == 252598 + || contractID == 252628 || extraRune == 252582 || extraRune == 252579 + || extraRune == 252581 || extraRune == 252584 || extraRune == 252597 + || extraRune == 252598 || extraRune == 252628) { + isHealer = true; + cnt++; + } + + if (contractID == 252570) { + isArcher = true; + cnt++; + } + + if (classID != 0) + cnt++; + + if (extraRune != 0 && extraRune != contractID) { + cnt++; + } + + + writer.putInt(cnt); + + //Race + writer.putInt(1); + writer.putInt(0); + + if (npc.mobBase != null) + writer.putInt(npc.mobBase.getLoadID()); + else + writer.putInt(2011); + + writer.putInt(GameObjectType.NPCRaceRune.ordinal()); + writer.putInt(npc.currentID); + + //Class/Trainer/Whatever + writer.putInt(5); + writer.putInt(0); + + if (npc.contract != null) { + writer.putInt(contractID); + }else + writer.putInt(2500); + + writer.putInt(GameObjectType.NPCClassRune.ordinal()); + writer.putInt(npc.currentID); + + //vampire trainer + cnt = 0; + + if (extraRune != 0) + cnt = serializeExtraRune(npc,extraRune, cnt, writer); + if (isVamp) + cnt = serializeExtraRune(npc,252647, cnt, writer); + + //Healer trainer + if (isHealer) { + // int healerRune = 2501; + // if (npc.getLevel() >= 60) + //healerRune = 252592; + cnt = serializeExtraRune(npc,252592, cnt, writer); + } + + if (classID != 0) { + writer.putInt(4); + writer.putInt(0); + writer.putInt(classID); + writer.putInt(GameObjectType.NPCExtraRune.ordinal()); + writer.putInt(npc.currentID); + } + + //Scout trainer + if (isArcher) { + cnt = serializeExtraRune(npc,252654, cnt, writer); + } + + // if (extraRune != 0 && extraRune != contractID) { + // writer.putInt(3); + // writer.putInt(0); + // writer.putInt(extraRune); + // writer.putInt(GameObjectType.NPCExtraRune.ordinal()); + // writer.putInt(npc.getObjectUUID()); + // } + + //Shopkeeper + writer.putInt(5); + writer.putInt(0); + writer.putInt(0x3DACC); + writer.putInt(GameObjectType.NPCShopkeeperRune.ordinal()); + writer.putInt(npc.currentID); + + //Send Stats + writer.putInt(5); + writer.putInt(0x8AC3C0E6); //Str + writer.putInt(0); + writer.putInt(0xACB82E33); //Dex + writer.putInt(0); + writer.putInt(0xB15DC77E); //Con + writer.putInt(0); + writer.putInt(0xE07B3336); //Int + writer.putInt(0); + writer.putInt(0xFF665EC3); //Spi + writer.putInt(0); + + if (!npc.nameOverride.isEmpty()){ + writer.putString(npc.nameOverride); + writer.putInt(0); + }else + if (npc.contract != null) { + + if (npc.contract.isTrainer()) { + writer.putString(npc.name + ", " + npc.contract.getName()); + writer.putString(""); + } else { + writer.putString(npc.name); + writer.putString(npc.contract.getName()); + } + } else { + writer.putString(npc.name); + writer.putString(""); + } + + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + //writer.putInt(0); wHy do i see 4 in recording? + + writer.put((byte) 0); + writer.putInt(npc.getObjectType().ordinal()); + writer.putInt(npc.currentID); + // writer.putLong(npc.getCompositeID()); + + writer.putFloat(1.0f); + writer.putFloat(1.0f); + writer.putFloat(1.0f); + + if (npc.region != null){ + writer.putVector3f(npc.inBuildingLoc); + }else{ + writer.putFloat(npc.getLoc().getX()); + writer.putFloat(npc.getLoc().getY()); + writer.putFloat(npc.getLoc().getZ()); + } + + //Rotation + float radians = (float) Math.asin(npc.getRot().y) * 2; + + if (npc.building != null) + if (npc.building.getBounds() != null && npc.building.getBounds().getQuaternion() != null) + radians += (npc.building.getBounds().getQuaternion()).angleY; + + writer.putFloat(radians); + + //Running Speed + writer.putInt(0); + + // get a copy of the equipped items. + + if (npc.equip != null){ + writer.putInt(npc.equip.size()); + + for (MobEquipment me: npc.equip.values()) + MobEquipment.serializeForClientMsg(me,writer); + }else + writer.putInt(0); + + writer.putInt((npc.level / 10)); + writer.putInt(npc.level); + writer.putInt(npc.getIsSittingAsInt()); //Standing + writer.putInt(npc.getIsWalkingAsInt()); //Walking + writer.putInt(npc.getIsCombatAsInt()); //Combat + writer.putInt(2); //Unknown + writer.putInt(1); //Unknown - Headlights? + writer.putInt(0); + + if (npc.building != null && npc.region != null){ + writer.putInt(npc.building.getObjectType().ordinal()); + writer.putInt(npc.building.getObjectUUID()); + }else{ + writer.putInt(0); //<-Building Object Type + writer.putInt(0); //<-Building Object ID + } + writer.put((byte) 0); + writer.put((byte) 0); + writer.put((byte) 0); + + //npc dialog menus from contracts + + if (npc.contract != null) { + ArrayList npcMenuOptions = npc.contract.getNPCMenuOptions(); + writer.putInt(npcMenuOptions.size()); + for (Integer val : npcMenuOptions) { + writer.putInt(val); + } + + } else { + writer.putInt(0); + } + + writer.put((byte) 1); + + if (npc.building != null) { + writer.putInt(GameObjectType.StrongBox.ordinal()); + writer.putInt(npc.currentID); + writer.putInt(GameObjectType.StrongBox.ordinal()); + writer.putInt(npc.building.getObjectUUID()); + } else { + writer.putLong(0); + writer.putLong(0); + } + + if (npc.contract != null) { + writer.putInt(npc.contract.getIconID()); + } else { + writer.putInt(0); //npc icon ID + } + // for (int i=0;i<5;i++) + // writer.putInt(0); + writer.putInt(0); + writer.putShort((short)0); + + + if (npc.contract != null && npc.contract.isTrainer()) { + writer.putInt(classID); + } else { + writer.putInt(0); + } + + if (npc.contract != null && npc.contract.isTrainer()) { + writer.putInt(classID); + } else { + writer.putInt(0); + } + writer.putInt(0); + writer.putInt(0); + + writer.putFloat(4); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.put((byte) 0); + + //Pull guild info from building if linked to one + + + + Guild.serializeForClientMsg(npc.guild,writer, null, true); + + + writer.putInt(1); + writer.putInt(0x8A2E); + // writer.putInt((npc.contract != null) ? npc.contract.getContractID() : 0x8A2E); + writer.putInt(0); + writer.putInt(0); + + //TODO Guard + writer.put((byte) 0); //Is guard.. + + writer.putFloat(1500f); //npc.healthMax + writer.putFloat(1500f); //npc.health + + //TODO Peace Zone + writer.put((byte) 1); //0=show tags, 1=don't + writer.putInt(0); + writer.put((byte) 0); + } + + public void removeMinions() { + + for (Mob toRemove : this.siegeMinionMap.keySet()) { + + toRemove.setState(STATE.Disabled); + + try { + toRemove.clearEffects(); + }catch(Exception e){ + Logger.error( e.getMessage()); + } + + if (toRemove.getParentZone() != null) + toRemove.getParentZone().zoneMobSet.remove(toRemove); + + WorldGrid.RemoveWorldObject(toRemove); + DbManager.removeFromCache(toRemove); + + PlayerCharacter petOwner = toRemove.getOwner(); + + if (petOwner != null) { + + petOwner.setPet(null); + toRemove.setOwner(null); + + PetMsg petMsg = new PetMsg(5, null); + Dispatch dispatch = Dispatch.borrow(petOwner, petMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); + + } + } + } + + private static int serializeExtraRune(NPC npc,int runeID, int cnt, ByteBufferWriter writer) { + writer.putInt(5); + writer.putInt(0); + writer.putInt(runeID); + if (cnt == 0) { + writer.putInt(GameObjectType.NPCClassRuneTwo.ordinal()); + } else { + writer.putInt(GameObjectType.NPCClassRuneThree.ordinal()); + } + writer.putInt(npc.currentID); + return cnt + 1; + } + + + @Override + public float getSpeed() { + if (this.isWalk()) { + return MBServerStatics.WALKSPEED; + } else { + return MBServerStatics.RUNSPEED; + } + } + + @Override + public float getPassiveChance(String type, int AttackerLevel, boolean fromCombat) { + //TODO add this later for dodge + return 0f; + } + + /** + * @ Kill this Character + */ + @Override + public void killCharacter(AbstractCharacter attacker) { + //TODO Handle Death + killCleanup(); + + //TODO Send death message if needed + } + + @Override + public void killCharacter(String reason) { + //TODO Handle Death + killCleanup(); + + //Orphan inventory so it can be looted + //if (!this.inSafeZone) + if (this.charItemManager != null) { + this.charItemManager.orphanInventory(); + } + + //TODO Send death message if needed + //Question? How would a mob die to water? + } + + private void killCleanup() { + //TODO handle cleanup from death here + + //set so character won't load + this.load = false; + + //Create Corpse and add to world + //Corpse.makeCorpse(this); + //TODO damage equipped items + //TODO cleanup any timers + } + + public Zone getParentZone() { + return this.parentZone; + } + + public int getParentZoneID() { + if (this.parentZone != null) { + return this.parentZone.getObjectUUID(); + } + return 0; + } + + public void setParentZone(Zone zone) { + if (ConfigManager.serverType.equals(ServerType.LOGINSERVER)) { + return; + } + + if (this.contract == null) + return; + //update ZoneManager's zone building list + if (zone != null) { + if (this.parentZone != null) { + if (zone.getObjectUUID() != this.parentZone.getObjectUUID()) { + this.parentZone.zoneNPCSet.remove(this); + zone.zoneNPCSet.add(this); + } + } else { + zone.zoneNPCSet.add(this); + } + } else if (this.parentZone != null) { + this.parentZone.zoneNPCSet.remove(this); + } + + if (this.parentZone == null) + this.parentZone = zone; + + if (!this.parentZone.isPlayerCity()) + this.sellPercent = 1; + + + if (this.building != null){ + + BuildingModelBase buildingModel = BuildingModelBase.getModelBase(this.building.getMeshUUID()); + + Vector3fImmutable slotLocation = Vector3fImmutable.ZERO; + + if (buildingModel != null){ + + + int putSlot; + BuildingLocation buildingLocation = null; + + //-1 slot means no slot available in building. + if (this.slot != -1) + putSlot = this.slot; + else + putSlot = NPC.getBuildingSlot(this); + + + buildingLocation = buildingModel.getSlotLocation(putSlot); + + if (buildingLocation != null){ + slotLocation = buildingLocation.getLoc(); + this.setRot(new Vector3f(buildingLocation.getRot())); + } + + else{ + //only log if npc has been loaded in building. + if (this.building.getHirelings().containsKey(this) && putSlot != -1) { + Logger.error("could not slot npc : " + currentID + " in slot " + putSlot + " for building Mesh " + this.building.getMeshUUID()); + } + } + } + + + Vector3fImmutable buildingWorldLoc = ZoneManager.convertLocalToWorld(this.building, slotLocation); + + //Set floor and level here after building World Location. + + this.region = BuildingManager.GetRegion(this.building, buildingWorldLoc.x, buildingWorldLoc.y, buildingWorldLoc.z); + + + if (this.region != null){ + + + this.buildingFloor = region.getRoom(); + this.buildingLevel = region.getLevel(); + this.inBuildingLoc = ZoneManager.convertWorldToLocal(building, this.getLoc()); + + }else{ + this.buildingFloor = -1; + this.buildingLevel = -1; + } + this.setBindLoc(new Vector3fImmutable(buildingWorldLoc.x, buildingWorldLoc.y, buildingWorldLoc.z)); + if (ConfigManager.serverType.equals(ServerType.WORLDSERVER)) + this.setLoc(new Vector3fImmutable(buildingWorldLoc.x, buildingWorldLoc.y, buildingWorldLoc.z)); + + }else{ + this.setBindLoc(new Vector3fImmutable(this.statLat + zone.absX, this.statAlt + zone.absY, this.statLon + zone.absZ)); + if (ConfigManager.serverType.equals(ServerType.WORLDSERVER)) + this.setLoc(new Vector3fImmutable(this.statLat + zone.absX, this.statAlt + zone.absY, this.statLon + zone.absZ)); + + } + //create npc profits + if (this.parentZone != null){ + if (this.parentZone.isPlayerCity()) + if (NPC.GetNPCProfits(this) == null) + NPCProfits.CreateProfits(this); + } + + } + + + + @Override + public Vector3fImmutable getLoc() { + + return super.getLoc(); + } + + public float getSpawnX() { + return this.statLat; + } + + public float getSpawnY() { + return this.statAlt; + } + + public float getSpawnZ() { + return this.statLon; + } + + //Sets the relative position to a parent zone + public void setRelPos(Zone zone, float locX, float locY, float locZ) { + + //update ZoneManager's zone building list + if (zone != null) { + if (this.parentZone != null) { + if (zone.getObjectUUID() != this.parentZone.getObjectUUID()) { + this.parentZone.zoneNPCSet.remove(this); + zone.zoneNPCSet.add(this); + } + } else { + zone.zoneNPCSet.add(this); + } + } else if (this.parentZone != null) { + this.parentZone.zoneNPCSet.remove(this); + } + + this.statLat = locX; + this.statAlt = locY; + this.statLon = locZ; + this.parentZone = zone; + } + + public float getSellPercent() { + return this.sellPercent; + } + + public void setSellPercent(float sellPercent) { + this.sellPercent = sellPercent; + } + + public float getBuyPercent() { + return this.buyPercent; + } + public float getBuyPercent(PlayerCharacter player) { + if (NPC.GetNPCProfits(this) == null || this.guild == null) + return this.buyPercent; + NPCProfits profits = NPC.GetNPCProfits(this); + if (player.getGuild().equals(this.guild)) + return profits.buyGuild; + if (player.getGuild().getNation().equals(this.guild.getNation())) + return profits.buyNation; + + return profits.buyNormal; + } + + public float getSellPercent(PlayerCharacter player) { + if (NPC.GetNPCProfits(this) == null || this.guild == null) + return 1 + this.sellPercent; + NPCProfits profits = NPC.GetNPCProfits(this); + if (player.getGuild().equals(this.guild)) + return 1 + profits.sellGuild; + if (player.getGuild().getNation().equals(this.guild.getNation())) + return 1 + profits.sellNation; + + return 1 + profits.sellNormal; + } + + public void setBuyPercent(float buyPercent) { + this.buyPercent = buyPercent; + } + + @Override + public boolean canBeLooted() { + return !this.isAlive(); + } + + // *** Refactor : this has a useInit flag that can be removed + + public static NPC createNPC(String name, int contractID, Vector3fImmutable spawn, Guild guild, boolean isMob, Zone parent, short level, boolean useInit, Building building) { + NPC npcWithoutID = new NPC(name, (short) 0, (short) 0, (short) 0, (short) 0, + (short) 0, (short) 1, 0, false, false, false, spawn, spawn, Vector3fImmutable.ZERO, + (short) 1, (short) 1, (short) 1, guild, (byte) 0, 0, isMob, building, contractID, parent); + + npcWithoutID.setLevel(level); + if (parent != null) { + npcWithoutID.setRelPos(parent, spawn.x - parent.absX, spawn.y - parent.absY, spawn.z - parent.absZ); + } + + if (npcWithoutID.mobBase == null) { + return null; + } + NPC npc; + try { + npc = DbManager.NPCQueries.ADD_NPC(npcWithoutID, isMob); + npc.setObjectTypeMask(MBServerStatics.MASK_NPC); + } catch (Exception e) { + Logger.error( e); + npc = null; + } + + + return npc; + } + + public static NPC getNPC(int id) { + + if (id == 0) + return null; + NPC npc = (NPC) DbManager.getFromCache(GameObjectType.NPC, id); + if (npc != null) + return npc; + + return DbManager.NPCQueries.GET_NPC(id); + } + + public ArrayList getModTypeTable() { + return this.modTypeTable; + } + + @Override + public void updateDatabase() { + DbManager.NPCQueries.updateDatabase(this); + } + + public int getSymbol() { + return symbol; + } + + public ArrayList getModSuffixTable() { + return modSuffixTable; + } + + public ArrayList getItemModTable() { + return itemModTable; + } + + public boolean isRanking() { + + return this.upgradeDateTime != null; + } + + @Override + public void runAfterLoad() { + + if (ConfigManager.serverType.equals(ServerType.LOGINSERVER)) + return; + + if (this.fidalityID != 0) + DbManager.NPCQueries.LOAD_RUNES_FOR_FIDELITY_NPC(this); + + try{ + + this.equip = loadEquipmentSet(this.equipmentSetID); + + }catch(Exception e){ + Logger.error(e.getMessage()); + } + + if (this.equip == null) + this.equip = new HashMap<>(); + + try{ + + DbManager.NPCQueries.LOAD_ALL_ITEMS_TO_PRODUCE(this); + + for (ProducedItem producedItem : this.forgedItems){ + MobLoot ml = new MobLoot(this, ItemBase.getItemBase(producedItem.getItemBaseID()), false); + + DbManager.NPCQueries.UPDATE_ITEM_ID(producedItem.getID(), currentID, ml.getObjectUUID()); + + if (producedItem.isInForge()){ + + if (producedItem.getPrefix() != null && !producedItem.getPrefix().isEmpty()){ + ml.addPermanentEnchantment(producedItem.getPrefix(), 0, 0, true); + ml.setPrefix(producedItem.getPrefix()); + } + + if (producedItem.getSuffix() != null && !producedItem.getSuffix().isEmpty()){ + ml.addPermanentEnchantment(producedItem.getSuffix(), 0, 0, false); + ml.setSuffix(producedItem.getSuffix()); + } + + if (!producedItem.isRandom()) + ml.setIsID(true); + + ml.loadEnchantments(); + + ml.setValue(producedItem.getValue()); + ml.setDateToUpgrade(producedItem.getDateToUpgrade().getMillis()); + ml.containerType = Enum.ItemContainerType.FORGE; + this.addItemToForge(ml); + }else{ + if (producedItem.getPrefix() != null && !producedItem.getPrefix().isEmpty()){ + ml.addPermanentEnchantment(producedItem.getPrefix(), 0, 0, true); + ml.setPrefix(producedItem.getPrefix()); + } + + if (producedItem.getSuffix() != null && !producedItem.getSuffix().isEmpty()){ + ml.addPermanentEnchantment(producedItem.getSuffix(), 0, 0, false); + ml.setSuffix(producedItem.getSuffix()); + } + + ml.setDateToUpgrade(producedItem.getDateToUpgrade().getMillis()); + ml.containerType = Enum.ItemContainerType.INVENTORY; + ml.setIsID(true); + + this.charItemManager.addItemToInventory(ml); + } + ml.setValue(producedItem.getValue()); + } + + // Create NPC bounds object + Bounds npcBounds = Bounds.borrow(); + npcBounds.setBounds(this.getLoc()); + + }catch (Exception e){ + Logger.error( e.getMessage()); + } + } + + public void removeFromZone() { + this.parentZone.zoneNPCSet.remove(this); + } + + @Override + protected ConcurrentHashMap initializePowers() { + return new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + } + + public DateTime getUpgradeDateTime() { + lock.readLock().lock(); + try { + return upgradeDateTime; + } finally { + lock.readLock().unlock(); + } + } + + public void setUpgradeDateTime(DateTime upgradeDateTime) { + + if (!DbManager.NPCQueries.updateUpgradeTime(this, upgradeDateTime)){ + Logger.error( "Failed to set upgradeTime for building " + currentID); + return; + } + + this.upgradeDateTime = upgradeDateTime; + } + + public ArrayList getRolling() { + synchronized(rolling){ + return rolling; + } + } + + public int getRollingCount(){ + synchronized(this.rolling){ + return rolling.size(); + } + } + + public void addItemToForge(MobLoot item){ + synchronized(this.rolling){ + this.rolling.add(item); + } + } + + public void removeItemFromForge(Item item){ + synchronized(this.rolling){ + this.rolling.remove(item); + } + } + + public ArrayList getProtectedBuildings() { + + ArrayList protectedBuildings = new ArrayList<>(); + + if (this.building == null) + return protectedBuildings; + + if (this.building.getCity() == null) + return protectedBuildings; + + for (Building b : this.building.getCity().getParent().zoneBuildingSet) { + + if (b.getBlueprint() == null) + continue; + + if (b.getProtectionState().equals(ProtectionState.CONTRACT)) + protectedBuildings.add(b); + + if (b.getProtectionState().equals(ProtectionState.PENDING)) + protectedBuildings.add(b); + } + + return protectedBuildings; + } + + @Override + public Guild getGuild(){ + if (this.building != null) + return building.getGuild(); + return this.guild; + } + + public ArrayList getSiegeMinions() { + return siegeMinions; + } + + public HashSet getCanRoll() { + + if (this.canRoll == null){ + this.canRoll = DbManager.ItemQueries.GET_ITEMS_FOR_VENDOR(this.vendorID); + + if (this.contract.getVendorID() == 102){ + + for (int i = 0;i getSiegeMinionMap() { + return siegeMinionMap; + } + + public void setSiegeMinionMap(ConcurrentHashMap siegeMinionMap) { + this.siegeMinionMap = siegeMinionMap; + } + + // Method removes the npc from the game simulation + // and deletes it from the database. + + public boolean remove() { + + Building building; + + // Remove npc from it's building + + building = this.building; + + if (building != null) { + building.getHirelings().remove(this); + this.removeMinions(); + } + + // Delete npc from database + + if (DbManager.NPCQueries.DELETE_NPC(this) == 0) { + return false; + } + + // Remove npc from the simulation + + this.removeFromCache(); + WorldGrid.RemoveWorldObject(this); + WorldGrid.removeObject(this); + return true; + } + + public static void loadAllPirateNames() { + + DbManager.NPCQueries.LOAD_PIRATE_NAMES(); + } + + public static String getPirateName(int mobBaseID) { + + ArrayList nameList = null; + + // If we cannot find name for this mobbase then + // fallback to human male + + if (_pirateNames.containsKey(mobBaseID)) + nameList = _pirateNames.get(mobBaseID); + else + nameList = _pirateNames.get(2111); + + if (nameList == null) { + Logger.error("Null name list for 2111!"); + } + + return nameList.get(ThreadLocalRandom.current().nextInt(nameList.size())); + + } + + public synchronized Mob createSiegeMob(int loadID, Guild guild, Zone parent, Vector3fImmutable loc, short level) { + + MobBase minionMobBase; + Mob mob; + + if (siegeMinionMap.size() == 3) + return null; + + minionMobBase = MobBase.getMobBase(loadID); + + if (minionMobBase == null) + return null; + + mob = new Mob(minionMobBase, guild, parent, level,new Vector3fImmutable(1,1,1), 0,false); + + mob.despawned = true; + DbManager.addToCache(mob); + + if (parent != null) + mob.setRelPos(parent, loc.x - parent.absX, loc.y - parent.absY, loc.z - parent.absZ); + + mob.setObjectTypeMask(MBServerStatics.MASK_MOB | mob.getTypeMasks()); + + // mob.setMob(); + mob.setSiege(true); + mob.setParentZone(parent); + + int slot = 0; + + if (!siegeMinionMap.containsValue(1)) + slot = 1; + else if (!siegeMinionMap.containsValue(2)) + slot = 2; + + siegeMinionMap.put(mob, slot); + mob.setInBuildingLoc(this.building, this); + + Vector3fImmutable buildingWorldLoc = ZoneManager.convertLocalToWorld(this.building, mob.getInBuildingLoc()); + mob.setBindLoc(buildingWorldLoc); + mob.setLoc(buildingWorldLoc); + + mob.setSpawnTime(10); + mob.setNpcOwner(this); + mob.setState(STATE.Awake); + + return mob; + } + + public int getUpgradeCost() { + + int upgradeCost; + + upgradeCost = Integer.MAX_VALUE; + + if (this.getRank() < 7) + return (this.getRank() * 100650) + 21450; + + return upgradeCost; + } + + public int getUpgradeTime() { + + int upgradeTime; // expressed in hours + + upgradeTime = Integer.MAX_VALUE; + + if (this.getRank() < 7) + return (this.getRank() * 8); + + return 0; + } + + public static boolean ISGuardCaptain(int contractID){ + return MinionType.ContractToMinionMap.containsKey(contractID); + } + + public synchronized Item produceItem(int playerID,int amount, boolean isRandom, int pToken, int sToken, String customName, int itemID) { + + Zone serverZone; + City city; + Item item = null; + + PlayerCharacter player = null; + + if (playerID != 0) + player = SessionManager.getPlayerCharacterByID(playerID); + + try{ + + if (this.getRollingCount() >= this.getRank()) { + + if (player != null) + ChatManager.chatSystemInfo(player, this.getName() + " " + this.getContract().getName() + " slots are full"); + + return null; + } + + // Cannot roll items without a warehouse. + // Due to the fact fillForge references the + // warehouse and early exits. *** Refactor??? + + serverZone = this.building.getParentZone(); + + if (serverZone == null) + return null; + + city = City.GetCityFromCache(serverZone.getPlayerCityUUID()); + + if (city == null) { + + if (player != null) + ErrorPopupMsg.sendErrorMsg(player, "Could not find city."); // Production denied: This building must be protected to gain access to warehouse resources. + + return null; + } + + if (this.building == null){ + + if (player != null) + ErrorPopupMsg.sendErrorMsg(player, "Could not find building."); // Production denied: This building must be protected to gain ac + + return null; + } + + //TODO create Normal Items. + + if (amount == 0) + amount = 1; + + if (isRandom) + item = ItemFactory.randomRoll(this, player,amount, itemID); + else + item = ItemFactory.fillForge(this, player,amount,itemID, pToken,sToken, customName); + + if (item == null) + return null; + + ItemProductionMsg outMsg = new ItemProductionMsg(this.building, this, item, 8, true); + DispatchMessage.dispatchMsgToInterestArea(this, outMsg, DispatchChannel.SECONDARY, 700, false, false); + + } catch(Exception e){ + Logger.error(e); + } + return item; + } + + public synchronized boolean completeItem(int itemUUID) { + + MobLoot targetItem; + + try { + targetItem = MobLoot.getFromCache(itemUUID); + + if (targetItem == null) + return false; + + if (!this.getCharItemManager().forgeContains(targetItem, this)) + return false; + + if (!DbManager.NPCQueries.UPDATE_ITEM_TO_INVENTORY(targetItem.getObjectUUID(), currentID)) + return false; + + targetItem.setIsID(true); + + this.rolling.remove(targetItem); + this.getCharItemManager().addItemToInventory(targetItem); + + //remove from client forge window + + ItemProductionMsg outMsg1 = new ItemProductionMsg(this.building, this, targetItem, 9, true); + DispatchMessage.dispatchMsgToInterestArea(this, outMsg1, DispatchChannel.SECONDARY, MBServerStatics.STRUCTURE_LOAD_RANGE, false, false); + ItemProductionMsg outMsg = new ItemProductionMsg(this.building, this, targetItem, 10, true); + DispatchMessage.dispatchMsgToInterestArea(this, outMsg, DispatchChannel.SECONDARY, MBServerStatics.STRUCTURE_LOAD_RANGE, false, false); + + } catch(Exception e) { + Logger.error( e.getMessage()); + } + return true; + } + + public int getFidalityID() { + return fidalityID; + } + + public int getBuildingLevel() { + return buildingLevel; + } + + public int getBuildingFloor() { + return buildingFloor; + } + + public HashMap getEquip() { + return equip; + } + + public static int getBuildingSlot(NPC npc){ + int slot = -1; + + if (npc.building == null) + return -1; + + + + BuildingModelBase buildingModel = BuildingModelBase.getModelBase(npc.building.getMeshUUID()); + + if (buildingModel == null) + return -1; + + if (npc.fidalityID != 0){ + + if (npc.building.fidelityNpcs.get(npc.currentID) != null){ + slot = npc.building.fidelityNpcs.get(npc.currentID); + } + + } else{ + if (npc.building.getHirelings().containsKey(npc)) + slot = (npc.building.getHirelings().get(npc)); + } + + if (buildingModel.getNPCLocation(slot) == null) + return -1; + + + return slot; + } + + public int getEquipmentSetID() { + return equipmentSetID; + } + + public static boolean UpdateSlot(NPC npc,int slot){ + + if (!DbManager.NPCQueries.UPDATE_SLOT(npc, slot)) + return false; + + npc.slot = slot; + return true; + } + + public static boolean UpdateEquipSetID(NPC npc, int equipSetID){ + + if (!EquipmentSetEntry.EquipmentSetMap.containsKey(equipSetID)) + return false; + + if (!DbManager.NPCQueries.UPDATE_EQUIPSET(npc, equipSetID)) + return false; + + npc.equipmentSetID = equipSetID; + + return true; + } + + public static boolean UpdateRaceID(NPC npc, int raceID){ + + if (!DbManager.NPCQueries.UPDATE_MOBBASE(npc, raceID)) + return false; + + npc.loadID = raceID; + npc.mobBase = MobBase.getMobBase(npc.loadID); + return true; + } + + public String getNameOverride() { + return nameOverride; + } + + public static NPCProfits GetNPCProfits(NPC npc){ + return NPCProfits.ProfitCache.get(npc.currentID); + } + + public int getRepairCost() { + return repairCost; + } + + public void setRepairCost(int repairCost) { + this.repairCost = repairCost; + } + + public void processUpgradeNPC(PlayerCharacter player) { + + int rankCost; + Building building; + DateTime dateToUpgrade; + + + this.lock.writeLock().lock(); + try{ + + + building = this.getBuilding(); + + // Cannot upgrade an npc not within a building + + if (building == null) + return; + + // Cannot upgrade an npc at max rank + + if (this.getRank() == 7) + return; + + // Cannot upgrade an npc who is currently ranking + + if (this.isRanking()) + return; + + rankCost = this.getUpgradeCost(); + + // SEND NOT ENOUGH GOLD ERROR + + if (!building.hasFunds(rankCost)){ + sendErrorPopup(player, 127); + return; + } + + if (rankCost > building.getStrongboxValue()) { + sendErrorPopup(player, 127); + return; + } + + try { + + if (!building.transferGold(-rankCost,false)) + return; + + dateToUpgrade = DateTime.now().plusHours(this.getUpgradeTime()); + + this.setUpgradeDateTime(dateToUpgrade); + + // Schedule upgrade job + + this.submitUpgradeJob(); + + } catch (Exception e) { + PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + } + }catch(Exception e){ + Logger.error(e); + }finally{ + this.lock.writeLock().unlock(); + } + } + + public void processRedeedNPC( ClientConnection origin) { + + // Member variable declaration + PlayerCharacter player; + Contract contract; + CharacterItemManager itemMan; + ItemBase itemBase; + Item item; + + this.lock.writeLock().lock(); + + try{ + + + if (building == null) + return; + player = SessionManager.getPlayerCharacter(origin); + itemMan = player.getCharItemManager(); + + contract = this.getContract(); + + if (!player.getCharItemManager().hasRoomInventory((short)1)){ + ErrorPopupMsg.sendErrorPopup(player, 21); + return; + } + + + if (!building.getHirelings().containsKey(this)) + return; + + if (!this.remove()) { + PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); + return; + } + + building.getHirelings().remove(this); + + itemBase = ItemBase.getItemBase(contract.getContractID()); + + if (itemBase == null) { + Logger.error("Could not find Contract for npc: " + this.getObjectUUID()); + return; + } + + boolean itemWorked = false; + + item = new Item( itemBase, player.getObjectUUID(), OwnerType.PlayerCharacter, (byte) ((byte) this.getRank() - 1), (byte) ((byte) this.getRank() - 1), + (short) 1, (short) 1, true, false, Enum.ItemContainerType.INVENTORY, (byte) 0, + new ArrayList<>(),""); + item.setNumOfItems(1); + item.containerType = Enum.ItemContainerType.INVENTORY; + + try { + item = DbManager.ItemQueries.ADD_ITEM(item); + itemWorked = true; + } catch (Exception e) { + Logger.error(e); + } + if (itemWorked) { + itemMan.addItemToInventory(item); + itemMan.updateInventory(); + } + + ManageCityAssetsMsg mca = new ManageCityAssetsMsg(); + mca.actionType = SVR_CLOSE_WINDOW; + mca.setTargetType(building.getObjectType().ordinal()); + mca.setTargetID(building.getObjectUUID()); + origin.sendMsg(mca); + + }catch(Exception e){ + Logger.error(e); + }finally{ + this.lock.writeLock().unlock(); + } + + } + + + + +} diff --git a/src/engine/objects/NPCProfits.java b/src/engine/objects/NPCProfits.java new file mode 100644 index 00000000..c96ac55b --- /dev/null +++ b/src/engine/objects/NPCProfits.java @@ -0,0 +1,106 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum.ProfitType; +import engine.gameManager.DbManager; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; + +public class NPCProfits { + + + public int npcUID; + public float buyNormal; + public float buyGuild; + public float buyNation; + public float sellNormal; + public float sellGuild; + public float sellNation; + + public static NPCProfits defaultProfits = new NPCProfits(0,.33f,.33f,.33f,1,1,1); + + + + + public static HashMap ProfitCache = new HashMap<>(); + + /** + * ResultSet Constructor + */ + + public NPCProfits(ResultSet rs) throws SQLException { + + npcUID = rs.getInt("npcUID"); + buyNormal = rs.getFloat("buy_normal"); + buyGuild = rs.getFloat("buy_guild"); + buyNation = rs.getFloat("buy_nation"); + sellNormal = rs.getFloat("sell_normal"); + sellGuild = rs.getFloat("sell_guild"); + sellNation = rs.getFloat("sell_nation"); + + } + + public NPCProfits(int npcUID, float buyNormal, float buyGuild, float buyNation, float sellNormal, + float sellGuild, float sellNation) { + super(); + this.npcUID = npcUID; + this.buyNormal = buyNormal; + this.buyGuild = buyGuild; + this.buyNation = buyNation; + this.sellNormal = sellNormal; + this.sellGuild = sellGuild; + this.sellNation = sellNation; + } + + public static boolean UpdateProfits(NPC npc, NPCProfits profit, ProfitType profitType, float value){ + + try { + if (!DbManager.NPCQueries.UPDATE_PROFITS(npc, profitType, value)) + return false; + }catch(Exception e){ + e.printStackTrace(); + } + + switch (profitType){ + case BuyNormal: + profit.buyNormal = value; + break; + case BuyGuild: + profit.buyGuild = value; + break; + case BuyNation: + profit.buyNation = value; + break; + case SellNormal: + profit.sellNormal = value; + break; + case SellGuild: + profit.sellGuild = value; + break; + case SellNation: + profit.sellNation = value; + break; + + } + return true; + } + + + public static boolean CreateProfits(NPC npc){ + DbManager.NPCQueries.CREATE_PROFITS(npc); + NPCProfits profits = new NPCProfits(npc.getObjectUUID(),.33f,.33f,.33f,1,1,1); + NPCProfits.ProfitCache.put(npc.getObjectUUID(), profits); + return true; + } + +} diff --git a/src/engine/objects/NPCRune.java b/src/engine/objects/NPCRune.java new file mode 100644 index 00000000..9f4ad3fb --- /dev/null +++ b/src/engine/objects/NPCRune.java @@ -0,0 +1,119 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.gameManager.DbManager; +import engine.net.ByteBufferWriter; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; + + +public class NPCRune extends AbstractGameObject { + + private final RuneBase runeBase; + private final int player; + private final ArrayList skillsGranted; + private final ArrayList effectsGranted; + + /** + * No Table ID Constructor + */ + public NPCRune(RuneBase runeBase, int characterID) { + super(); + this.runeBase = runeBase; + this.player = characterID; + if (this.runeBase != null) + this.skillsGranted = DbManager.SkillReqQueries.GET_REQS_FOR_RUNE(this.runeBase.getObjectUUID()); + else + this.skillsGranted = new ArrayList<>(); + this.effectsGranted = new ArrayList<>(); + } + + /** + * Normal Constructor + */ + public NPCRune(RuneBase runeBase, int characterID, int newUUID) { + super(newUUID); + this.runeBase = runeBase; + this.player = characterID; + if (this.runeBase == null) + this.skillsGranted = DbManager.SkillReqQueries.GET_REQS_FOR_RUNE(this.runeBase.getObjectUUID()); + else + this.skillsGranted = new ArrayList<>(); + this.effectsGranted = new ArrayList<>(); + } + /** + * ResultSet Constructor + */ + public NPCRune(ResultSet rs) throws SQLException { + super(rs); + + this.runeBase = RuneBase.getRuneBase(rs.getInt("RuneBaseID")); + this.player = rs.getInt("NpcID"); + if (this.runeBase != null) { + this.skillsGranted = DbManager.SkillReqQueries.GET_REQS_FOR_RUNE(this.runeBase.getObjectUUID()); + this.effectsGranted = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE(this.runeBase.getObjectUUID()); + } else { + Logger.error("Failed to find RuneBase for NPCRune " + this.getObjectUUID()); + this.skillsGranted = new ArrayList<>(); + this.effectsGranted = new ArrayList<>(); + } + } + + /* + * Getters + */ + public RuneBase getRuneBase() { + return this.runeBase; + } + + public int getRuneBaseID() { + if (this.runeBase != null) + return this.runeBase.getObjectUUID(); + return 0; + } + + public int getPlayerID() { + return this.player; + } + + public ArrayList getSkillsGranted() { + return this.skillsGranted; + } + + public ArrayList getEffectsGranted() { + return this.effectsGranted; + } + + /* + * Serializing + */ + + public static void serializeForClientMsg(NPCRune npcRune,ByteBufferWriter writer) { + if (npcRune.runeBase != null) { + writer.putInt(npcRune.runeBase.getType()); + writer.putInt(0); + writer.putInt(npcRune.runeBase.getObjectUUID()); + writer.putInt(npcRune.getObjectType().ordinal()); + writer.putInt(npcRune.getObjectUUID()); + } else { + for (int i = 0; i < 5; i++) + writer.putInt(0); + } + } + + @Override + public void updateDatabase() { + + } +} diff --git a/src/engine/objects/Nation.java b/src/engine/objects/Nation.java new file mode 100644 index 00000000..59b69294 --- /dev/null +++ b/src/engine/objects/Nation.java @@ -0,0 +1,121 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + package engine.objects; + +import engine.net.ByteBufferWriter; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class Nation extends AbstractWorldObject { + + private final String name; + private GuildTag gt; + private String motd = ""; + private int primaryGuildID = 0; + + /** + * No Id Constructor + */ + public Nation( String name, GuildTag gt) { + super(); + this.name = name; + this.gt = gt; + } + + /** + * Normal Constructor + */ + public Nation(String name, GuildTag gt, int newUUID) { + super(newUUID); + this.name = name; + this.gt = gt; + } + + /** + * ResultSet Constructor + */ + public Nation(ResultSet rs) throws SQLException { + super(rs); + + this.name = rs.getString("name"); + + this.gt = new GuildTag( rs.getInt("backgroundColor01"), + rs.getInt("backgroundColor02"), + rs.getInt("symbolColor"), + rs.getInt("symbol"), + rs.getInt("backgroundDesign")); + this.motd = rs.getString("motd"); + this.primaryGuildID = rs.getInt("primaryGuild"); + } + + /* + * Getters + */ + @Override + public String getName() { + return this.name; + } + + public GuildTag getGuildTag() { + return this.gt; + } + + public String getMOTD() { + return this.motd; + } + + public void setMOTD(String value) { + this.motd = value; + } + + public int getPrimaryGuildID() { + return this.primaryGuildID; + } + + public void setPrimaryGuildID(int value) { + this.primaryGuildID = value; + } + + /* + * Utils + */ + private static Nation n; + + public static Nation getErrantNation() { + if (n == null) { + n = new Nation("None", GuildTag.ERRANT, 0); + } + return n; + } + + + + /* + * Serialization + */ + + public static void serializeForTrack(Nation nation,ByteBufferWriter writer) { + writer.putInt(nation.getObjectType().ordinal()); + writer.putInt(nation.getObjectUUID()); + writer.put((byte)1); + GuildTag._serializeForDisplay(nation.gt,writer); + } + + + @Override + public void updateDatabase() { + + } + + @Override + public void runAfterLoad() {} +} diff --git a/src/engine/objects/PlayerBonuses.java b/src/engine/objects/PlayerBonuses.java new file mode 100644 index 00000000..60492929 --- /dev/null +++ b/src/engine/objects/PlayerBonuses.java @@ -0,0 +1,500 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum; +import engine.Enum.ModType; +import engine.Enum.SourceType; +import engine.gameManager.ChatManager; +import engine.gameManager.ConfigManager; +import engine.gameManager.PowersManager; +import engine.powers.DamageShield; +import engine.powers.EffectsBase; +import engine.powers.effectmodifiers.AbstractEffectModifier; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.concurrent.ConcurrentHashMap; + + +public class PlayerBonuses { + + //First bonus set + private ConcurrentHashMap bonusFloats = new ConcurrentHashMap<>(); + private ConcurrentHashMap bonusDamageShields = new ConcurrentHashMap<>(); + private ConcurrentHashMap bonusStrings = new ConcurrentHashMap<>(); + private ConcurrentHashMap> bonusLists = new ConcurrentHashMap<>(); + private ConcurrentHashMap> bonusBools = new ConcurrentHashMap<>(); + private ConcurrentHashMap skillBonuses = new ConcurrentHashMap<>(); + private ConcurrentHashMap regens = new ConcurrentHashMap<>(); + + //If active == 0 then all gets come from the A list and all puts go to the B list + //If active == 1 then all gets come from the B list and all puts go to the A list + //They alternate each time bonuses are calculated so the one being updated isn't read from. + + + /** + * Generic Constructor + */ + public PlayerBonuses(PlayerCharacter pc) { + } + + public static void InitializeBonuses(PlayerCharacter player){ + if (player.bonuses == null) + return; + if (ConfigManager.serverType.equals(Enum.ServerType.LOGINSERVER)) + return; + + player.bonuses.calculateRuneBaseEffects(player); + } + + public PlayerBonuses(Mob mob) { + clearRuneBaseEffects(); + } + + public static PlayerBonuses grantBonuses(AbstractCharacter ac) { + + if (ac.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) + return new PlayerBonuses((PlayerCharacter)ac); + else if (ac.getObjectType().equals(Enum.GameObjectType.Mob)) + return new PlayerBonuses((Mob)ac); + else + return null; + } + + public void clearRuneBaseEffects() { + + this.bonusBools.clear(); + this.bonusFloats.clear(); + this.bonusStrings.clear(); + this.bonusDamageShields.clear(); + this.bonusLists.clear(); + this.skillBonuses.clear(); + this.regens.put(ModType.HealthRecoverRate, (float) 1); + this.regens.put(ModType.ManaRecoverRate, (float) 1); + this.regens.put(ModType.StaminaRecoverRate, (float) 1); + } + + + + public void calculateRuneBaseEffects(PlayerCharacter pc) { + //Clear everything + clearRuneBaseEffects(); + + //recalculate race + + + if (pc.getRace() != null){ + + + if (pc.getRace().getEffectsList() != null) + for (MobBaseEffects raceEffect: pc.getRace().getEffectsList()){ + EffectsBase eb = PowersManager.getEffectByToken(raceEffect.getToken()); + + if (eb == null) + continue; + if (pc.getLevel() < raceEffect.getReqLvl()) + continue; + for (AbstractEffectModifier modifier: eb.getModifiers()){ + modifier.applyBonus(pc, raceEffect.getRank()); + } + + } + + if (SkillsBase.runeSkillsCache.containsKey(pc.getRaceID())){ + for (int skillToken : SkillsBase.runeSkillsCache.get(pc.getRaceID()).keySet()){ + float amount = SkillsBase.runeSkillsCache.get(pc.getRaceID()).get(skillToken); + + SkillsBase sb = SkillsBase.tokenCache.get(skillToken); + + if (sb == null) + continue; + if (this.skillBonuses.containsKey(sb.sourceType) == false) + this.skillBonuses.put(sb.sourceType, amount); + else + this.skillBonuses.put(sb.sourceType, this.skillBonuses.get(sb.sourceType) + amount); + + } + } + } + + //calculate baseclass effects + if (pc.getBaseClass() != null){ + + if (pc.getBaseClass().getEffectsList() != null) + for (MobBaseEffects classEffect: pc.getBaseClass().getEffectsList()){ + EffectsBase eb = PowersManager.getEffectByToken(classEffect.getToken()); + + if (eb == null) + continue; + if (pc.getLevel() < classEffect.getReqLvl()) + continue; + for (AbstractEffectModifier modifier: eb.getModifiers()){ + modifier.applyBonus(pc, classEffect.getRank()); + } + + } + + if (SkillsBase.runeSkillsCache.containsKey(pc.getBaseClassID())){ + for (int skillToken : SkillsBase.runeSkillsCache.get(pc.getBaseClassID()).keySet()){ + float amount = SkillsBase.runeSkillsCache.get(pc.getBaseClassID()).get(skillToken); + + SkillsBase sb = SkillsBase.tokenCache.get(skillToken); + + if (sb == null) + continue; + if (this.skillBonuses.containsKey(sb.sourceType) == false) + this.skillBonuses.put(sb.sourceType, amount); + else + this.skillBonuses.put(sb.sourceType, this.skillBonuses.get(sb.sourceType) + amount); + } + } + + } + + //calculate promotionClass Effects + if (pc.getPromotionClass() != null){ + if (pc.getPromotionClass().getEffectsList() != null) + for (MobBaseEffects promoEffect: pc.getPromotionClass().getEffectsList()){ + EffectsBase eb = PowersManager.getEffectByToken(promoEffect.getToken()); + + if (eb == null) + continue; + if (pc.getLevel() < promoEffect.getReqLvl()) + continue; + for (AbstractEffectModifier modifier: eb.getModifiers()){ + modifier.applyBonus(pc, promoEffect.getRank()); + } + + } + + if (SkillsBase.runeSkillsCache.containsKey(pc.getPromotionClassID())){ + for (int skillToken : SkillsBase.runeSkillsCache.get(pc.getPromotionClassID()).keySet()){ + float amount = SkillsBase.runeSkillsCache.get(pc.getPromotionClassID()).get(skillToken); + + SkillsBase sb = SkillsBase.tokenCache.get(skillToken); + + if (sb == null) + continue; + if (this.skillBonuses.containsKey(sb.sourceType) == false) + this.skillBonuses.put(sb.sourceType, amount); + else + this.skillBonuses.put(sb.sourceType, this.skillBonuses.get(sb.sourceType) + amount); + + } + } + + } + + for(CharacterRune runes : pc.getRunes()){ + RuneBase characterRune = RuneBase.getRuneBase(runes.getRuneBaseID()); + + if (characterRune.getEffectsList() != null) + for (MobBaseEffects runeEffect: characterRune.getEffectsList()){ + EffectsBase eb = PowersManager.getEffectByToken(runeEffect.getToken()); + + if (eb == null) + continue; + if (pc.getLevel() < runeEffect.getReqLvl()) + continue; + for (AbstractEffectModifier modifier: eb.getModifiers()){ + modifier.applyBonus(pc, runeEffect.getRank()); + } + + } + + if (SkillsBase.runeSkillsCache.containsKey(runes.getRuneBaseID())){ + for (int skillToken : SkillsBase.runeSkillsCache.get(runes.getRuneBaseID()).keySet()){ + float amount = SkillsBase.runeSkillsCache.get(runes.getRuneBaseID()).get(skillToken); + + SkillsBase sb = SkillsBase.tokenCache.get(skillToken); + + if (sb == null) + continue; + if (this.skillBonuses.containsKey(sb.sourceType) == false) + this.skillBonuses.put(sb.sourceType, amount); + else + this.skillBonuses.put(sb.sourceType, this.skillBonuses.get(sb.sourceType) + amount); + + } + } + + } + + //Update seeInvis if needed + + float seeInvis = this.getFloat(ModType.SeeInvisible, SourceType.None); + if (pc.getSeeInvis() < seeInvis) + pc.setSeeInvis((short)seeInvis); + + } + + + public void grantEffect(RuneBaseEffect rbe) { + } + + + public void setFloat(AbstractEffectModifier mod, float val) { + if (val != 0) + this.bonusFloats.put(mod, val); + else + this.bonusFloats.remove(mod); + } + + public void setString(AbstractEffectModifier mod, String val) { + if (!val.isEmpty()) + this.bonusStrings.put(mod, val); + else + this.bonusStrings.remove(mod); + } + + public void setList(ModType mod, HashSet val) { + if (!val.equals(null)) + this.bonusLists.put(mod, val); + else + this.bonusLists.remove(mod); + } + + + + + public void addFloat(AbstractEffectModifier mod, Float val) { + if (this.bonusFloats.containsKey(mod) == false) + this.bonusFloats.put(mod, val); + else + this.bonusFloats.put(mod, this.bonusFloats.get(mod) + val); + } + + public void multFloat(AbstractEffectModifier mod, Float val) { + if (this.bonusFloats.containsKey(mod) == false) + this.bonusFloats.put(mod, val); + else + this.bonusFloats.put(mod,this.bonusFloats.get(mod) + (val * ( this.bonusFloats.get(mod) + val))); + } + + public void multRegen(ModType mod, Float val) { + this.regens.put(mod,this.regens.get(mod) + (this.regens.get(mod) * val)); + } + + + public boolean getBool(ModType modType, SourceType sourceType) { + + if (this.bonusBools.containsKey(modType) == false) + return false; + + if (this.bonusBools.get(modType).containsKey(sourceType) == false) + return false; + + return this.bonusBools.get(modType).get(sourceType); + + } + + public float getSkillBonus(SourceType sourceType) { + + if (this.skillBonuses.containsKey(sourceType) == false) + return 0; + return this.skillBonuses.get(sourceType); + } + + + public float getFloat(ModType modType, SourceType sourceType) { + float amount = 0; + for (AbstractEffectModifier mod : this.bonusFloats.keySet()){ + if (mod.getPercentMod() != 0) + continue; + if (mod.modType.equals(modType) == false || mod.sourceType.equals(sourceType) == false) + continue; + + if (this.bonusFloats.get(mod) == null) + continue; + + amount += this.bonusFloats.get(mod); + } + return amount; + } + + public float getFloatPercentPositive(ModType modType, SourceType sourceType) { + float amount = 0; + for (AbstractEffectModifier mod : this.bonusFloats.keySet()){ + + if (mod.getPercentMod() == 0 && !modType.equals(ModType.AdjustAboveDmgCap)) + continue; + + + if (mod.modType.equals(modType) == false || mod.sourceType.equals(sourceType) == false) + continue; + + + if (this.bonusFloats.get(mod) == null) + continue; + + if (this.bonusFloats.get(mod) < 0) + continue; + amount += this.bonusFloats.get(mod); + } + + return amount; + } + + public float getFloatPercentAll(ModType modType, SourceType sourceType) { + float amount = 0; + for (AbstractEffectModifier mod : this.bonusFloats.keySet()){ + + if (mod.getPercentMod() == 0 && !modType.equals(ModType.AdjustAboveDmgCap)) + continue; + + + if (mod.modType.equals(modType) == false || mod.sourceType.equals(sourceType) == false) + continue; + + if (this.bonusFloats.get(mod) == null) + continue; + + amount += this.bonusFloats.get(mod); + } + + return amount; + } + + public float getRegen(ModType modType) { + return this.regens.get(modType); + } + + + + public float getFloatPercentNullZero(ModType modType, SourceType sourceType) { + float amount = 0; + for (AbstractEffectModifier mod : this.bonusFloats.keySet()){ + + if (mod.getPercentMod() == 0) + continue; + if (mod.modType.equals(modType) == false || mod.sourceType.equals(sourceType) == false) + continue; + + if (this.bonusFloats.get(mod) == null) + continue; + + amount += this.bonusFloats.get(mod); + } + return amount; + } + + public float getFloatPercentNegative(ModType modType, SourceType sourceType) { + float amount = 0; + for (AbstractEffectModifier mod : this.bonusFloats.keySet()){ + + if (mod.getPercentMod() == 0) + continue; + if (mod.modType.equals(modType) == false || mod.sourceType.equals(sourceType) == false) + continue; + + if (this.bonusFloats.get(mod) == null) + continue; + + if (this.bonusFloats.get(mod) > 0) + continue; + + + amount += this.bonusFloats.get(mod); + } + return amount; + } + + + + + + public HashSet getList(ModType modType) { + if (this.bonusLists.containsKey(modType)) + return this.bonusLists.get(modType); + else + return null; + } + + public ConcurrentHashMap getDamageShields() { + return this.bonusDamageShields; + } + + public void addDamageShield(AbstractEffectModifier mod , DamageShield ds) { + this.bonusDamageShields.put(mod, ds); + } + + + + public void updateIfHigher(AbstractEffectModifier mod, Float val) { + + if (this.bonusFloats.containsKey(mod) == false){ + this.bonusFloats.put(mod, val); + return; + } + float oldVal = this.getFloat(mod.modType, mod.sourceType); + + if (oldVal > val) + return; + + this.bonusFloats.put(mod,val); + + } + + + //Read maps + + public void printBonusesToClient(PlayerCharacter pc) { + + + + for (ModType modType: this.bonusBools.keySet()){ + for (SourceType sourceType: this.bonusBools.get(modType).keySet()){ + ChatManager.chatSystemInfo(pc, modType.name() + "-" + sourceType.name() + " = " + this.bonusBools.get(modType).get(sourceType)); + } + } + + for (ModType modType : ModType.values()){ + + if (modType.equals(ModType.StaminaRecoverRate) || modType.equals(ModType.HealthRecoverRate) || modType.equals(ModType.ManaRecoverRate)) + ChatManager.chatSystemInfo(pc, modType.name() + " = " + this.getRegen(modType)); + else + for (SourceType sourceType : SourceType.values()){ + float amount = this.getFloat(modType, sourceType); + float percentAmount = this.getFloatPercentPositive(modType, sourceType); + float percentAmountNegative = this.getFloatPercentNegative(modType, sourceType); + + if (amount != 0) + ChatManager.chatSystemInfo(pc, modType.name() + "-" + (sourceType.equals(SourceType.None) == false ? sourceType.name() : "") + " = " + amount); + + if (percentAmount != 0) + ChatManager.chatSystemInfo(pc, "Percent : " + modType.name() + "-" + (sourceType.equals(SourceType.None) == false ? sourceType.name() : "") + " = " + percentAmount); + + if (percentAmountNegative != 0) + ChatManager.chatSystemInfo(pc, "Negative Percent : " + modType.name() + "-" + (sourceType.equals(SourceType.None) == false ? sourceType.name() : "") + " = " + percentAmountNegative); + + } + } + + } + + + public void setBool(ModType modType, SourceType sourceType , boolean val) { + if (val == true){ + + if (this.bonusBools.get(modType) == null){ + HashMap sourceMap = new HashMap<>(); + this.bonusBools.put(modType, sourceMap); + } + + this.bonusBools.get(modType).put(sourceType, val); + return; + } + + if (this.bonusBools.containsKey(modType)) + this.bonusBools.get(modType).remove(sourceType); + } + +} diff --git a/src/engine/objects/PlayerCharacter.java b/src/engine/objects/PlayerCharacter.java new file mode 100644 index 00000000..ef6f623a --- /dev/null +++ b/src/engine/objects/PlayerCharacter.java @@ -0,0 +1,5659 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum; +import engine.Enum.*; +import engine.InterestManagement.HeightMap; +import engine.InterestManagement.InterestManager; +import engine.InterestManagement.RealmMap; +import engine.InterestManagement.WorldGrid; +import engine.ai.MobileFSM.STATE; +import engine.db.archive.CharacterRecord; +import engine.db.archive.DataWarehouse; +import engine.db.archive.PvpRecord; +import engine.exception.MsgSendException; +import engine.exception.SerializationException; +import engine.gameManager.*; +import engine.job.JobContainer; +import engine.job.JobScheduler; +import engine.jobs.DeferredPowerJob; +import engine.jobs.FinishSpireEffectJob; +import engine.jobs.NoTimeJob; +import engine.math.Bounds; +import engine.math.FastMath; +import engine.math.Vector3fImmutable; +import engine.net.ByteBufferWriter; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.*; +import engine.net.client.msg.login.CommitNewCharacterMsg; +import engine.powers.EffectsBase; +import engine.server.MBServerStatics; +import engine.server.login.LoginServer; +import engine.server.login.LoginServerMsgHandler; +import engine.util.MiscUtils; +import org.joda.time.DateTime; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + + +public class PlayerCharacter extends AbstractCharacter { + + //This object is to be used as the lock in a synchronized statement + //any time the name of a PlayerCharacter needs to be set or + //changed. It ensures the uniqueness check and subsequent + //database update can happen exclusively. + private static final Object FirstNameLock = new Object(); + + private final Account account; + private final Race race; + private BaseClass baseClass; + private PromotionClass promotionClass; + protected ArrayList runes; + + private final byte skinColor; + private final byte hairColor; + private final byte beardColor; + + private final byte hairStyle; + private final byte beardStyle; + private long channelMute = 0; // none muted. + + //All Guild information should be held here + private final AtomicInteger guildStatus; + + private final AtomicInteger strMod = new AtomicInteger(); // Stat Modifiers + private final AtomicInteger dexMod = new AtomicInteger(); + private final AtomicInteger conMod = new AtomicInteger(); + private final AtomicInteger intMod = new AtomicInteger(); + private final AtomicInteger spiMod = new AtomicInteger(); + private final ReadWriteLock teleportLock = new ReentrantReadWriteLock(true); + public final ReadWriteLock respawnLock = new ReentrantReadWriteLock(true); + + private ConcurrentHashMap ignoredPlayerIDs = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + + public boolean notDeleted; // <-Use this for deleting character + + // =========================================== + // Variables NOT to put into the database!!!! (session only) + // =========================================== + public short statStrMax; // Max Base Stats + public short statDexMax; + public short statConMax; + public short statIntMax; + public short statSpiMax; + public short statStrMin; // Min Base Stats + public short statDexMin; + public short statConMin; + public short statIntMin; + public short statSpiMin; + + // Current Stats before Equip and Effect + // Modifiers + public short statStrBase; + public short statDexBase; + public short statConBase; + public short statIntBase; + public short statSpiBase; + public short trainedStatPoints = 0; + + private boolean lfGroup = false; + private boolean lfGuild = false; + private boolean recruiting = false; + private MovementState movementState = MovementState.IDLE; + private MovementState lastMovementState = MovementState.IDLE; + + private int overFlowEXP = 0; + + private int lastGuildToInvite; + private int lastGroupToInvite; + private boolean follow = false; + private final HashMap summoners = new HashMap<>(); + private final HashSet loadedObjects = new HashSet<>(); + private HashSet loadedStaticObjects = new HashSet<>(); + private Vector3fImmutable lastStaticLoc = new Vector3fImmutable(0.0f, 0.0f, 0.0f); + private final ConcurrentHashMap> chatChanFloodList = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private final ConcurrentHashMap killMap = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private GameObjectType lastTargetType; + private int lastTargetID; + private int hidden = 0; // current rank of hide/sneak/invis + private int seeInvis = 0; // current rank of see invis + private float speedMod; + private float raceRunMod; + private boolean teleportMode = false; // Teleport on MoveToPoint + private final AtomicInteger trainsAvailable = new AtomicInteger(0); // num skill trains not used + private float dexPenalty; + private long lastPlayerAttackTime = 0; + private long lastMobAttackTime = 0; + private long lastUsedPowerTime = 0; + private long lastTargetOfUsedPowerTime = 0; + private long lastUpdateTime = System.currentTimeMillis(); + private long lastStamUpdateTime = System.currentTimeMillis(); + private boolean safeZone = false; + public boolean isCSR = false; + private int bindBuildingID; + private int lastContract; + private boolean noTeleScreen = false; + + private int lastRealmID = -2; + private int subRaceID = 0; + + private boolean hasAnniversery = false; + + //TODO Public fields break OO!!! + public boolean newChar; + + private DeferredPowerJob weaponPower; + private NPC lastNPCDialog; + + private Mob pet; + public final ArrayList necroPets = new ArrayList<>(); + //Used for skill/Power calculation optimization + private CharacterTitle title = CharacterTitle.NONE; + private boolean asciiLastName = true; + + private int spamCount = 0; + + private boolean initialized = false; + + private boolean enteredWorld = false; + + private boolean canBreathe = true; + /* + DataWarehouse based kill/death tracking. + These sets contain the last 10 UUID's + */ + + public LinkedList pvpKills; + public LinkedList pvpDeaths; + private String hash; + public int lastBuildingAccessed = 0; + + private ArrayList guildHistory = new ArrayList<>(); + + public double timeLoggedIn = 0; + + public boolean RUN_MAGICTREK = true; + + public int spellsCasted = 0; + public int pingCount = 0; + public long startPing = 0; + public double ping = 0; + private boolean wasTripped75 = false; + private boolean wasTripped50 = false; + private boolean wasTripped25 = false; + + private float characterHeight = 0; + public float centerHeight = 0; + private boolean lastSwimming = false; + + private boolean isTeleporting = false; + public float landingAltitude = 0; + + public int bindBuilding = 0; + public FriendStatus friendStatus = FriendStatus.Available; + + /** + * No Id Constructor + */ + public PlayerCharacter( String firstName, String lastName, short strMod, short dexMod, short conMod, short intMod, + short spiMod, Guild guild, byte runningTrains, Account account, Race race, BaseClass baseClass, byte skinColor, byte hairColor, + byte beardColor, byte hairStyle, byte beardStyle) { + super(firstName, lastName, (short) 1, (short) 1, (short) 1, (short) 1, (short) 1, (short) 1, 0, + Vector3fImmutable.ZERO, Vector3fImmutable.ZERO, Vector3fImmutable.ZERO, + guild, runningTrains); + + this.runes = new ArrayList<>(); + this.account = account; + this.notDeleted = true; + this.race = race; + this.baseClass = baseClass; + this.skinColor = skinColor; + this.hairColor = hairColor; + this.beardColor = beardColor; + this.hairStyle = hairStyle; + this.beardStyle = beardStyle; + this.lfGroup = false; + this.lfGuild = false; + + this.strMod.set(strMod); + this.dexMod.set(dexMod); + this.conMod.set(conMod); + this.intMod.set(intMod); + this.spiMod.set(spiMod); + + this.guildStatus = new AtomicInteger(0); + this.bindBuildingID = -1; + } + + /** + * ResultSet Constructor + */ + public PlayerCharacter(ResultSet rs) throws SQLException { + super(rs, true); + + this.runes = DbManager.CharacterRuneQueries.GET_RUNES_FOR_CHARACTER(this.getObjectUUID()); + int accountID = rs.getInt("parent"); + this.account = DbManager.AccountQueries.GET_ACCOUNT(accountID); + this.gridObjectType = GridObjectType.DYNAMIC; + + this.notDeleted = rs.getBoolean("char_isActive"); + + int raceID = rs.getInt("char_raceID"); + this.race = Race.getRace(raceID); + + int baseClassID = rs.getInt("char_baseClassID"); + this.baseClass = DbManager.BaseClassQueries.GET_BASE_CLASS(baseClassID); + + int promotionClassID = rs.getInt("char_promotionClassID"); + this.promotionClass = DbManager.PromotionQueries.GET_PROMOTION_CLASS(promotionClassID); + + this.skinColor = rs.getByte("char_skinColor"); + this.hairColor = rs.getByte("char_hairColor"); + this.beardColor = rs.getByte("char_beardColor"); + this.hairStyle = rs.getByte("char_hairStyle"); + this.beardStyle = rs.getByte("char_beardStyle"); + + this.lfGroup = false; + this.lfGuild = false; + + //TODO Unify game object with database after DB overhaul + this.guildStatus = new AtomicInteger(0); + + Guild guild = Guild.getGuild(this.getGuildUUID()); + if (guild != null && guild.isGuildLeader(this.getObjectUUID())) + this.setGuildLeader(true); + else + this.setGuildLeader(false); + + this.hasAnniversery = rs.getBoolean("anniversery"); + + this.setInnerCouncil(rs.getBoolean("guild_isInnerCouncil")); + this.setFullMember(rs.getBoolean("guild_isFullMember")); + this.setTaxCollector(rs.getBoolean("guild_isTaxCollector")); + this.setRecruiter(rs.getBoolean("guild_isRecruiter")); + this.setGuildTitle(rs.getInt("guild_title")); + + if (this.account != null) + this.ignoredPlayerIDs = DbManager.PlayerCharacterQueries.GET_IGNORE_LIST(this.account.getObjectUUID(), false); + + this.strMod.set(rs.getShort("char_strMod")); + this.dexMod.set(rs.getShort("char_dexMod")); + this.conMod.set(rs.getShort("char_conMod")); + this.intMod.set(rs.getShort("char_intMod")); + this.spiMod.set(rs.getShort("char_spiMod")); + + this.bindBuildingID = rs.getInt("char_bindBuilding"); + + this.hash = rs.getString("hash"); + + + // For debugging skills + // CharacterSkill.printSkills(this); + } + + public void setGuildTitle(int value) { + if (GuildStatusController.getTitle(this.guildStatus) == value) + return; + DbManager.PlayerCharacterQueries.SET_GUILD_TITLE(this, value); + GuildStatusController.setTitle(guildStatus, value); + } + + public void setFullMember(boolean value) { + if (GuildStatusController.isFullMember(this.guildStatus) == value) + return; + DbManager.PlayerCharacterQueries.SET_FULL_MEMBER(this, value); + GuildStatusController.setFullMember(guildStatus, value); + } + + public void setRecruiter(boolean value) { + if (GuildStatusController.isRecruiter(this.guildStatus) == value) + return; + DbManager.PlayerCharacterQueries.SET_RECRUITER(this, value); + GuildStatusController.setRecruiter(guildStatus, value); + } + + public void setTaxCollector(boolean value) { + if (GuildStatusController.isTaxCollector(this.guildStatus) == value) + return; + DbManager.PlayerCharacterQueries.SET_TAX_COLLECTOR(this, value); + GuildStatusController.setTaxCollector(guildStatus, value); + } + + public void setInnerCouncil(boolean value) { + + // dont update if its the same. + if (GuildStatusController.isInnerCouncil(this.guildStatus) == value) + return; + + DbManager.PlayerCharacterQueries.SET_INNERCOUNCIL(this, value); + GuildStatusController.setInnerCouncil(guildStatus, value); + } + + public void setGuildLeader(boolean value) { + if (GuildStatusController.isGuildLeader(this.guildStatus) == value) + return; + + GuildStatusController.setGuildLeader(guildStatus, value); + if (value == true){ + this.setInnerCouncil(true); + this.setFullMember(true); + } + } + + //END -> Guild Status Interface + public void resetGuildStatuses() { + this.setInnerCouncil(false); + this.setFullMember(false); + this.setGuildTitle(0); + this.setTaxCollector(false); + this.setRecruiter(false); + this.setGuildLeader(false); + } + + /* + * Getters + */ + public byte getHairStyle() { + return hairStyle; + } + + public byte getBeardStyle() { + return beardStyle; + } + + public void setWeaponPower(DeferredPowerJob value) { + this.weaponPower = value; + } + + public DeferredPowerJob getWeaponPower() { + return this.weaponPower; + } + + public void setSafeZone(boolean value) { + this.safeZone = value; + } + + public boolean inSafeZone() { + return this.safeZone; + } + + public boolean isInSafeZone() { + + Zone zone = ZoneManager.findSmallestZone(this.getLoc()); + + if (zone != null){ + return zone.getSafeZone() == (byte) 1; + } + + return false; + //return this.safeZone; + } + + /** + * @return the account + */ + public Account getAccount() { + return account; + } + + public void deactivateCharacter() { + this.notDeleted = false; + DbManager.PlayerCharacterQueries.SET_DELETED(this); + DbManager.removeFromCache(this); + } + + public void activateCharacter() { + this.notDeleted = true; + DbManager.PlayerCharacterQueries.SET_DELETED(this); + } + + public boolean isDeleted() { + return !this.notDeleted; + } + + public ArrayList getRunes() { + return this.runes; + } + + public CharacterRune getRune(int runeID) { + if (this.runes == null) + return null; + for (CharacterRune cr : this.runes) { + if (cr.getRuneBase() != null && cr.getRuneBase().getObjectUUID() == runeID) + return cr; + } + return null; + } + + public boolean addRune(CharacterRune value) { + if (this.runes.size() > 12) // Max Runes + return false; + if (this.runes.indexOf(value) != -1) // Already contains rune + return false; + this.runes.add(value); + return true; + } + + public boolean removeRune(CharacterRune value) { + int index = this.runes.indexOf(value); + if (index == -1) + return false; + this.runes.remove(index); + return true; + } + + public CharacterRune removeRune(int runeID) { + Iterator it = this.runes.iterator(); + while (it.hasNext()) { + CharacterRune cr = it.next(); + if (cr != null) { + RuneBase rb = cr.getRuneBase(); + if (rb != null) + if (runeID == rb.getObjectUUID()) { + it.remove(); + DbManager.CharacterRuneQueries.DELETE_CHARACTER_RUNE(cr); + return cr; + } + } + } + return null; + } + + /** + * @ Kill this Character + */ + @Override + public void killCharacter(AbstractCharacter attacker) { + + killCleanup(); + + // *** Mobs have a separate combat path? Crazy shit! + // *** Mobs don't get Experience for killing players. everything else is done in killCleanup(); + + if (attacker.getObjectType().equals(GameObjectType.PlayerCharacter) == false){ + + Zone zone = ZoneManager.findSmallestZone(this.getLoc()); + + //DeathShroud + + if (zone.getSafeZone() == 0) + PowersManager.applyPower(this, this, Vector3fImmutable.ZERO, 1672601862, 40, false); + + //enable this to give players deathshroud if mobs kill player. + + // Zone zone = ZoneManager.findSmallestZone(this.getLoc()); + // if (zone.getSafeZone() == 0) + // PowersManager.applyPower(this, this, Vector3fImmutable.ZERO, 1672601862, 40, false); + return; + } + + + // Death to other player. + // TODO Send PvP and guild/nation message + PlayerCharacter att = (PlayerCharacter) attacker; + String message = this.getFirstName(); + if (this.guild != null && (!(this.guild.getName().equals("Errant")))) + message += " of " + this.guild.getName(); + message += " was killed by " + att.getFirstName(); + if (att.guild != null && (!(att.guild.getName().equals("Errant")))) + message += " of " + att.guild.getName(); + message += "!"; + + + //see if we shold grant xp to attacker + boolean doPVPEXP = false; + long lastKill = att.getLastKillOfTarget(this.getObjectUUID()); + if ((System.currentTimeMillis() - lastKill) > MBServerStatics.PLAYER_KILL_XP_TIMER) + if (attacker.getLevel() > 39 && this.getLevel() > 39) { + Guild aN = null; + Guild tN = null; + if (attacker.getGuild() != null) + aN = attacker.getGuild().getNation(); + if (this.getGuild() != null) + tN = this.getGuild().getNation(); + if (aN == null || tN == null || aN.isErrant() || Guild.sameGuild(aN, tN) || this.isDeathShroud()) { + //skip giving xp if same guild or attacker is errant, or target is in death shroud. + } else { + doPVPEXP = true; + } + } + //apply death shroud to non safeholds. + Zone zone = ZoneManager.findSmallestZone(this.getLoc()); + + //DeathShroud + + if (zone.getSafeZone() == 0) + PowersManager.applyPower(this, this, Vector3fImmutable.ZERO, 1672601862, 40, false); + + if (doPVPEXP){ + Group g = GroupManager.getGroup((PlayerCharacter) attacker); + Experience.doExperience((PlayerCharacter) attacker, this, g); + } + + ChatManager.chatPVP(message); + + /* + Update kill / death tracking lists + Each character on list is unique. Only once! + */ + + PlayerCharacter aggressorCharacter = (PlayerCharacter)attacker; + + boolean containsVictim = true; + boolean containsAttacker = true; + + containsVictim = aggressorCharacter.pvpKills.contains(this.getObjectUUID()); + containsAttacker = aggressorCharacter.pvpKills.contains(this.getObjectUUID()); + + // Rorate attacker's kill list + + if ((aggressorCharacter.pvpKills.size() == 10) && containsVictim == false) + aggressorCharacter.pvpKills.removeLast(); + + if (containsVictim == false) + aggressorCharacter.pvpKills.addFirst(this.getObjectUUID()); + + // Rotate the poor victim's deathlist + + if ((this.pvpDeaths.size() == 10) && containsAttacker == false) + this.pvpDeaths.removeLast(); + + if (containsAttacker == false) + this.pvpDeaths.addFirst(this.getObjectUUID()); + + // DataWarehouse: store pvp event + + PvpRecord pvpRecord = PvpRecord.borrow((PlayerCharacter) attacker, this, this.getLoc(), doPVPEXP); + DataWarehouse.pushToWarehouse(pvpRecord); + + // Mark kill time in killmap + + att.updateKillMap(this.getObjectUUID()); + } + + @Override + public void killCharacter(String reason) { + + killCleanup(); + Zone zone = ZoneManager.findSmallestZone(this.getLoc()); + + if (zone.getSafeZone() == 0) + PowersManager.applyPower(this, this, Vector3fImmutable.ZERO, 1672601862, 40, false); + + // Send death message if needed + if (reason.equals("Water")) { + + TargetedActionMsg targetedActionMsg = new TargetedActionMsg(this, true); + Dispatch dispatch = Dispatch.borrow(this, targetedActionMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + + String message = this.getFirstName(); + + if (this.guild != null && (!(this.guild.getName().equals("Errant")))) + message += " of " + this.guild.getName(); + else + message += this.getLastName(); + message += " was killed by water!"; + + ChatManager.chatPVP(message); + + } + } + + private void killCleanup() { + this.stopMovement(this.getLoc()); + + this.health.set(-1); + //remove pet + if (this.pet != null) + this.dismissPet(); + + this.dismissNecroPets(); + // remove flight job. + + this.setTakeOffTime(0); + this.setDesiredAltitude(0); + this.altitude = (float) 0; + + this.getCharItemManager().closeTradeWindow(); + + //increment live counter. This is to prevent double kills from casts + this.liveCounter++; + + //remove any effects + try { + this.clearEffects(); + }catch(Exception e){ + Logger.error("PlayerCharacter.KillCleanup", e.getMessage()); + } + + //remove the SIT flag + this.setSit(false); + + + + // sends a kill message to ensure the Player falls over. + + this.respawnLock.writeLock().lock(); + + try{ + if (SessionManager.getPlayerCharacterByID(this.getObjectUUID()) == null && !this.enteredWorld){ + WorldGrid.RemoveWorldObject(this); + this.respawn(false, false,true); + }else{ + TargetedActionMsg killmsg = new TargetedActionMsg(this, true); + DispatchMessage.dispatchMsgToInterestArea(this, killmsg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); + } + }catch(Exception e){ + Logger.error(e); + }finally{ + this.respawnLock.writeLock().unlock(); + } + + // TODO damage equipped items + if (this.charItemManager != null) + this.charItemManager.damageAllGear(); + + // TODO cleanup any timers + //recalculate inventory weights + if (this.charItemManager != null) { + this.charItemManager.endTrade(true); + this.charItemManager.calculateWeights(); + this.charItemManager.updateInventory(); + } + + + + + + + } + + + public void updateKillMap(int target) { + this.killMap.put(target, System.currentTimeMillis()); + } + + public long getLastKillOfTarget(int target) { + if (this.killMap.containsKey(target)) + return this.killMap.get(target); + return 0L; + } + + public boolean isDeathShroud() { + return this.effects != null && this.effects.containsKey("DeathShroud"); + } + + public void setSafeMode() { + PowersManager.applyPower(this, this, Vector3fImmutable.ZERO, -1661758934, 40, false); + } + + public boolean safemodeInvis() { + + if (!this.effects.containsKey("Invisible")) + return false; + + Effect eff = this.effects.get("Invisible"); + + if (eff == null) + return false; + + return eff.getEffectToken() == -1661751254; + + } + + public void respawn(boolean setAlive, boolean enterWorld, boolean makeCorpse) { + + // Recalculate everything + + + this.recalculatePlayerStats(true); + this.setCombat(false); + + // Set Health to 1/4 max + + + + Corpse corpse = null; + + if (makeCorpse){ + try { + corpse = Corpse.makeCorpse(this, enterWorld); + } catch (Exception e) { + Logger.error( e); + } + //if we're not making corpse, just purge inventory. used for characters dead while logged out. + } + + if (!setAlive){ + if (corpse == null && makeCorpse) { + Logger.error("Corpse not created."); + } + else { + if(makeCorpse && corpse != null){ + InterestManager.forceLoad(corpse); + } + } + return; + } + + this.setHealth((float) (healthMax * .25)); + this.isAlive.set(true); + + + // Put player in safe mode + // Teleport the player to his bind loc + // or to a ruin as apporpriate. + + Building bindBuilding = BuildingManager.getBuildingFromCache(this.getBindBuildingID()); + + if (enterWorld) { + this.stopMovement(this.getBindLoc()); + } + else if (bindBuilding != null) { + if (bindBuilding.getParentZone().equals(ZoneManager.findSmallestZone(this.getLoc()))) + this.teleport(Ruins.getRandomRuin().getLocation()); + else + this.teleport(this.getBindLoc()); + } else // no bind building found for player, teleport to ruins. + this.teleport(Ruins.getRandomRuin().getLocation()); + + this.lastUpdateTime = System.currentTimeMillis(); + this.lastStamUpdateTime = System.currentTimeMillis(); + + this.update(); + + PowersManager.applyPower(this, this, Vector3fImmutable.ZERO, -1661758934, 40, false); + + if (corpse == null && makeCorpse) { + Logger.error("Corpse not created."); + } + else { + if(makeCorpse && corpse != null){ + InterestManager.forceLoad(corpse); + } + } + } + + public Effect addCityEffect(String name, EffectsBase eb, int trains, int duration, boolean onEnter, City city) { + JobContainer jc = null; + if (onEnter) { + NoTimeJob ntj = new NoTimeJob(this, name, eb, trains); //infinite timer + ntj.setEffectSourceType(city.getObjectType().ordinal()); + ntj.setEffectSourceID(city.getObjectUUID()); + jc = new JobContainer(ntj); + } else { + FinishSpireEffectJob fsej = new FinishSpireEffectJob(this, name, eb, trains); + fsej.setEffectSourceType(city.getObjectType().ordinal()); + fsej.setEffectSourceID(city.getObjectUUID()); + jc = JobScheduler.getInstance().scheduleJob(fsej, duration); + } + + if (this.effects.get(name) != null) + this.effects.get(name).cancelJob(); + + Effect eff = new Effect(jc, eb, trains); + this.effects.put(name, eff); + applyAllBonuses(); + eff.sendSpireEffect(this.getClientConnection(), onEnter); + return eff; + } + + /** + * @return the race + */ + public Race getRace() { + return race; + } + + public int getRaceID() { + if (race != null) + return race.getRaceRuneID(); + return 0; + } + + /** + * @return the baseClass + */ + public BaseClass getBaseClass() { + return baseClass; + } + + public int getBaseClassID() { + if (baseClass != null) + return baseClass.getObjectUUID(); + return 0; + } + + public int getBaseClassToken() { + if (this.baseClass == null) + return 0; + else + return this.baseClass.getToken(); + } + + public boolean setBaseClass(int value) { + BaseClass bs = BaseClass.getBaseClass(value); + if (bs != null) { + this.baseClass = bs; + return true; + } + return false; + } + + @Override + public Vector3fImmutable getBindLoc() { + + Vector3fImmutable bindLocation; + + // Return garbage and early exit if this is the login server. + // getBindLoc() does a TOL lookup, which also then loads the + // city and other garbage not needed on the login server. + + if (ConfigManager.serverType.equals(ServerType.LOGINSERVER)) + return Vector3fImmutable.ZERO; + + Building bindBuilding = PlayerCharacter.getUpdatedBindBuilding(this); + + //handle rented room binds. + + + if (bindBuilding == null){ + bindLocation = Enum.Ruins.getRandomRuin().getLocation(); + return bindLocation; + } + + + + bindLocation = BuildingManager.GetBindLocationForBuilding(bindBuilding); + + if (bindLocation == null) + bindLocation = Enum.Ruins.getRandomRuin().getLocation(); + + return bindLocation; + + } + + public int getInventoryCapacity() { + return statStrBase * 3; + } + + public int getInventoryCapacityRemaining() { + return (this.getInventoryCapacity() - this.charItemManager.getInventoryWeight()); + } + + /** + * @return the PromotionClass + */ + public PromotionClass getPromotionClass() { + return promotionClass; + } + + public int getPromotionClassID() { + if (promotionClass != null) + return promotionClass.getObjectUUID(); + return 0; + } + + public boolean setPromotionClass(int value) { + + PromotionClass promotionClass = PromotionClass.GetPromtionClassFromCache(value); + + if (promotionClass == null) + return false; + + + if (!DbManager.PlayerCharacterQueries.SET_PROMOTION_CLASS(this, value)) + return false; + + this.promotionClass = promotionClass; + + // Warehouse this event + CharacterRecord.updatePromotionClass(this); + return true; + } + + /** + * @return the skinColor + */ + public byte getSkinColor() { + return skinColor; + } + + /** + * @return the hairColor + */ + public byte getHairColor() { + return hairColor; + } + + /** + * @return the beardColor + */ + public byte getBeardColor() { + return beardColor; + } + + /** + * @return the lfGroup + */ + public boolean isLfGroup() { + return lfGroup; + } + + public int getIsLfGroupAsInt() { + if (lfGroup) + return 2; + return 1; + } + + + public final void toggleLFGroup() { + this.lfGroup = !this.lfGroup; + } + + public final void toggleLFGuild() { + this.lfGuild = !this.lfGuild; + } + + public final void toggleRecruiting() { + this.recruiting = !this.recruiting; + } + + public final void setLFGroup(final boolean value) { + this.lfGroup = value; + } + + public final void setLFGuild(final boolean value) { + this.lfGuild = value; + } + + public final void setRecruiting(final boolean value) { + this.recruiting = value; + } + + public final boolean isLFGroup() { + return this.lfGroup; + } + + public final boolean isLFGuild() { + return this.lfGuild; + } + + public final boolean isRecruiting() { + return this.recruiting; + } + + /** + * @return the lfGuild + */ + public boolean isLfGuild() { + return lfGuild; + } + + public final int getHeadlightsAsInt() { + if (this.lfGroup) + if (this.lfGuild) + if (this.recruiting) + return 14; // LFGroup + LFGuild + Recruiting + else + return 6; // LFGroup + LFGuild + else if (this.recruiting) + return 10; // LFGroup + Recruiting + else + return 2; // LFGroup only + else if (this.lfGuild) + if (this.recruiting) + return 12; // LFGuild + Recruiting + else + return 4; // LFGuild only + else if (this.recruiting) + return 8; // Recruiting only + else + return 0; // No Headlights + } + + public int getIsLfGuildAsInt() { + if (lfGuild) + return 2; + return 1; + } + + public int getStrMax() { + return this.statStrMax; + } + public int getDexMax() { + return this.statDexMax; + } + public int getConMax() { + return this.statConMax; + } + public int getIntMax() { + return this.statIntMax; + } + public int getSpiMax() { + return this.statSpiMax; + } + + public void addStr(int amount) { + + boolean worked = false; + short newStr = (short) 0; + while (!worked) { + + if ((this.unusedStatPoints - this.trainedStatPoints) <= 0) + return; + + newStr = (short) (this.statStrBase + amount); + short mod = (short) this.strMod.get(); + short newStrMod = (short) (mod + amount); + + if (newStr > this.statStrMax) { + newStrMod += (this.statStrMax - newStr); + newStr = this.statStrMax; + } + worked = this.strMod.compareAndSet(mod, newStrMod); + } + this.trainedStatPoints++; + this.statStrBase = newStr; + this.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS); + this.applyBonuses(); + this.calculateSkills(); + } + + public void addDex(int amount) { + + boolean worked = false; + short newDex = (short) 0; + + while (!worked) { + + if ((this.unusedStatPoints - this.trainedStatPoints) <= 0) + return; + + newDex = (short) (this.statDexBase + amount); + short mod = (short) this.dexMod.get(); + short newDexMod = (short) (mod + amount); + + if (newDex > this.statDexMax) { + newDexMod += (this.statDexMax - newDex); + newDex = this.statDexMax; + } + + worked = this.dexMod.compareAndSet(mod, newDexMod); + } + this.trainedStatPoints++; + this.statDexBase = newDex; + this.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS); + this.applyBonuses(); + this.calculateSkills(); + } + + public void addCon(int amount) { + boolean worked = false; + short newCon = (short) 0; + while (!worked) { + + if ((this.unusedStatPoints - this.trainedStatPoints) <= 0) + return; + + newCon = (short) (this.statConBase + amount); + short mod = (short) this.conMod.get(); + short newConMod = (short) (mod + amount); + + if (newCon > this.statConMax) { + newConMod += (this.statConMax - newCon); + newCon = this.statConMax; + } + worked = this.conMod.compareAndSet(mod, newConMod); + } + this.trainedStatPoints++; + this.statConBase = newCon; + this.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS); + this.applyBonuses(); + this.calculateSkills(); + } + + public void addInt(int amount) { + boolean worked = false; + short newInt = (short) 0; + while (!worked) { + + if ((this.unusedStatPoints - this.trainedStatPoints) <= 0) + return; + + newInt = (short) (this.statIntBase + amount); + short mod = (short) this.intMod.get(); + short newIntMod = (short) (mod + amount); + + if (newInt > this.statIntMax) { + newIntMod += (this.statIntMax - newInt); + newInt = this.statIntMax; + } + worked = this.intMod.compareAndSet(mod, newIntMod); + } + this.trainedStatPoints++; + this.statIntBase = newInt; + this.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS); + this.applyBonuses(); + this.calculateSkills(); + } + + public void addSpi(int amount) { + boolean worked = false; + short newSpi = (short) 0; + + while (!worked) { + + if ((this.unusedStatPoints - this.trainedStatPoints) <= 0) + return; + + newSpi = (short) (this.statSpiBase + amount); + short mod = (short) this.spiMod.get(); + short newSpiMod = (short) (mod + amount); + + if (newSpi > this.statSpiMax) { + newSpiMod += (this.statSpiMax - newSpi); + newSpi = this.statSpiMax; + } + worked = this.spiMod.compareAndSet(mod, newSpiMod); + } + this.trainedStatPoints++; + this.statSpiBase = newSpi; + this.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS); + this.applyBonuses(); + this.calculateSkills(); + } + + public boolean refineStr() { + boolean worked = false; + short newStr = (short) 0; + + while (!worked) { + + newStr = (short) (this.statStrBase - 1); + short mod = (short) this.strMod.get(); + + if (mod == 0) + return false; + + short newStrMod = (short) (mod - 1); + + if (newStr < this.statStrMin) + return false; + + if (!canRefineLower(MBServerStatics.STAT_STR_ID)) + return false; + + worked = this.strMod.compareAndSet(mod, newStrMod); + } + this.trainedStatPoints--; + this.statStrBase = newStr; + this.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS); + this.applyBonuses(); + this.calculateSkills(); + return true; + } + + public boolean refineDex() { + boolean worked = false; + short newDex = (short) 0; + + while (!worked) { + newDex = (short) (this.statDexBase - 1); + short mod = (short) this.dexMod.get(); + + if (mod == 0) + return false; + + short newDexMod = (short) (mod - 1); + + if (newDex < this.statDexMin) + return false; + + if (!canRefineLower(MBServerStatics.STAT_DEX_ID)) + return false; + + worked = this.dexMod.compareAndSet(mod, newDexMod); + } + this.trainedStatPoints--; + this.statDexBase = newDex; + this.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS); + this.applyBonuses(); + this.calculateSkills(); + return true; + } + + public boolean refineCon() { + boolean worked = false; + short newCon = (short) 0; + + while (!worked) { + newCon = (short) (this.statConBase - 1); + short mod = (short) this.conMod.get(); + + if (mod == 0) + return false; + + short newConMod = (short) (mod - 1); + + if (newCon < this.statConMin) + return false; + + if (!canRefineLower(MBServerStatics.STAT_CON_ID)) + return false; + + worked = this.conMod.compareAndSet(mod, newConMod); + } + this.trainedStatPoints--; + this.statConBase = newCon; + this.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS); + this.applyBonuses(); + this.calculateSkills(); + return true; + } + + public boolean refineInt(RefineMsg msg) { + boolean worked = false; + short newInt = (short) 0; + + while (!worked) { + newInt = (short) (this.statIntBase - 1); + short mod = (short) this.intMod.get(); + + if (mod == 0) + return false; + short newIntMod = (short) (mod + - 1); + + if (newInt < this.statIntMin) + return false; + + if (!canRefineLower(MBServerStatics.STAT_INT_ID)) + return false; + + worked = this.intMod.compareAndSet(mod, newIntMod); + } + this.trainedStatPoints--; + this.statIntBase = newInt; + this.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS); + + verifySkillMax(msg); + + this.applyBonuses(); + this.calculateSkills(); + return true; + } + + public boolean refineSpi() { + boolean worked = false; + short newSpi = (short) 0; + while (!worked) { + newSpi = (short) (this.statSpiBase - 1); + short mod = (short) this.spiMod.get(); + if (mod == 0) + return false; + short newSpiMod = (short) (mod - 1); + if (newSpi < this.statSpiMin) + return false; + if (!canRefineLower(MBServerStatics.STAT_SPI_ID)) + return false; + worked = this.spiMod.compareAndSet(mod, newSpiMod); + } + this.trainedStatPoints--; + this.statSpiBase = newSpi; + this.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS); + this.applyBonuses(); + this.calculateSkills(); + return true; + } + + //this verifies stat doesn't fall too low to keep runes applied while refining + private boolean canRefineLower(int stat) { + for (CharacterRune cr : this.runes) { + if (cr != null) { + RuneBase rb = cr.getRuneBase(); + if (rb != null) { + ArrayList attrs = rb.getAttrs(); + + if (attrs != null) + for (RuneBaseAttribute rba : attrs) { + int attrID = rba.getAttributeID(); + int mod = rba.getModValue(); + if (stat == MBServerStatics.STAT_STR_ID) { + if (attrID == MBServerStatics.RUNE_STR_MIN_NEEDED_ATTRIBUTE_ID && ((int) this.statStrBase <= mod)) + return false; + } else if (stat == MBServerStatics.STAT_DEX_ID) { + if (attrID == MBServerStatics.RUNE_DEX_MIN_NEEDED_ATTRIBUTE_ID && ((int) this.statDexBase <= mod)) + return false; + } else if (stat == MBServerStatics.STAT_CON_ID) { + if (attrID == MBServerStatics.RUNE_CON_MIN_NEEDED_ATTRIBUTE_ID && ((int) this.statConBase <= mod)) + return false; + } else if (stat == MBServerStatics.STAT_INT_ID) { + if (attrID == MBServerStatics.RUNE_INT_MIN_NEEDED_ATTRIBUTE_ID && ((int) this.statIntBase <= mod)) + return false; + } else if (stat == MBServerStatics.STAT_SPI_ID) + if (attrID == MBServerStatics.RUNE_SPI_MIN_NEEDED_ATTRIBUTE_ID && ((int) this.statSpiBase <= mod)) + return false; + } + } + } + } + return true; + } + + //checked on refining int to see if skills need refined also. + private void verifySkillMax(RefineMsg msg) { + + ConcurrentHashMap skills = getSkills(); + + //make sure no skills are over the max number of trains + int maxTrains = CharacterSkill.getMaxTrains((int) this.statIntBase); + + RefineMsg rm = new RefineMsg(msg.getNpcType(), msg.getNpcID(), 0, 0); + + for (CharacterSkill skill : skills.values()) { + + while (skill.getNumTrains() > maxTrains) { + boolean worked = skill.refine(this, false); //refine skill, do not recalculate everything + if (worked) { + rm.setToken(skill.getToken()); + + Dispatch dispatch = Dispatch.borrow(this, rm); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + } else { + Logger.error("Failed to force refine of skill " + skill.getObjectUUID() + " by character " + this.getObjectUUID()); + break; + } + } + } + } + + public int getClassToken() { + if (this.promotionClass != null) + return this.promotionClass.getToken(); + else if (this.baseClass != null) + return this.baseClass.getToken(); + return 0; + } + + public int getRaceToken() { + + if (this.race == null) + return 0; + + return this.race.getToken(); + } + + public void setLastTarget(GameObjectType type, int id) { + this.lastTargetType = type; + this.lastTargetID = id; + } + + public GameObjectType getLastTargetType() { + return this.lastTargetType; + } + + public int getLastTargetID() { + return this.lastTargetID; + } + + public synchronized int getBindBuildingID() { + return this.bindBuildingID; + } + + public synchronized void setBindBuildingID(int value) { + DbManager.PlayerCharacterQueries.SET_BIND_BUILDING(this, value); + this.bindBuildingID = value; + } + + public static Building getUpdatedBindBuilding(PlayerCharacter player){ + Building returnBuilding = null; + + //update bindBuilding based on Guild or nation TOL; + + if(player.getBindBuildingID() == 0) { + + returnBuilding = PlayerCharacter.getBindBuildingForGuild(player); + + if (returnBuilding != null) + player.setBindBuildingID(returnBuilding.getObjectUUID()); + return returnBuilding; + } + returnBuilding = BuildingManager.getBuildingFromCache(player.getBindBuildingID()); + + if (returnBuilding == null){ + returnBuilding = PlayerCharacter.getBindBuildingForGuild(player); + + if (returnBuilding != null) + player.setBindBuildingID(returnBuilding.getObjectUUID()); + } + return returnBuilding; + } + + public static Building getBindBuildingForGuild(PlayerCharacter player){ + + Building returnBuilding; + + if (player.getGuild() == null || player.getGuild().isErrant()) + return null; + + if (player.getGuild().getOwnedCity() == null){ + + if (player.getGuild().getNation().getOwnedCity() == null) + return null; + + if (player.getGuild().getNation().getOwnedCity().getTOL() == null) + return null; + + returnBuilding = player.getGuild().getNation().getOwnedCity().getTOL(); + player.setBindBuildingID(returnBuilding.getObjectUUID()); + return returnBuilding; + } + + if (player.getGuild().getOwnedCity().getTOL() == null) + return null; + + returnBuilding = player.getGuild().getOwnedCity().getTOL(); + return returnBuilding; + } + + public AbstractGameObject getLastTarget() { + if (this.lastTargetType == GameObjectType.unknown) + return null; + + switch (this.lastTargetType) { + // Make sure these only return an object that is + // already in the GOM, and doesn't reload from the DB + case PlayerCharacter: + return DbManager.getFromCache(GameObjectType.PlayerCharacter, this.lastTargetID); + + case Building: + return DbManager.getFromCache(GameObjectType.Building, this.lastTargetID); + + case NPC: + return NPC.getFromCache(this.lastTargetID); + + case Mob: + return Mob.getFromCache(this.lastTargetID); + + case Item: + return DbManager.getFromCache(GameObjectType.Item, this.lastTargetID); + + case Corpse: + return DbManager.getFromCache(GameObjectType.Corpse, this.lastTargetID); + + default: + + // Ignore exception for MobLoot? ***Check + if (this.lastTargetType != GameObjectType.MobLoot) + Logger.error( "getLastTarget() unhandled object type: " + + this.lastTargetType.toString()); + } + return null; + } + + public Vector3fImmutable getLastStaticLoc() { + return this.lastStaticLoc; + } + + public void setLastStaticLoc(Vector3fImmutable value) { + this.lastStaticLoc = value; + } + + public int getHidden() { + return this.hidden; + } + + public void setHidden(int value) { + this.hidden = value; + } + + public int getSeeInvis() { + if (this.getDebug(8)) //<-added for see invis debug devcmd + return 10000; + return this.seeInvis; + } + + public void setSeeInvis(int value) { + this.seeInvis = value; + } + + public long getLastPlayerAttackTime() { + return this.lastPlayerAttackTime; + } + + public void setLastPlayerAttackTime() { + this.lastPlayerAttackTime = System.currentTimeMillis(); + } + + public void setLastMobAttackTime() { + this.lastMobAttackTime = System.currentTimeMillis(); + } + + public void setLastUsedPowerTime() { + this.lastUsedPowerTime = System.currentTimeMillis(); + } + + public void setLastTargetOfUsedPowerTime() { + this.lastTargetOfUsedPowerTime = System.currentTimeMillis(); + } + + public void setLastNPCDialog(NPC value) { + this.lastNPCDialog = value; + } + + public NPC getLastNPCDialog() { + return this.lastNPCDialog; + } + + public void setLastContract(int value) { + this.lastContract = value; + } + + public void setPet(Mob mob) { + + if (mob == null) + return; + + this.pet = mob; + } + + public Mob getPet() { + return this.pet; + } + + public Mob getNecroPet(int i) { + return this.necroPets.get(i); + } + + +public static void auditNecroPets(PlayerCharacter player){ + int removeIndex =0; + while(player.necroPets.size() >= 10){ + + + if (removeIndex == player.necroPets.size()) + break; + + Mob toRemove = player.necroPets.get(removeIndex); + + if (toRemove == null){ + removeIndex++; + continue; + } + toRemove.dismissNecroPet(true); + player.necroPets.remove(toRemove); + removeIndex++; + + + } +} + +public static void resetNecroPets(PlayerCharacter player){ + for (Mob necroPet: player.necroPets) + if (necroPet.isPet()) + necroPet.setMob(); +} + + public void spawnNecroPet(Mob mob) { + if (mob == null) + return; + if (mob.getMobBaseID() != 12021 && mob.getMobBaseID() != 12022) + return; + + PlayerCharacter.auditNecroPets(this); + PlayerCharacter.resetNecroPets(this); + + this.necroPets.add(mob); + } + + + public void dismissPet() { + if (this.pet != null) { + this.pet.dismiss(); + this.pet = null; + } + } + +public void dismissNecroPets() { + + + if (this.necroPets.isEmpty()) + return; + + for (Mob necroPet: this.necroPets){ + + try{ + necroPet.dismissNecroPet(true); + }catch(Exception e){ + necroPet.setState(STATE.Disabled); + Logger.error(e); + } + } + this.necroPets.clear(); + } + + + //called to verify player has correct item equipped for casting. + public boolean validEquip(int slot, String type) { + + if (this.charItemManager == null) + return false; + + Item item = this.charItemManager.getEquipped(slot); + + if (item == null) + return false; + + ItemBase ib = item.getItemBase(); + if (ib != null) { + + if ((ib.getType().equals(ItemType.WEAPON)) + && (ib.getSkillRequired().equals(type) || ib.getMastery().equals(type))) + return true; + + return (ib.getType().equals(ItemType.ARMOR)) + && (ib.getSkillRequired().equals(type)); + } + + return false; + } + + public short getPCLevel() { + short level = (short) Experience.getLevel(this.exp); + if (this.promotionClass == null && level >= 10) + return (short) 10; + + if (this.overFlowEXP > 0) + return this.level; + + return level; + } + + @Override + public float getSpeed() { + + float speed; + + if (this.getAltitude() > 0) + if (this.walkMode) { + speed = race.getRaceType().getRunSpeed().getFlyWalk(); + } + else { + speed = race.getRaceType().getRunSpeed().getFlyRun(); + } + else if (this.lastSwimming == true) + speed = MBServerStatics.SWIMSPEED; + else + if (this.walkMode) { + if (this.isCombat()) + speed = race.getRaceType().getRunSpeed().getWalkCombat(); + else + speed = race.getRaceType().getRunSpeed().getWalkStandard(); + } + else { + if (this.isCombat()) + speed = race.getRaceType().getRunSpeed().getRunCombat(); + else + speed = race.getRaceType().getRunSpeed().getRunStandard(); + } + + float endSpeed = speed * this.speedMod; + + if (endSpeed > 41 && !this.isCSR) + endSpeed = 41; + + return endSpeed; + } + + public synchronized void grantXP(int xp) { + // Stop players from getting experience past the cap + if (this.exp + xp >= Experience.getBaseExperience(MBServerStatics.LEVELCAP)) + xp = Experience.getBaseExperience(MBServerStatics.LEVELCAP) - this.exp + 1; + + if (xp == 0) + xp = 1; + + boolean isNewLevel = false; + boolean charReloadRequired = false; + int remainingXP = xp; + int neededXP = 0; + + // handle players that have not yet promoted. + ClientConnection origin = this.getClientConnection(); + + //not promoted at level 10, start checking for negative EXP + if (this.promotionClass == null && this.getLevel() == 10) { + + if (this.getExp() == Experience.getBaseExperience(11)){ + if (this.overFlowEXP == 110000) + return; + + if (this.overFlowEXP + xp > 110000){ + remainingXP = 110000 - this.overFlowEXP; + this.overFlowEXP = 110000; + + + } + else{ + this.overFlowEXP += remainingXP; + } + + GrantExperienceMsg gem = new GrantExperienceMsg(this, remainingXP); + Dispatch dispatch = Dispatch.borrow(this, gem); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + this.addDatabaseJob("EXP", MBServerStatics.FIVE_MINUTES); + return; + //didnt reach level 11 EXP to start overflow, add exp normally till we get here; + }else{ + + //Player exp begins negative exp, add remaing exp after level 11 to overflow + if (this.getExp() + remainingXP >= Experience.getBaseExperience(11)){ + + this.overFlowEXP = remainingXP - (Experience.getBaseExperience(11) - this.getExp()); + this.exp = Experience.getBaseExperience(11); + + GrantExperienceMsg grantExperienceMsg = new GrantExperienceMsg(this, remainingXP); + Dispatch dispatch = Dispatch.borrow(this, grantExperienceMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + this.addDatabaseJob("EXP", MBServerStatics.FIVE_MINUTES); + return; + + //didnt reach negative exp yet, just do normal exp gain. + }else{ + this.exp += remainingXP; + GrantExperienceMsg grantExperienceMsg = new GrantExperienceMsg(this, remainingXP); + remainingXP = 0; + Dispatch dispatch = Dispatch.borrow(this, grantExperienceMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + this.addDatabaseJob("EXP", MBServerStatics.FIVE_MINUTES); + return; + } + } + } + + if (this.overFlowEXP > 0){ + + int nextLevel; + + if (level == 10) + nextLevel = 12; + else + nextLevel = level + 2; + + int nextLevelEXP = Experience.getBaseExperience(nextLevel); + + // if overflow > 0, u have level 11 experience + overflow, but level is still 10 due to just promoting. + //Use level + 2 experience for next level. + this.overFlowEXP += 1; + + if (this.getExp() + this.overFlowEXP >= nextLevelEXP){ + + int expToNextLevel = nextLevelEXP - this.getExp(); + this.overFlowEXP -= expToNextLevel; + this.exp += expToNextLevel; + this.level++; + charReloadRequired = true; + + GrantExperienceMsg grantExperienceMsg = new GrantExperienceMsg(this, 1); + Dispatch dispatch = Dispatch.borrow(this, grantExperienceMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + SetObjectValueMsg upm = new SetObjectValueMsg(this, 9); + DispatchMessage.dispatchMsgToInterestArea(this, upm, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); + checkGuildStatus(); + this.addDatabaseJob("EXP", MBServerStatics.FIVE_MINUTES); + // double overflow exp used up, remaining overflow will just add to level + 1. + }else if (this.getExp() + this.overFlowEXP >= Experience.getBaseExperience(level + 1)){ + int nextExperience = Experience.getBaseExperience(level + 1) + this.overFlowEXP; + this.exp = nextExperience; + this.level ++; + charReloadRequired = true; + this.overFlowEXP = 0; + GrantExperienceMsg grantExperienceMsg = new GrantExperienceMsg(this, 1); + Dispatch dispatch = Dispatch.borrow(this, grantExperienceMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + SetObjectValueMsg upm = new SetObjectValueMsg(this, 9); + DispatchMessage.dispatchMsgToInterestArea(this, upm, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); + checkGuildStatus(); + this.addDatabaseJob("EXP", MBServerStatics.FIVE_MINUTES); + } + + }else{ + // Hand out each Level one at a time. + isNewLevel = Experience.getLevel(exp + remainingXP) > this.getLevel(); + + if (isNewLevel) { + neededXP = Experience.getBaseExperience(this.getLevel() + 1) - this.exp; + + charReloadRequired = true; + this.exp += neededXP; + this.level++; + + GrantExperienceMsg grantExperienceMsg = new GrantExperienceMsg(this, neededXP); + Dispatch dispatch = Dispatch.borrow(this, grantExperienceMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + remainingXP -= neededXP; + + //Send newLevel. + SetObjectValueMsg upm = new SetObjectValueMsg(this, 9); + DispatchMessage.dispatchMsgToInterestArea(this, upm, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); + checkGuildStatus(); + } else { + + this.exp += remainingXP; + GrantExperienceMsg grantExperienceMsg = new GrantExperienceMsg(this, remainingXP); + remainingXP = 0; + Dispatch dispatch = Dispatch.borrow(this, grantExperienceMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + + this.addDatabaseJob("EXP", MBServerStatics.FIVE_MINUTES); + } + } + + if (charReloadRequired) { + this.update(); + this.incVer(); + this.recalculate(); + this.calculateMaxHealthManaStamina(); + this.setHealth(this.healthMax); + this.mana.set(this.manaMax); + this.stamina.set(this.staminaMax); + //LoadJob.reloadCharacter(this); + DbManager.PlayerCharacterQueries.SET_PROPERTY(this, "char_experience", this.exp); + // updateDatabase(); + DbManager.AccountQueries.INVALIDATE_LOGIN_CACHE(this.getObjectUUID(), "character"); + } + } + + //This checks if a player meets the requirements to be in current guild. + public void checkGuildStatus() { + + Guild g = this.guild; + + if (g == null || g.isErrant() || GuildStatusController.isGuildLeader(guildStatus)) + return; + + //check level + int curLevel = (int) getPCLevel(); + if (curLevel < g.getRepledgeMin() || curLevel >= g.getRepledgeKick()) { + //TODO kick from guild + g.removePlayer(this,GuildHistoryType.LEAVE); + ChatManager.chatGuildInfo(this, "You no longer meet the level requirements for the guild."); + } + } + + public void calculateSpeedMod() { + // get base race speed modifer + + + //this is retarded. *** Refactor + // if (this.race != null) { + // int ID = this.race.getObjectUUID(); + // if (ID == 2004 || ID == 2005) + // this.raceRunMod = 1.21f; // centaur run bonus 22% + //// else if (ID == 2017) + //// this.raceRunMod = 1.14f; // mino run bonus 15% + // else + // this.raceRunMod = 1; + // } else + // this.raceRunMod = 1; + + + float bonus = 1f; + + // // TODO: hardcoded, as didnt have time to introduce DB column to base object + // if (baseClass.getName().equals("Fighter") || baseClass.getName().equals("Rogue")) + // bonus += .05f; + + // get running skill + if (this.skills != null) { + CharacterSkill running = this.skills.get("Running"); + if (running != null){ + + float runningBonus = (float) (Math.log(Math.round(running.getModifiedAmount())*.01f) / Math.log(2) *.50f); + runningBonus = (float) (Math.pow(2, runningBonus) - 1); + runningBonus += 1; + runningBonus *= .25f; + bonus += runningBonus; + + } + } + + if (this.bonuses != null) + // get rune and effect bonuses + bonus += this.bonuses.getFloatPercentNullZero(ModType.Speed, SourceType.None); + + // TODO get equip bonus + this.update(); + this.speedMod = bonus; + } + + public ClientConnection getClientConnection() { + return SessionManager.getClientConnection(this); + } + + /* + * Serializing + */ + + public static void __serializeForClientMsg(PlayerCharacter playerCharacter,ByteBufferWriter writer) throws SerializationException { + serializeForClientCommon(playerCharacter,writer, true, false, false, false); + } + + public static void serializeForClientMsgLogin(PlayerCharacter playerCharacter,ByteBufferWriter writer) throws SerializationException { + serializeForClientCommon(playerCharacter,writer, true, false, false, false); + } + + public static void serializeForClientMsgCommit(PlayerCharacter playerCharacter,ByteBufferWriter writer) throws SerializationException { + serializeForClientCommon(playerCharacter,writer, true, true, false, false); + } + + public static void serializeForClientMsgFull(PlayerCharacter playerCharacter,ByteBufferWriter writer) throws SerializationException { + serializeForClientCommon(playerCharacter,writer, false, false, false, false); + } + + + public static void serializeForClientMsgOtherPlayer(PlayerCharacter playerCharacter,ByteBufferWriter writer) throws SerializationException { + serializeForClientCommon(playerCharacter,writer, false, false, true, false); + } + + + public static void serializePlayerForClientMsgOtherPlayer(PlayerCharacter playerCharacter,ByteBufferWriter writer, boolean hideAsciiLastName) throws SerializationException { + serializeForClientCommon(playerCharacter,writer, false, false, true, hideAsciiLastName); + } + + // TODO what is a Fresh Char? + private static void serializeForClientCommon(PlayerCharacter playerCharacter,ByteBufferWriter writer, boolean loginData, boolean freshChar, boolean otherPlayer, boolean hideAsciiLastName) + throws SerializationException { + + /* + * RUNES + */ + // Handle Applied Runes + writer.putInt(0); // Pad + writer.putInt(0); // Pad + + // Put number of runes + //We need to send all runes to everyone, otherwise playerCharacter will cause major issues + if (playerCharacter.promotionClass != null) + writer.putInt(playerCharacter.runes.size() + 3); + else + writer.putInt(playerCharacter.runes.size() + 2); + + // Cant forget that Race and baseClass are technically Runes :0 + if (playerCharacter.subRaceID != 0){ + writer.putInt(1); // For Race + writer.putInt(0); // Pad + writer.putInt(playerCharacter.subRaceID); + + writer.putInt(Enum.GameObjectType.Race.ordinal()); + writer.putInt(playerCharacter.subRaceID); + }else + playerCharacter.race.serializeForClientMsg(writer); + if (playerCharacter.promotionClass != null) { + BaseClass.serializeForClientMsg(playerCharacter.baseClass,writer, 2); + PromotionClass.serializeForClientMsg(playerCharacter.promotionClass,writer); + } else + BaseClass.serializeForClientMsg(playerCharacter.baseClass,writer, 3); + + // Put runes. + + for (CharacterRune rb : playerCharacter.runes) { + CharacterRune.serializeForClientMsg(rb,writer); + } + + /* + * STATS + */ + // Number of Stats to follow + writer.putInt(5); + + writer.putInt(MBServerStatics.STAT_STR_ID); // Strength ID + writer.putInt(freshChar ? 0 : playerCharacter.getStrMod()); + + writer.putInt(MBServerStatics.STAT_SPI_ID); // Spirit ID + writer.putInt(freshChar ? 0 : playerCharacter.getSpiMod()); + + writer.putInt(MBServerStatics.STAT_CON_ID); // Constitution ID + writer.putInt(freshChar ? 0 : playerCharacter.getConMod()); + + writer.putInt(MBServerStatics.STAT_DEX_ID); // Dexterity ID + writer.putInt(freshChar ? 0 : playerCharacter.getDexMod()); + + writer.putInt(MBServerStatics.STAT_INT_ID); // Intelligence ID + writer.putInt(freshChar ? 0 : playerCharacter.getIntMod()); + + // Handle Info + playerCharacter.title._serializeFirstName(writer, playerCharacter.firstName); + playerCharacter.title._serializeLastName(writer, playerCharacter.lastName, hideAsciiLastName, playerCharacter.asciiLastName); + + // Unknown + writer.putInt(0); + + writer.putString(ConfigManager.MB_WORLD_NAME.getValue()); + writer.putInt(MBServerStatics.worldMapID); + + writer.put((byte) 1); // End Datablock byte + writer.putInt(0); // Unsure, Pad? + writer.putInt(playerCharacter.getObjectType().ordinal()); + writer.putInt(playerCharacter.getObjectUUID()); + + // Perhaps playerCharacter is loc and the next 3 are Facing dir? + writer.putFloat(1); // Unknown + writer.putFloat(playerCharacter.race.getRaceType().getScaleHeight()); // Unknown + writer.putFloat(1); // Unknown + + writer.putVector3f(playerCharacter.getLoc()); + writer.putFloat(playerCharacter.faceDir.getRotation()); // Rotation, direction + + // facing + + // Running trains. + + if (otherPlayer){ + CharacterSkill runSkill = playerCharacter.skills.get("Running"); + if (runSkill == null) + // Logger.log.log( + // LogEventType.WARNING, + // "Failed to find the 'Running Skill' when serializing PlayerCharacter '" + // + playerCharacter.getCombinedName() + "'"); + // TODO put int=0 for now. + writer.putInt(0); + else + writer.putInt(runSkill.getNumTrains()); + }else + writer.putInt(0); + + + ArrayList equipped = playerCharacter.charItemManager.getEquippedList(); + + writer.putInt(equipped.size()); + for (Item item: equipped){ + Item._serializeForClientMsg(item, writer); + } + writer.putInt(playerCharacter.getRank()); + + writer.putInt(playerCharacter.getLevel()); + if (loginData) + writer.putInt(5); + else + writer.putInt(playerCharacter.getIsSittingAsInt()); // 5 + writer.putInt(playerCharacter.getIsWalkingAsInt()); // 1 + writer.putInt(playerCharacter.getIsCombatAsInt()); // 1 + writer.putInt(playerCharacter.getIsFlightAsInt()); // 2 or 3 + + writer.putInt(playerCharacter.getIsLfGroupAsInt()); // 1 + + // if (loginData) + // writer.putInt(0); + // else + writer.putInt(playerCharacter.getHeadlightsAsInt()); + + + if (playerCharacter.getRegion() != null && !loginData){ + Building building = Regions.GetBuildingForRegion(playerCharacter.getRegion()); + + if (building == null){ + writer.putInt(0); + writer.putInt(0); + }else{ + writer.putInt(GameObjectType.Building.ordinal()); + writer.putInt(building.getObjectUUID()); + } + + } + else{ + writer.putInt(0); + writer.putInt(0); + } + + + writer.put((byte)0); + writer.put((byte)0); + writer.put((byte)0); + writer.putInt(0); + writer.put((byte)0); + writer.put((byte)0); + writer.put((byte)0); + +// writer.putInt(0); +// writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + + if (!playerCharacter.isAlive() && otherPlayer) { + writer.putInt(0); + writer.putInt(0); + } + + + + + //TODO FIGURE OUT THE REAL SEARLIZATION FOR NEXT 2 SHORTS? + writer.putInt(playerCharacter.skinColor); // Skin Color + writer.putFloat(20); + writer.put((byte)0); //Unknown + + //unknown object + writer.putInt(0); + writer.putInt(0); + + //unknown type + writer.putInt(0); + //0x4080 should be the next short here, instead it wraps 0's down their in for loops.. seriously.. who wrote playerCharacter shit. + // playerCharacter aint right! + // ByteBufferUtils.putString(writer, playerCharacter.guild.getName()); + // writer.putInt(playerCharacter.getGuild().getUUID()); + // ByteBufferUtils.putString(writer, playerCharacter.guild.getNation().getName()); + // writer.putInt(playerCharacter.getGuild().getNation().getUUID()); + Guild.serializeForClientMsg(playerCharacter.getGuild(),writer, playerCharacter, false); + + //Send Tokens for race/class/promotion (disciplines?) + if (playerCharacter.promotionClass != null) + writer.putInt(3); + else + writer.putInt(2); + writer.putInt(playerCharacter.race.getToken()); + writer.putInt(playerCharacter.baseClass.getToken()); + if (playerCharacter.promotionClass != null) + writer.putInt(playerCharacter.promotionClass.getToken()); + // writer.putInt(2); // Unknown Counter + // writer.putInt(0x04C1BE88); // Unknown + // writer.putInt(0x0F651512); // Unknown + + writer.putFloat(playerCharacter.altitude); // altitude? + writer.putFloat(playerCharacter.altitude); // altitude? + writer.put((byte) 0); // End Datablock byte + + writer.putFloat(playerCharacter.healthMax); + writer.putFloat(playerCharacter.health.get()); + + writer.put((byte) 0); // End Datablock byte + //size + + + if (loginData){ + writer.putInt(0); + }else{ + int indexPosition = writer.position(); + writer.putInt(0); //placeholder for item cnt + int total = 0; + // Logger.info("",""+ playerCharacter.getEffects().size()); + for (Effect eff : playerCharacter.getEffects().values()) { + if (eff.getPower() == null && otherPlayer) + continue; + if ( !eff.serializeForLoad(writer)) + continue; + ++total; + + } + + writer.putIntAt(total, indexPosition); + } + + if (otherPlayer) { + writer.put((byte) 0); // End Datablock Byte + return; + + } + + //made up for sendalleffects + //writer.putInt(0); // Pad + //writer.put((byte) 0); // End Datablock byte + writer.putInt(playerCharacter.getUnusedStatPoints()); + writer.putInt(playerCharacter.getLevel()); + writer.putInt(playerCharacter.getExp() + playerCharacter.overFlowEXP); + writer.putFloat(playerCharacter.manaMax); + writer.putFloat(playerCharacter.mana.get()); + writer.putFloat(playerCharacter.staminaMax); + writer.putFloat(playerCharacter.stamina.get()); + writer.putInt(playerCharacter.getAtrHandOne()); + writer.putInt(playerCharacter.getAtrHandTwo()); + writer.putInt(playerCharacter.getDefenseRating()); + + if (MBServerStatics.POWERS_DEBUG) //debug mode, grant lots of trains + writer.putInt(1000); + else + writer.putInt(playerCharacter.trainsAvailable.get()); + + /* + * Skills + */ + if (loginData) + writer.putInt(0); // Skip skills + else { + writer.putInt(playerCharacter.skills.size()); + Iterator it = playerCharacter.skills.keySet().iterator(); + while (it.hasNext()) { + String name = it.next(); + CharacterSkill.serializeForClientMsg(playerCharacter.skills.get(name),writer); + } + } + + /* + * Powers + */ + if (loginData) + writer.putInt(0); // Skip Powers + else + if (MBServerStatics.POWERS_DEBUG) //debug mode, grant all powers + PowersManager.testPowers(writer); + else { + writer.putInt(playerCharacter.powers.size()); + for (CharacterPower sp : playerCharacter.powers.values()) { + CharacterPower.serializeForClientMsg(sp,writer); + } + } + + /* + * Inventory + */ + if (loginData) { + writer.putInt(0); // Skip Inventory + writer.putInt(playerCharacter.getInventoryCapacity()); // Inventory Capacity + + } else { + ArrayList inv = playerCharacter.charItemManager.getInventory(true); + Item.putList(writer, inv, false, playerCharacter.getObjectUUID()); + writer.putInt(playerCharacter.getInventoryCapacityRemaining()); + } + + /* + * Bank + */ + if (loginData) { + writer.putInt(0); // Skip Bank + writer.putInt(AbstractCharacter.getBankCapacity()); // Bank Capacity + + } else { + ArrayList bank = playerCharacter.charItemManager.getBank(); + + Item.putList(writer, bank, false, playerCharacter.getObjectUUID()); + writer.putInt(playerCharacter.getBankCapacityRemaining()); + } + //load player friends. + if (loginData) + writer.putInt(0); + else{ + HashSet friendMap = PlayerFriends.PlayerFriendsMap.get(playerCharacter.getObjectUUID()); + if (friendMap == null) + writer.putInt(0); + else{ + writer.putInt(friendMap.size()); + for (int friendID : friendMap){ + PlayerCharacter friend = PlayerCharacter.getFromCache(friendID); + //shouldn't get here, but if null serialize blank friend. + if (friend == null){ + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + }else{ + writer.putInt(friend.getObjectType().ordinal()); + writer.putInt(friend.getObjectUUID()); + writer.putString(friend.getName()); + boolean online = SessionManager.getPlayerCharacterByID(friend.getObjectUUID()) != null ? true : false; + writer.putInt(online ? 0 : 1); + writer.putInt(friend.friendStatus.ordinal()); + } + + } + } + } + + + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + writer.putInt(0); + + writer.putShort((short) 0); + writer.put((byte) 0); + // playerCharacter is for send self in enter world (full character) + if (!loginData && !freshChar) { + int size = playerCharacter.getRecycleTimers().size(); + writer.putInt(size); + if (size > 0) + for (int token : playerCharacter.getRecycleTimers().keySet()) { + + JobContainer frtj = playerCharacter.getRecycleTimers().get(token); + long timeLeft = frtj.timeOfExection() - System.currentTimeMillis(); + writer.putInt(token); + writer.putInt((int) timeLeft / 1000); + } + DateTime enterWorld = new DateTime(playerCharacter.timestamps.get("EnterWorld")); + writer.putDateTime(enterWorld); + + writer.putInt(0x49EF1E98); //DUnno what playerCharacter is. + writer.putFloat(DateTime.now().hourOfDay().get()); //daylight in float. + writer.putFloat(6); //interval of light to change per game hour //float + //writer.putInt(1637194901); //playerCharacter is actually an opcode taht is in recordings, no clue what it is, dumped it and it changes nothing + } else { + writer.put((byte) 0); //added to compensate the cooldown check. + + //add server up or down + int serverUp = LoginServer.worldServerRunning ? 1 : 0; + + if (playerCharacter.account == null) + serverUp = 0; + + if ((playerCharacter.account.status.equals(AccountStatus.ADMIN) == false) && + (playerCharacter.account.status.equals(MBServerStatics.worldAccessLevel) == false)) + serverUp = 0; + + writer.putInt(serverUp); + writer.putInt(0); // effects, not sure used by players + writer.put((byte) 0); // End Player Datablock + } + + } + + + + public static PlayerCharacter generatePCFromCommitNewCharacterMsg(Account a, CommitNewCharacterMsg msg, ClientConnection clientConnection) + { + + String firstName = msg.getFirstName().trim(); + String lastName = msg.getLastName().trim(); + + if (firstName.length() < 3){ + LoginServerMsgHandler.sendInvalidNameMsg(firstName, lastName, MBServerStatics.INVALIDNAME_FIRSTNAME_MUST_BE_LONGER, + clientConnection); + return null; + } + + // Ensure names are below required length + if (firstName.length() > 15 || lastName.length() > 15){ + LoginServerMsgHandler.sendInvalidNameMsg(firstName, lastName, MBServerStatics.INVALIDNAME_FIRSTANDLAST_MUST_BE_SHORTER, + clientConnection); + return null; + } + + // Check if firstname is valid + if (MiscUtils.checkIfFirstNameInvalid(firstName)){ + LoginServerMsgHandler.sendInvalidNameMsg(firstName, lastName, MBServerStatics.INVALIDNAME_PLEASE_CHOOSE_ANOTHER_FIRSTNAME, + clientConnection); + return null; + } + + // Check if last name is valid + if (MiscUtils.checkIfLastNameInvalid(lastName)){ + LoginServerMsgHandler.sendInvalidNameMsg(firstName, lastName, MBServerStatics.INVALIDNAME_LASTNAME_UNAVAILABLE, + clientConnection); + return null; + } + + // Verify Race + int raceID = msg.getRace(); + + Race race = Race.getRace(raceID); + + if (race == null) { + Logger.info("Invalid RaceID: " + raceID); + return null; + } + + // Verify BaseClass Object. + int baseClassID = msg.getBaseClass(); + BaseClass baseClass = DbManager.BaseClassQueries.GET_BASE_CLASS(baseClassID); + + if (baseClass == null) { + Logger.info("Invalid BaseClasID: " + baseClassID); + return null; + } + + // Verify Race/baseClass combo. + boolean valid = false; + + for (BaseClass bc : race.getValidBaseClasses()) { + + if (bc.getObjectUUID() == baseClassID) { + valid = true; + break; + } + } + + if (!valid) { + Logger.info("Invalid BaseClass/Race Combo"); + return null; + } + + // Verify HairStyle/BeardStyle/SkinColor/HairColor/BeardColor + int hairStyleID = msg.getHairStyle(); + int beardStyleID = msg.getBeardStyle(); + int skinColorID = msg.getSkinColor(); + int hairColorID = msg.getHairColor(); + int beardColorID = msg.getBeardColor(); + + if (!race.isValidHairStyle(hairStyleID)) { + Logger.info("Invalid HairStyleID: " + hairStyleID + " for race: " + race.getName()); + return null; + } + + if (!race.isValidSkinColor(skinColorID)) { + Logger.info("Invalid skinColorID: " + skinColorID + " for race: " + race.getName()); + return null; + } + + if (!race.isValidHairColor(hairColorID)) { + Logger.info("Invalid hairColorID: " + hairColorID + " for race: " + race.getName()); + return null; + } + + if (!race.isValidBeardColor(beardColorID)) { + Logger.info("Invalid beardColorID: " + beardColorID + " for race: " + race.getName()); + return null; + } + + // Get stat modifiers + int strMod = msg.getStrengthMod(); + int dexMod = msg.getDexterityMod(); + int conMod = msg.getConstitutionMod(); + int intMod = msg.getIntelligenceMod(); + int spiMod = msg.getSpiritMod(); + + + if (intMod < -5 || dexMod < -5 || conMod < -5 || strMod <-5 || spiMod < -5) { + Logger.error("NEGATIVE STAT CHEAT ATTEMPTED! ACCOUNT: " +a.getUname() + "(" + a.getObjectUUID() + ") IP ADDRESS: " +clientConnection.getClientIpAddress()); + return null; + } + + // calculate current stats: + short strCur = (short) (race.getStrStart() + baseClass.getStrMod() + strMod); + short dexCur = (short) (race.getDexStart() + baseClass.getDexMod() + dexMod); + short conCur = (short) (race.getConStart() + baseClass.getConMod() + conMod); + short intCur = (short) (race.getIntStart() + baseClass.getIntMod() + intMod); + short spiCur = (short) (race.getSpiStart() + baseClass.getSpiMod() + spiMod); + + // calculate max stats: + short strMax = race.getStrMax(); + short dexMax = race.getDexMax(); + short conMax = race.getConMax(); + short intMax = race.getIntMax(); + short spiMax = race.getSpiMax(); + + // Verify not too many runes applied + int numRunes = msg.getNumRunes(); + + if (numRunes > 16) { + Logger.info("Too many Runes applied"); + return null; + } + + // Get Runes + // ArrayList characterRunesUsed = new ArrayList(); + // ArrayList subtypesUsed = new ArrayList(); + int remainingPoints = race.getStartingPoints() - strMod - dexMod - conMod - intMod - spiMod; + + int[] characterRunes = msg.getRunes(); + + HashSet usedRunesSubType = new HashSet<>(); + HashSet usedRunes = new HashSet<>(); + + // So that all the penalties can be added at the end. + ConcurrentHashMap penalties = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + + penalties.put("StrCur", 0); + penalties.put("StrMax", 0); + penalties.put("DexCur", 0); + penalties.put("DexMax", 0); + penalties.put("ConCur", 0); + penalties.put("ConMax", 0); + penalties.put("IntCur", 0); + penalties.put("IntMax", 0); + penalties.put("SpiCur", 0); + penalties.put("SpiMax", 0); + + PriorityQueue> orderedRunes = new PriorityQueue<>(14, + new Comparator>() { + + @Override + public int compare(Entry o1, Entry o2) { + return o1.getKey() - o2.getKey(); + } + }); + + // Figure out which Runes we are adding. + for (int i : characterRunes) { + // Zero skip + if (i == 0) + continue; + + // Skip the Race and BaseClass runes... already dealt with. + if (i == raceID || i == baseClassID) + continue; + + RuneBase runeBase = RuneBase.getRuneBase(i); + + // Null check + if (runeBase == null) { + Logger.info("GOM returned NULL RuneBase"); + return null; + } + + // Validate Rune against Race + if (!race.isAllowedRune(runeBase)) { + Logger.info("Trait Not valid for Race"); + return null; + } + + // Validate BaseClass against Race + if (!baseClass.isAllowedRune(runeBase)) { + Logger.info("Trait Not valid for BaseClass"); + return null; + } + + int previous_size = usedRunes.size(); + int previous_subtype = usedRunesSubType.size(); + + usedRunes.add(runeBase); + usedRunesSubType.add(runeBase.getSubtype()); + + // Duplicate Rune check + if (usedRunes.size() <= previous_size) { + Logger.info("Duplicate RuneBase"); + return null; + } + + // Duplicate Subtype check + if (runeBase.getSubtype() != 0 && usedRunesSubType.size() <= previous_subtype) { + Logger.info("Duplicate RuneBase Subtype"); + return null; + } + + int maxValue = 0; + + // Every attempt is made to load MIN_NEEDED_ATTRIBUTES first. + + if (runeBase.getAttrs() != null) + for (RuneBaseAttribute rba : runeBase.getAttrs()) { + if (rba.getAttributeID() == MBServerStatics.RUNE_STR_MIN_NEEDED_ATTRIBUTE_ID + || rba.getAttributeID() == MBServerStatics.RUNE_DEX_MIN_NEEDED_ATTRIBUTE_ID + || rba.getAttributeID() == MBServerStatics.RUNE_CON_MIN_NEEDED_ATTRIBUTE_ID + || rba.getAttributeID() == MBServerStatics.RUNE_INT_MIN_NEEDED_ATTRIBUTE_ID + || rba.getAttributeID() == MBServerStatics.RUNE_SPI_MIN_NEEDED_ATTRIBUTE_ID) { + maxValue = rba.getModValue(); + if (runeBase.getName().equals("Giant's Blood")) + maxValue = 45; // Take care of the Giant's Blood special + // case. + break; + } + } + + orderedRunes.add(new AbstractMap.SimpleEntry<>(maxValue, runeBase)); + } + + while (orderedRunes.size() > 0) { + RuneBase rb = orderedRunes.remove().getValue(); + ArrayList attrs = rb.getAttrs(); + + if (attrs != null) + for (RuneBaseAttribute abr : attrs) { + + int attrID = abr.getAttributeID(); + int value = abr.getModValue(); + + switch (attrID) { + case MBServerStatics.RUNE_COST_ATTRIBUTE_ID: + + Logger.info( "Bought " + rb.getName() + " for " + value + " points. " + + (remainingPoints - value) + " left."); + + if ((remainingPoints - value) >= 0) { + remainingPoints -= value; + continue; + } + Logger.info("Not enough points left"); + return null; + case MBServerStatics.RUNE_STR_MIN_NEEDED_ATTRIBUTE_ID: + + if (strCur >= value) + continue; + + Logger.info("STR fails to meet Rune Minimum --> " + rb.getName()); + return null; + case MBServerStatics.RUNE_DEX_MIN_NEEDED_ATTRIBUTE_ID: + + if (dexCur >= value) + continue; + + Logger.info("DEX fails to meet Rune Minimum --> " + rb.getName()); + return null; + case MBServerStatics.RUNE_CON_MIN_NEEDED_ATTRIBUTE_ID: + + if (conCur >= value) + continue; + + Logger.info("CON fails to meet Rune Minimum --> " + rb.getName()); + return null; + case MBServerStatics.RUNE_INT_MIN_NEEDED_ATTRIBUTE_ID: + + if (intCur >= value) + continue; + + Logger.info("INT fails to meet Rune Minimum --> " + rb.getName()); + return null; + case MBServerStatics.RUNE_SPI_MIN_NEEDED_ATTRIBUTE_ID: + + if (spiCur >= value) + continue; + + Logger.info("SPI fails to meet Rune Minimum --> " + rb.getName()); + return null; + case MBServerStatics.RUNE_STR_ATTRIBUTE_ID: + + if (value < 0) + penalties.put("StrCur", (penalties.get("StrCur") + value)); + else + strCur += value; + continue; + + case MBServerStatics.RUNE_DEX_ATTRIBUTE_ID: + if (value < 0) + penalties.put("DexCur", (penalties.get("DexCur") + value)); + else + dexCur += value; + continue; + case MBServerStatics.RUNE_CON_ATTRIBUTE_ID: + if (value < 0) + penalties.put("ConCur", (penalties.get("ConCur") + value)); + else + conCur += value; + continue; + case MBServerStatics.RUNE_INT_ATTRIBUTE_ID: + if (value < 0) + penalties.put("IntCur", (penalties.get("IntCur") + value)); + else + intCur += value; + continue; + case MBServerStatics.RUNE_SPI_ATTRIBUTE_ID: + if (value < 0) + penalties.put("SpiCur", (penalties.get("SpiCur") + value)); + else + spiCur += value; + continue; + case MBServerStatics.RUNE_STR_MAX_ATTRIBUTE_ID: + if (value < 0) + penalties.put("StrMax", (penalties.get("StrMax") + value)); + else + strMax += value; + continue; + case MBServerStatics.RUNE_DEX_MAX_ATTRIBUTE_ID: + if (value < 0) + penalties.put("DexMax", (penalties.get("DexMax") + value)); + else + dexMax += value; + continue; + case MBServerStatics.RUNE_CON_MAX_ATTRIBUTE_ID: + if (value < 0) + penalties.put("ConMax", (penalties.get("ConMax") + value)); + else + conMax += value; + continue; + case MBServerStatics.RUNE_INT_MAX_ATTRIBUTE_ID: + if (value < 0) + penalties.put("IntMax", (penalties.get("IntMax") + value)); + else + intMax += value; + continue; + case MBServerStatics.RUNE_SPI_MAX_ATTRIBUTE_ID: + if (value < 0) + penalties.put("SpiMax", (penalties.get("SpiMax") + value)); + else + spiMax += value; + continue; + + default: + Logger.info("Unknown ATTRIBUTE_ID while checking RuneBaseAttributes: " + attrID); + return null; + } + } + } + + // Add in all of the penalties. + strCur += penalties.get("StrCur"); + strMax += penalties.get("StrMax"); + dexCur += penalties.get("DexCur"); + dexMax += penalties.get("DexMax"); + conCur += penalties.get("ConCur"); + conMax += penalties.get("ConMax"); + intCur += penalties.get("IntCur"); + intMax += penalties.get("IntMax"); + spiCur += penalties.get("SpiCur"); + spiMax += penalties.get("SpiMax"); + + int kitID = msg.getKit(); + + // get the correctKit + int raceClassID = Kit.GetKitIDByRaceClass(raceID, baseClassID); + ArrayList allKits = Kit.RaceClassIDMap.get(raceClassID); + + Kit kit = null; + + for (Kit k : allKits) { + if (k.getKitNumber() == kitID) { + kit = k; + break; + } + } + + if (kit == null) { + Logger.info("Unable to find matching kitID: " + kitID); + return null; + } + + byte runningTrains = 0; + PlayerCharacter playerCharacter; + + //Synchronized block to allow exclusive access when confirming + //uniqueness of FirstName and subsequently saving the new record + //to the database with that FirstName + synchronized (FirstNameLock) { + // Test if FirstName already exists. + // This must be the very last check before calling the + // DB to create the character record + if (DbManager.PlayerCharacterQueries.IS_CHARACTER_NAME_UNIQUE(firstName) == false){ + LoginServerMsgHandler.sendInvalidNameMsg(firstName, lastName, MBServerStatics.INVALIDNAME_FIRSTNAME_UNAVAILABLE, + clientConnection); + return null; + } + + // Make PC + PlayerCharacter pcWithoutID = new PlayerCharacter( firstName, lastName, (short) strMod, (short) dexMod, (short) conMod, + (short) intMod, (short) spiMod, Guild.getErrantGuild(), runningTrains, a, race, baseClass, (byte) skinColorID, (byte) hairColorID, + (byte) beardColorID, (byte) beardStyleID, (byte) hairStyleID); + + try { + playerCharacter = DbManager.PlayerCharacterQueries.ADD_PLAYER_CHARACTER(pcWithoutID); + } catch (Exception e) { + Logger.error("generatePCFromCommitNewCharacterMsg", "An error occurred while saving new PlayerCharacter to DB", e); + return null; + } + + if (playerCharacter == null) { + Logger.info("GOM Failed to create PlayerCharacter"); + return null; + } + + } // END synchronized(FirstNameLock) + + // Add creation runes + for (RuneBase rb : usedRunes) { + CharacterRune runeWithoutID = new CharacterRune(rb, playerCharacter.getObjectUUID()); + CharacterRune characterRune; + try { + characterRune = DbManager.CharacterRuneQueries.ADD_CHARACTER_RUNE(runeWithoutID); + } catch (Exception e) { + characterRune = null; + } + + if (characterRune == null) { + playerCharacter.deactivateCharacter(); + Logger.info("GOM Failed to create CharacterRune"); + return null; + } + + playerCharacter.addRune(characterRune); + } + + if (hairStyleID != 0) { + // Create Hair + Item tempHair = new Item( ItemBase.getItemBase(hairStyleID), playerCharacter.getObjectUUID(), OwnerType.PlayerCharacter, + (byte) 0, (byte) 0, (short) 1, (short) 1, false, false, ItemContainerType.EQUIPPED, + (byte) MBServerStatics.SLOT_HAIRSTYLE, new ArrayList<>(),""); + + Item hair; + + try { + hair = DbManager.ItemQueries.ADD_ITEM(tempHair); + } catch (Exception e) { + hair = null; + } + + if (hair == null) { + playerCharacter.deactivateCharacter(); + Logger.info("GameObjectManager failed to create Hair:" + hairStyleID + " in Slot:" + + MBServerStatics.SLOT_HAIRSTYLE); + return null; + } + } + + if (beardStyleID != 0) { + // Create Beard + Item tempBeard = new Item( ItemBase.getItemBase(beardStyleID), playerCharacter.getObjectUUID(), OwnerType.PlayerCharacter, + (byte) 0, (byte) 0, (short) 1, (short) 1, false, false,ItemContainerType.EQUIPPED, + (byte) MBServerStatics.SLOT_BEARDSTYLE, new ArrayList<>(),""); + Item beard; + try { + beard = DbManager.ItemQueries.ADD_ITEM(tempBeard); + } catch (Exception e) { + beard = null; + } + + if (beard == null) { + playerCharacter.deactivateCharacter(); + Logger.info("GameObjectManager failed to create Beard:" + beardStyleID + " in Slot:" + + MBServerStatics.SLOT_BEARDSTYLE); + return null; + } + } + // Create items from Kit and equip on character. + try { + kit.equipPCwithKit(playerCharacter); + } catch (Exception e) { + Logger.info("Unable to find KIT ID for Race: " + raceID + "||" + "Class:" + baseClassID ); + playerCharacter.deactivateCharacter(); + return null; + } + + // Get any new skills that belong to the player + playerCharacter.calculateSkills(); + + a.setLastCharacter(playerCharacter.getObjectUUID()); + playerCharacter.charItemManager.load(); + + playerCharacter.activateCharacter(); + + return playerCharacter; + } + + public String getCombinedName() { + return this.getName(); + } + + public long getLastGuildToInvite() { + return this.lastGuildToInvite; + } + + public void setLastGuildToInvite(int value) { + this.lastGuildToInvite = value; + } + + public boolean getFollow() { + return this.follow; + } + + public boolean toggleFollow() { + this.follow = !this.follow; + return this.follow; + } + + public void setFollow(boolean value) { + this.follow = value; + } + + public int getLastGroupToInvite() { + return this.lastGroupToInvite; + } + + public void setLastGroupToInvite(int value) { + this.lastGroupToInvite = value; + } + + @Override + public float getAltitude() { + if (this.altitude < 0) + this.altitude = 0; + + //player has reached desired altitude, return normal altitude. + if (this.getTakeOffTime() == 0) + return this.altitude; + + //sanity check if desired altitude is the same as current altitude. return desired altitude. + if (this.altitude == this.getDesiredAltitude()){ + return this.getDesiredAltitude(); + } + + //calculate how much the player has moved up + float amountMoved = (System.currentTimeMillis() - this.getTakeOffTime()) * MBServerStatics.FLY_RATE; //FUCK DIVIDING + + //Player is moving up + if (this.getDesiredAltitude() > this.altitude){ + + //if amount moved passed desiredAltitude, return the desired altitude. + if (this.altitude + amountMoved >= this.getDesiredAltitude()) + return this.getDesiredAltitude(); + + return this.altitude + amountMoved; + //Player is moving down + }else{ + //if amount moved passed desiredAltitude, return the desired altitude. + if (this.altitude - amountMoved <= this.getDesiredAltitude()) + return this.getDesiredAltitude(); + return this.altitude - amountMoved; + } + + + + } + + public void setAltitude(float value) { + this.altitude = value; + } + + public HashSet getLoadedObjects() { + return this.loadedObjects; + } + + public HashSet getLoadedStaticObjects() { + return this.loadedStaticObjects; + } + + public void setLoadedStaticObjects(HashSet value) { + this.loadedStaticObjects = value; + } + + public void setTeleportMode(boolean teleportMode) { + this.teleportMode = teleportMode; + } + + public boolean isTeleportMode() { + return teleportMode; + } + + // public ConcurrentHashMap + // getRecycleTimers() { + // return this.recycleTimers; + // } + // public UsePowerJob getLastPower() { + // return this.lastPower; + // } + // public void setLastPower(UsePowerJob value) { + // this.lastPower = value; + // } + // public void clearLastPower() { + // this.lastPower = null; + // } + public long chatFloodTime(int chatOpcode, long chatTimeMilli, int qtyToSave) { + if (qtyToSave < 1) + return 0L; // disabled + LinkedList times = null; + long oldestTime; + synchronized (chatChanFloodList) { + if (!chatChanFloodList.containsKey(chatOpcode)) { + times = new LinkedList<>(); + for (int i = 0; i < qtyToSave; i++) { + times.add(0L); + } + chatChanFloodList.put(chatOpcode, times); + } else + times = chatChanFloodList.get(chatOpcode); + oldestTime = times.getLast(); + times.removeLast(); + times.addFirst(chatTimeMilli); + } + return oldestTime; + } + + public void addIgnoredPlayer(Account ac, String name) { + if (ac == null) + return; + int acID = ac.getObjectUUID(); + if (acID < 1) + return; + if (ignoredPlayerIDs == null) + return; + if (acID == getObjectUUID()) + return; // yourself + + ignoredPlayerIDs.put(acID, name); + } + + public static boolean isIgnoreListFull() { + return false; //Why were we setting a limit on ignores? - + //return (ignoredPlayerIDs.size() >= MBServerStatics.IGNORE_LIST_MAX); + } + + public void removeIgnoredPlayer(Account ac) { + if (ac == null) + return; + int acID = ac.getObjectUUID(); + if (acID < 1) + return; + if (ignoredPlayerIDs == null) + return; + if (acID == getObjectUUID()) + return; // yourself + + ignoredPlayerIDs.remove(acID); + } + + public boolean isIgnoringPlayer(PlayerCharacter pc) { + + if (pc == null) + return false; + + if (pc.account == null) + return false; + + return isIgnoringPlayer(pc.account); + } + + public boolean isIgnoringPlayer(Account ac) { + if (ac == null) + return false; + int acID = ac.getObjectUUID(); + if (acID < 1) + return false; + return ignoredPlayerIDs.containsKey(acID); + } + + public static boolean isIgnorable() { + return true; + // // if (account == null) return false; + // if (account.getAccessLevel() > 0) { + // return false; + // } + // return true; + } + + public String[] getIgnoredPlayerNames() { + int size = ignoredPlayerIDs.size(); + String[] ary = new String[size]; + for (int i = 0; i < size; i++) { + // ary[i] = PlayerCharacter.getFirstName(ignoredPlayerIDs.get(i)); + ary[i] = ignoredPlayerIDs.get(i); + } + return ary; + } + + public int getStrMod() { + return this.strMod.get(); + } + + public int getDexMod() { + return this.dexMod.get(); + } + + public int getConMod() { + return this.conMod.get(); + } + + public int getIntMod() { + return this.intMod.get(); + } + + public int getSpiMod() { + return this.spiMod.get(); + } + + public boolean isMale() { + if (this.race == null) + return true; + return (this.race.getRaceType().getCharacterSex().equals(CharacterSex.MALE)); + } + + + public boolean canSee(PlayerCharacter tar) { + + if (tar == null) + return false; + + if (this.equals(tar)) + return true; + + return this.getSeeInvis() >= tar.hidden && !tar.safemodeInvis(); + } + + /** + * @ Initialize player upon creation + */ + public static void initializePlayer(PlayerCharacter player) { + + if (player.initialized) + return; + // Logger.info("", " Initializing " + player.getCombinedName()); + player.skills = DbManager.CharacterSkillQueries.GET_SKILLS_FOR_CHARACTER(player); + player.powers = player.initializePowers(); + + + if (ConfigManager.serverType.equals(ServerType.WORLDSERVER)) + player.setLoc(player.bindLoc); + player.endLoc = Vector3fImmutable.ZERO; + + //get level based on experience + player.level = (short) Experience.getLevel(player.exp); + + player.setHealth(999999f); + player.mana.set(999999f); + player.stamina.set(999999f); + player.bonuses = new PlayerBonuses(player); + PlayerBonuses.InitializeBonuses(player); + player.resists = new Resists(player); + player.charItemManager.load(); + + if (ConfigManager.serverType.equals(ServerType.WORLDSERVER)) { + + //CharacterSkill.updateAllBaseAmounts(this); + CharacterPower.grantTrains(player); + + // calculate skills. Make sure none are missing. + AbstractCharacter.runBonusesOnLoad(player); + + PlayerCharacter.InitializeSkillsOnLoad(player); + + //apply all bonuses + player.recalculatePlayerStats(true); + player.trainsAvailable.set(CharacterSkill.getTrainsAvailable(player)); + + if (player.trainsAvailable.get() < 0) + player.recalculateTrains(); + + //this.resists.calculateResists(this); + player.newChar = true; + + //check current guild valid for player + player.checkGuildStatus(); + + player.setHealth(player.getHealthMax()); + player.mana.set(player.manaMax); + player.stamina.set(player.staminaMax); + } else + player.setBindLoc(Vector3fImmutable.ZERO); + + player.initialized = true; + + String lastAscii = player.lastName.replaceAll("[^\\p{ASCII}]", ""); + player.asciiLastName = lastAscii.equals(player.lastName); + } + + public void recalculatePlayerStats(boolean initialized) { + + //calculate base stats + calculateBaseStats(); + + //calculate base skills + CharacterSkill.updateAllBaseAmounts(this); + calculateModifiedStats(); + + //calculate modified skills + CharacterSkill.updateAllModifiedAmounts(this); + this.updateScaleHeight(); + + //calculate modified stats + + + //calculate ATR, damage and defense + calculateAtrDefenseDamage(); + + //calculate movement bonus + calculateSpeedMod(); + + // recalculate Max Health/Mana/Stamina + calculateMaxHealthManaStamina(); + + // recalculate Resists + Resists.calculateResists(this); + + } + + public static void recalculatePlayerStatsOnLoad(PlayerCharacter pc) { + + //calculate base stats + pc.calculateBaseStats(); + + //calculate base skills + CharacterSkill.updateAllBaseAmounts(pc); + pc.calculateModifiedStats(); + + //calculate modified skills + CharacterSkill.updateAllModifiedAmounts(pc); + + + //calculate modified stats + + + //calculate ATR, damage and defense + pc.calculateAtrDefenseDamage(); + + //calculate movement bonus + pc.calculateSpeedMod(); + + // recalculate Max Health/Mana/Stamina + pc.calculateMaxHealthManaStamina(); + + // recalculate Resists + Resists.calculateResists(pc); + + } + + /** + * @ Recalculate player after promoting or gaining a level + */ + public void recalculate() { + this.applyBonuses(); + this.trainsAvailable.set(CharacterSkill.getTrainsAvailable(this)); + if (this.trainsAvailable.get() < 0) + recalculateTrains(); + //this.resists.calculateResists(this); + + // calculate skills and powers. Make sure none are missing. + this.calculateSkills(); + + // calculate powers again. See if any new powers unlocked + this.calculateSkills(); + } + + //This is run to auto-fix any overage on skill training. + private void recalculateTrains() { + int trainsAvailable = CharacterSkill.getTrainsAvailable(this); + if (trainsAvailable < 0) { + + //refine powers first, run twice to catch any prereqs + ConcurrentHashMap powers = this.getPowers(); + for (int i = 0; i < 2; i++) { + for (CharacterPower p : powers.values()) { + if (trainsAvailable >= 0) + return; + while (p.getTrains() > 0 && p.refine(this)) { + trainsAvailable++; + if (trainsAvailable >= 0) + return; + } + } + } + + //refine skills + ConcurrentHashMap skills = this.getSkills(); + for (CharacterSkill s : skills.values()) { + if (trainsAvailable >= 0) + return; + while (s.getNumTrains() > 0 && s.refine(this)) { + if (CharacterSkill.getTrainsAvailable(this) >= 0) + return; + } + } + } + } + + /** + * @ Calculates Base Stats Call this when modifying stats or adding/removing + * runes + */ + public void calculateBaseStats() { + if (this.race == null || this.baseClass == null) + // Logger.getInstance().log( LogEventType.ERROR, + // "PlayerCharacter.updateBaseStats: Missing race or baseclass for Player " + // + this.getUUID()); + return; + + // get base stats and total available + int strMin = this.race.getStrStart() + this.baseClass.getStrMod() - 5; + int dexMin = this.race.getDexStart() + this.baseClass.getDexMod() - 5; + int conMin = this.race.getConStart() + this.baseClass.getConMod() - 5; + int intMin = this.race.getIntStart() + this.baseClass.getIntMod() - 5; + int spiMin = this.race.getSpiStart() + this.baseClass.getSpiMod() - 5; + int str = this.race.getStrStart() + this.baseClass.getStrMod() + this.strMod.get(); + int dex = this.race.getDexStart() + this.baseClass.getDexMod() + this.dexMod.get(); + int con = this.race.getConStart() + this.baseClass.getConMod() + this.conMod.get(); + int intt = this.race.getIntStart() + this.baseClass.getIntMod() + this.intMod.get(); + int spi = this.race.getSpiStart() + this.baseClass.getSpiMod() + this.spiMod.get(); + int strMax = this.race.getStrMax(); + int dexMax = this.race.getDexMax(); + int conMax = this.race.getConMax(); + int intMax = this.race.getIntMax(); + int spiMax = this.race.getSpiMax(); + int available = this.race.getStartingPoints() - this.strMod.get() - this.dexMod.get() - this.conMod.get() - this.intMod.get() - this.spiMod.get(); + if (level < 20) + available += (level - 1) * 5; + else if (level < 30) + available += 90 + (level - 19) * 4; + else if (level < 40) + available += 130 + (level - 29) * 3; + else if (level < 50) + available += 160 + (level - 39) * 2; + else + available += 180 + (level - 49); + + // modify for any runes applied. + for (CharacterRune rune : this.runes) { + if (rune.getRuneBase() == null) + // Logger.getInstance().log( LogEventType.ERROR, + // "PlayerCharacter.updateBaseStats: Missing runebase for rune " + // + rune.getUUID()); + continue; + ArrayList attrs = rune.getRuneBase().getAttrs(); + if (attrs == null) + // Logger.getInstance().log( LogEventType.ERROR, + // "PlayerCharacter.updateBaseStats: Missing attributes for runebase " + // + rune.getRuneBase().getUUID()); + continue; + for (RuneBaseAttribute abr : attrs) { + int attrID = abr.getAttributeID(); + int value = abr.getModValue(); + switch (attrID) { + case MBServerStatics.RUNE_COST_ATTRIBUTE_ID: + available -= value; + break; + case MBServerStatics.RUNE_STR_ATTRIBUTE_ID: + str += value; + strMin += value; + break; + case MBServerStatics.RUNE_DEX_ATTRIBUTE_ID: + dex += value; + dexMin += value; + break; + case MBServerStatics.RUNE_CON_ATTRIBUTE_ID: + con += value; + conMin += value; + break; + case MBServerStatics.RUNE_INT_ATTRIBUTE_ID: + intt += value; + intMin += value; + break; + case MBServerStatics.RUNE_SPI_ATTRIBUTE_ID: + spi += value; + spiMin += value; + break; + case MBServerStatics.RUNE_STR_MAX_ATTRIBUTE_ID: + strMax += value; + break; + case MBServerStatics.RUNE_DEX_MAX_ATTRIBUTE_ID: + dexMax += value; + break; + case MBServerStatics.RUNE_CON_MAX_ATTRIBUTE_ID: + conMax += value; + break; + case MBServerStatics.RUNE_INT_MAX_ATTRIBUTE_ID: + intMax += value; + break; + case MBServerStatics.RUNE_SPI_MAX_ATTRIBUTE_ID: + spiMax += value; + break; + default: + } + } + + //Set titles based on rune.. + switch (rune.getRuneBaseID()) { + default: + break; + + case 2901: //CSR 1 + this.title = CharacterTitle.CSR_1; + break; + case 2902: //CSR 1 + this.title = CharacterTitle.CSR_2; + break; + case 2903: //CSR 1 + this.title = CharacterTitle.CSR_3; + break; + case 2904: //CSR 1 + this.title = CharacterTitle.CSR_4; + break; + + case 2910: //Wolfpack Developer + this.title = CharacterTitle.DEVELOPER; + break; + case 2911: //QA Test Rune + this.title = CharacterTitle.QA; + break; + } + } + + //hack check. Make sure available does not go below 0. + //subtract from each stat until available is 0 or greater. + if (available < 0) { + while (this.spiMod.get() > 0 && available < 0) { + this.spiMod.decrementAndGet(); + spi--; + available++; + } + while (this.conMod.get() > 0 && available < 0) { + this.conMod.decrementAndGet(); + con--; + available++; + } + while (this.strMod.get() > 0 && available < 0) { + this.strMod.decrementAndGet(); + str--; + available++; + } + while (this.dexMod.get() > 0 && available < 0) { + this.dexMod.decrementAndGet(); + dex--; + available++; + } + while (this.intMod.get() > 0 && available < 0) { + this.intMod.decrementAndGet(); + intt--; + available++; + } + + //update database + this.addDatabaseJob("Stats", MBServerStatics.THIRTY_SECONDS); + } + + this.statStrBase = (short) str; + this.statDexBase = (short) dex; + this.statConBase = (short) con; + this.statIntBase = (short) intt; + this.statSpiBase = (short) spi; + this.statStrMax = (short) (strMax); + this.statDexMax = (short) (dexMax); + this.statConMax = (short) (conMax); + this.statIntMax = (short) (intMax); + this.statSpiMax = (short) (spiMax); + this.statStrMin = (short) strMin; + this.statDexMin = (short) dexMin; + this.statConMin = (short) conMin; + this.statIntMin = (short) intMin; + this.statSpiMin = (short) spiMin; + this.unusedStatPoints = (short) available; + this.trainedStatPoints = 0; + + // Testing, allow characters to have more stats then normal for formula checking + if (this.statStrBase > this.statStrMax) + this.statStrMax = this.statStrBase; + if (this.statDexBase > this.statDexMax) + this.statDexMax = this.statDexBase; + if (this.statConBase > this.statConMax) + this.statConMax = this.statConBase; + if (this.statIntBase > this.statIntMax) + this.statIntMax = this.statIntBase; + if (this.statSpiBase > this.statSpiMax) + this.statSpiMax = this.statSpiBase; + + // Modified stats must be recalculated when base stats are + //calculateModifiedStats(); + //update hide and seeInvis levels + if (this.bonuses != null) { + this.hidden = (int)bonuses.getFloat(ModType.Invisible, SourceType.None); + this.seeInvis = (int) bonuses.getFloat(ModType.SeeInvisible, SourceType.None); + } else { + this.hidden = (byte) 0; + this.seeInvis = (byte) 0; + } + + //check is player is a CSR + this.isCSR = this.containsCSRRune(); + } + + private boolean containsCSRRune() { + + if (this.race != null && this.race.getRaceType().equals(RaceType.CSRMALE)) + return true; + + if (this.baseClass != null && this.baseClass.getObjectUUID() > 2900 && this.baseClass.getObjectUUID() < 2905) + return true; + + if (this.promotionClass != null && this.promotionClass.getObjectUUID() > 2900 && this.promotionClass.getObjectUUID() < 2905) + return true; + + if (this.runes == null) + return false; + + for (CharacterRune rune : this.runes) { + + if (rune == null || rune.getRuneBase() == null) + continue; + + RuneBase rb = rune.getRuneBase(); + + if (rb.getObjectUUID() > 2900 && rb.getObjectUUID() < 2905) + return true; + if (rb.getObjectUUID() == 2910) + return true; + + } + return false; + } + + public boolean isCSR() { + return this.isCSR; + } + + public void setAsciiLastName(boolean value) { + this.asciiLastName = value; + } + + public boolean _asciiLastName() { + return this.asciiLastName; + } + + public static boolean hideNonAscii() { + + return false; + } + + /** + * @ Calculates Modified Stats Call this when changing equipment or + * add/removing effect. skips base stat modification. + */ + public void calculateModifiedStats() { + float strVal = this.statStrBase; + float dexVal = this.statDexBase; + float conVal = this.statConBase; + float intVal = this.statIntBase; + float spiVal = this.statSpiBase; + + this.dexPenalty = getDexPenalty(); + + // TODO modify for equipment + if (this.bonuses != null) { + // modify for effects + strVal += Math.round(this.bonuses.getFloat(ModType.Attr, SourceType.Strength)); + dexVal += Math.round(this.bonuses.getFloat(ModType.Attr, SourceType.Dexterity)); + conVal += Math.round(this.bonuses.getFloat(ModType.Attr, SourceType.Constitution)); + intVal += Math.round(this.bonuses.getFloat(ModType.Attr, SourceType.Intelligence)); + spiVal += Math.round(this.bonuses.getFloat(ModType.Attr, SourceType.Spirit)); + + + // apply dex penalty for armor + dexVal *= this.dexPenalty; + + // modify percent amounts. DO THIS LAST! + strVal *= (1 +Math.round(this.bonuses.getFloatPercentAll(ModType.Attr, SourceType.Strength))); + dexVal *= (1 +Math.round(this.bonuses.getFloatPercentAll(ModType.Attr, SourceType.Dexterity))); + conVal *= (1 + Math.round(this.bonuses.getFloatPercentAll(ModType.Attr, SourceType.Constitution))); + intVal *= (1+Math.round(this.bonuses.getFloatPercentAll(ModType.Attr, SourceType.Intelligence))); + spiVal *= (1+Math.round(this.bonuses.getFloatPercentAll(ModType.Attr, SourceType.Spirit))); + } else + // apply dex penalty for armor + dexVal *= this.dexPenalty; + + // Set current stats + this.statStrCurrent = (strVal < 1) ? (short) 1 : (short) strVal; + this.statDexCurrent = (dexVal < 1) ? (short) 1 : (short) dexVal; + this.statConCurrent = (conVal < 1) ? (short) 1 : (short) conVal; + this.statIntCurrent = (intVal < 1) ? (short) 1 : (short) intVal; + this.statSpiCurrent = (spiVal < 1) ? (short) 1 : (short) spiVal; + + // recalculate skills + //CharacterSkill.updateAllBaseAmounts(this); + // recalculate Max Health/Mana/Stamina + //calculateMaxHealthManaStamina(); + // recalculate Resists + //this.resists.calculateResists(this); + } + + public float getDexPenalty() { + + if (this.charItemManager == null || this.charItemManager.getEquipped() == null) { + Logger.error( "Player " + this.getObjectUUID() + " missing equipment"); + return 1f; + } + + ConcurrentHashMap equipped = this.charItemManager.getEquipped(); + float dexPenalty = 0f; + dexPenalty += getDexPenalty(equipped.get(MBServerStatics.SLOT_HELMET)); + dexPenalty += getDexPenalty(equipped.get(MBServerStatics.SLOT_CHEST)); + dexPenalty += getDexPenalty(equipped.get(MBServerStatics.SLOT_ARMS)); + dexPenalty += getDexPenalty(equipped.get(MBServerStatics.SLOT_GLOVES)); + dexPenalty += getDexPenalty(equipped.get(MBServerStatics.SLOT_LEGGINGS)); + dexPenalty += getDexPenalty(equipped.get(MBServerStatics.SLOT_FEET)); + return (1 - (dexPenalty / 100)); + } + + public static float getDexPenalty(Item armor) { + if (armor == null) + return 0f; + ItemBase ab = armor.getItemBase(); + if (ab == null) + return 0f; + return ab.getDexPenalty(); + } + + public int getStrForClient() { + return this.statStrCurrent - this.race.getStrStart() - this.baseClass.getStrMod(); + } + + public int getDexForClient() { + return this.statDexCurrent - this.race.getDexStart() - this.baseClass.getDexMod(); + } + + public int getConForClient() { + return this.statConCurrent - this.race.getConStart() - this.baseClass.getConMod(); + } + + public int getIntForClient() { + return this.statIntCurrent - this.race.getIntStart() - this.baseClass.getIntMod(); + } + + public int getSpiForClient() { + return this.statSpiCurrent - this.race.getSpiStart() - this.baseClass.getSpiMod(); + } + + public int getTrainsAvailable() { + return this.trainsAvailable.get(); + } + + public void modifyTrainsAvailable(int amount) { + boolean worked = false; + while (!worked) { + int old = this.trainsAvailable.get(); + int newVal = old + amount; + // if (newVal < 0) + // newVal = 0; + worked = this.trainsAvailable.compareAndSet(old, newVal); + } + } + + // Reset any data that should not persist from a previous session + public void resetDataAtLogin() { + loadedObjects.clear(); + loadedStaticObjects.clear(); + lastStaticLoc = Vector3fImmutable.ZERO; + setLastTarget(GameObjectType.unknown, 0); + this.follow = false; + } + + /** + * @ Calculates Atr (both hands) Defense, and Damage for pc + */ + public void calculateAtrDefenseDamage() { + if (this.charItemManager == null || this.charItemManager.getEquipped() == null || this.skills == null) { + Logger.error("Player " + this.getObjectUUID() + " missing skills or equipment"); + defaultAtrAndDamage(true); + defaultAtrAndDamage(false); + this.defenseRating = 0; + return; + } + ConcurrentHashMap equipped = this.charItemManager.getEquipped(); + + // // Reset passives + // if (this.bonuses != null) { + // this.bonuses.setBool("Block", false); + // this.bonuses.setBool("Parry", false); + // if (this.baseClass != null && this.baseClass.getUUID() == 2502) + // this.bonuses.setBool("Dodge", true); + // else + // this.bonuses.setBool("Dodge", false); + // } + // calculate atr and damage for each hand + calculateAtrDamageForWeapon(equipped.get(MBServerStatics.SLOT_MAINHAND), true, equipped.get(MBServerStatics.SLOT_OFFHAND)); + calculateAtrDamageForWeapon(equipped.get(MBServerStatics.SLOT_OFFHAND), false, equipped.get(MBServerStatics.SLOT_MAINHAND)); + + // No Defense while in DeathShroud + if (this.effects != null && this.effects.containsKey("DeathShroud")) + this.defenseRating = (short) 0; + else { + // calculate defense for equipment + float defense = this.statDexCurrent * 2; + defense += getShieldDefense(equipped.get(MBServerStatics.SLOT_OFFHAND)); + defense += getArmorDefense(equipped.get(MBServerStatics.SLOT_HELMET)); + defense += getArmorDefense(equipped.get(MBServerStatics.SLOT_CHEST)); + defense += getArmorDefense(equipped.get(MBServerStatics.SLOT_ARMS)); + defense += getArmorDefense(equipped.get(MBServerStatics.SLOT_GLOVES)); + defense += getArmorDefense(equipped.get(MBServerStatics.SLOT_LEGGINGS)); + defense += getArmorDefense(equipped.get(MBServerStatics.SLOT_FEET)); + defense += getWeaponDefense(equipped); + + if (this.bonuses != null) { + // add any bonuses + defense += (short) this.bonuses.getFloat(ModType.DCV, SourceType.None); + + // Finally multiply any percent modifiers. DO THIS LAST! + float pos_Bonus = this.bonuses.getFloatPercentPositive(ModType.DCV, SourceType.None); + defense = (short) (defense * (1 + pos_Bonus)); + + //Lucky rune applies next + //applied runes will be calculated and added to the normal bonuses. no need for this garbage anymore + //defense = (short) (defense * (1 + ((float) this.bonuses.getShort("rune.Defense") / 100))); + + //and negative percent modifiers + //already done... + float neg_Bonus = this.bonuses.getFloatPercentNegative(ModType.DCV, SourceType.None); + defense = (short) (defense *(1 + neg_Bonus)); + + } else + // TODO add error log here + Logger.error( "Error: missing bonuses"); + + defense = (defense < 1) ? 1 : defense; + this.defenseRating = (short) (defense + 0.5f); + } + } + + /** + * @ Calculates Atr, and Damage for each weapon + */ + private void calculateAtrDamageForWeapon(Item weapon, boolean mainHand, Item otherHand) { + + // make sure weapon exists + boolean noWeapon = false; + ItemBase wb = null; + if (weapon == null) + noWeapon = true; + else { + ItemBase ib = weapon.getItemBase(); + if (ib == null) + noWeapon = true; + else + if (!ib.getType().equals(ItemType.WEAPON)) { + defaultAtrAndDamage(mainHand); + return; + } else + wb = ib; + } + float skillPercentage, masteryPercentage; + float mastDam; + float min, max; + float speed = 20f; + boolean strBased = false; + + ItemBase wbMain = (weapon != null) ? weapon.getItemBase() : null; + ItemBase wbOff = (otherHand != null) ? otherHand.getItemBase() : null; + + // get skill percentages and min and max damage for weapons + if (noWeapon) { + if (mainHand) { + Item off = this.charItemManager.getEquipped().get(MBServerStatics.SLOT_OFFHAND); + if (off != null && off.getItemBase() != null && off.getItemBase().getType().equals(ItemType.WEAPON)) + this.rangeHandOne = 10 * (1 + (this.statStrBase / 600)); // Set + // to + // no + // weapon + // range + else + this.rangeHandOne = -1; // set to do not attack + } else + this.rangeHandTwo = -1; // set to do not attack + + skillPercentage = getModifiedAmount(this.skills.get("Unarmed Combat")); + masteryPercentage = getModifiedAmount(this.skills.get("Unarmed Combat Mastery")); + if (masteryPercentage == 0f) + mastDam = CharacterSkill.getQuickMastery(this, "Unarmed Combat Mastery"); + else + mastDam = masteryPercentage; + // TODO Correct these + min = 1; + max = 3; + } else { + if (mainHand) + this.rangeHandOne = weapon.getItemBase().getRange() * (1 + (this.statStrBase / 600)); + else + this.rangeHandTwo = weapon.getItemBase().getRange() * (1 + (this.statStrBase / 600)); + + if (this.bonuses != null){ + float range_bonus = 1 + this.bonuses.getFloatPercentAll(ModType.WeaponRange, SourceType.None); + + if (mainHand) + this.rangeHandOne *= range_bonus; + else + this.rangeHandTwo *= range_bonus; + + } + skillPercentage = getModifiedAmount(this.skills.get(wb.getSkillRequired())); + masteryPercentage = getModifiedAmount(this.skills.get(wb.getMastery())); + if (masteryPercentage == 0f) + mastDam = 0f; + // mastDam = CharacterSkill.getQuickMastery(this, wb.getMastery()); + else + mastDam = masteryPercentage; + min = (float) wb.getMinDamage(); + max = (float) wb.getMaxDamage(); + strBased = wb.isStrBased(); + + // + // Add parry bonus for weapon and allow parry if needed + + // // Only Fighters and Thieves can Parry + // if ((this.baseClass != null && this.baseClass.getUUID() == 2500) + // || (this.promotionClass != null && this.promotionClass.getUUID() == 2520)) { + // if (wbMain == null || wbMain.getRange() < MBServerStatics.RANGED_WEAPON_RANGE) + // if (wbOff == null || wbOff.getRange() < MBServerStatics.RANGED_WEAPON_RANGE) + // this.bonuses.setBool("Parry", true); + // } + // } + } + + if (this.effects != null && this.effects.containsKey("DeathShroud")) + // No Atr in deathshroud. + if (mainHand) + this.atrHandOne = (short) 0; + else + this.atrHandTwo = (short) 0; + else { + // calculate atr + float atr = 0; + atr += (int) skillPercentage * 4f; //<-round down skill% - + atr += (int) masteryPercentage * 3f; + if (this.statStrCurrent > this.statDexCurrent) + atr += statStrCurrent / 2; + else + atr += statDexCurrent / 2; + + // add in any bonuses to atr + if (this.bonuses != null) { + // Add any base bonuses + atr += this.bonuses.getFloat(ModType.OCV, SourceType.None); + + // Finally use any multipliers. DO THIS LAST! + float pos_Bonus = (1 + this.bonuses.getFloatPercentPositive(ModType.OCV, SourceType.None)); + atr *= pos_Bonus; + + // next precise + //runes will have their own bonuses. + // atr *= (1 + ((float) this.bonuses.getShort("rune.Attack") / 100)); + + //and negative percent modifiers + float neg_Bonus = this.bonuses.getFloatPercentNegative(ModType.OCV, SourceType.None); + + atr *= (1 + neg_Bonus); + } + + atr = (atr < 1) ? 1 : atr; + + // set atr + if (mainHand) + this.atrHandOne = (short) (atr + 0.5f); + else + this.atrHandTwo = (short) (atr + 0.5f); + } + + //calculate speed + if (wb != null) + speed = wb.getSpeed(); + else + speed = 20f; //unarmed attack speed + if (weapon != null) + speed *= (1 + weapon.getBonusPercent(ModType.WeaponSpeed, SourceType.None)); + speed *= (1 + this.bonuses.getFloatPercentAll(ModType.AttackDelay, SourceType.None)); + if (speed < 10) + speed = 10; + + //add min/max damage bonuses for weapon + if (weapon != null) { + // Add any base bonuses + + min += weapon.getBonus(ModType.MinDamage, SourceType.None); + max += weapon.getBonus(ModType.MaxDamage, SourceType.None); + + min += weapon.getBonus(ModType.MeleeDamageModifier, SourceType.None); + max += weapon.getBonus(ModType.MeleeDamageModifier, SourceType.None); + // Finally use any multipliers. DO THIS LAST! + + float percentMinDamage = 1; + float percentMaxDamage = 1; + + percentMinDamage += weapon.getBonusPercent(ModType.MinDamage, SourceType.None); + percentMinDamage += weapon.getBonusPercent(ModType.MeleeDamageModifier, SourceType.None); + + percentMaxDamage += weapon.getBonusPercent(ModType.MaxDamage, SourceType.None); + percentMaxDamage += weapon.getBonusPercent(ModType.MeleeDamageModifier, SourceType.None); + + + + min *= percentMinDamage; + max *= percentMaxDamage; + } + + //if duel wielding, cut damage by 30% + if (otherHand != null) { + ItemBase ibo = otherHand.getItemBase(); + if (ibo != null && ibo.getType().equals(ItemType.WEAPON)) { + min *= 0.7f; + max *= 0.7f; + } + } + + // calculate damage + float minDamage; + float maxDamage; + float pri = (strBased) ? (float) this.statStrCurrent : (float) this.statDexCurrent; + float sec = (strBased) ? (float) this.statDexCurrent : (float) this.statStrCurrent; + minDamage = (float) (min * ((0.0315f * Math.pow(pri, 0.75f)) + (0.042f * Math.pow(sec, 0.75f)) + (0.01f * ((int) skillPercentage + (int) mastDam)))); + maxDamage = (float) (max * ((0.0785f * Math.pow(pri, 0.75f)) + (0.016f * Math.pow(sec, 0.75f)) + (0.0075f * ((int) skillPercentage + (int) mastDam)))); + minDamage = (float) ((int) (minDamage + 0.5f)); //round to nearest decimal + maxDamage = (float) ((int) (maxDamage + 0.5f)); //round to nearest decimal + + // Half damage if in death shroud + if (this.effects != null && this.effects.containsKey("DeathShroud")) { + minDamage *= 0.5f; + maxDamage *= 0.5f; + } + + // add in any bonuses to damage + if (this.bonuses != null) { + // Add any base bonuses + minDamage += this.bonuses.getFloat(ModType.MinDamage, SourceType.None); + maxDamage += this.bonuses.getFloat(ModType.MaxDamage, SourceType.None); + + minDamage += this.bonuses.getFloat(ModType.MeleeDamageModifier, SourceType.None); + maxDamage += this.bonuses.getFloat(ModType.MeleeDamageModifier, SourceType.None); + // Finally use any multipliers. DO THIS LAST! + + float percentMinDamage = 1; + float percentMaxDamage = 1; + + percentMinDamage += this.bonuses.getFloatPercentAll(ModType.MinDamage, SourceType.None); + percentMinDamage += this.bonuses.getFloatPercentAll(ModType.MeleeDamageModifier, SourceType.None); + + percentMaxDamage += this.bonuses.getFloatPercentAll(ModType.MaxDamage, SourceType.None); + percentMaxDamage += this.bonuses.getFloatPercentAll(ModType.MeleeDamageModifier, SourceType.None); + + minDamage *= percentMinDamage; + maxDamage *= percentMaxDamage; + + } + + // set damages + if (mainHand) { + this.minDamageHandOne = (int) minDamage; + this.maxDamageHandOne = (int) maxDamage; + this.speedHandOne = speed; + } else { + this.minDamageHandTwo = (int) minDamage; + this.maxDamageHandTwo = (int) maxDamage; + this.speedHandTwo = speed; + } + } + + /** + * @ Calculates Defense for shield + */ + private float getShieldDefense(Item shield) { + if (shield == null) + return 0; + ItemBase ab = shield.getItemBase(); + if (ab == null || !ab.isShield()) + return 0; + CharacterSkill blockSkill = this.skills.get("Block"); + float skillMod; + if (blockSkill == null) { + skillMod = 0; + } else + skillMod = blockSkill.getModifiedAmount(); + + float def = ab.getDefense(); + //apply item defense bonuses + if (shield != null){ + def += shield.getBonus(ModType.DR, SourceType.None); + def *= (1 + shield.getBonusPercent(ModType.DR, SourceType.None)); + + } + + // float val = ((float)ab.getDefense()) * (1 + (skillMod / 100)); + return (def * (1 + ((int) skillMod / 100f))); + } + + public void setPassives() { + if (this.bonuses != null) { + ConcurrentHashMap equipped = this.charItemManager.getEquipped(); + Item off = equipped.get(MBServerStatics.SLOT_OFFHAND); + Item main = equipped.get(MBServerStatics.SLOT_MAINHAND); + ItemBase wbMain = null; + ItemBase wbOff = null; + if (main != null) + wbMain = main.getItemBase(); + if (off != null) + wbOff = off.getItemBase(); + + //set block if block found + this.bonuses.setBool(ModType.Block,SourceType.None, false); + if (this.baseClass != null && (this.baseClass.getObjectUUID() == 2500 || this.baseClass.getObjectUUID() == 2501)) + if (off != null && off.getItemBase() != null && off.getItemBase().isShield()) + this.bonuses.setBool(ModType.Block,SourceType.None, true); + + //set dodge if rogue + if (this.baseClass != null && this.baseClass.getObjectUUID() == 2502) + this.bonuses.setBool(ModType.Dodge,SourceType.None, true); + else + this.bonuses.setBool(ModType.Dodge,SourceType.None, false); + + //set parry if fighter or thief and no invalid weapon found + this.bonuses.setBool(ModType.Parry,SourceType.None, false); + if ((this.baseClass != null && this.baseClass.getObjectUUID() == 2500) + || (this.promotionClass != null && this.promotionClass.getObjectUUID() == 2520)) + if (wbMain == null || wbMain.getRange() < MBServerStatics.RANGED_WEAPON_RANGE) + if (wbOff == null || wbOff.getRange() < MBServerStatics.RANGED_WEAPON_RANGE) + this.bonuses.setBool(ModType.Parry,SourceType.None, true); + + } + + } + + /** + * @ Calculates Defense for armor + */ + private float getArmorDefense(Item armor) { + + if (armor == null) + return 0; + + ItemBase ib = armor.getItemBase(); + + if (ib == null) + return 0; + + if (!ib.getType().equals(ItemType.ARMOR)) + return 0; + if (ib.getSkillRequired().isEmpty()) + return ib.getDefense(); + CharacterSkill armorSkill = this.skills.get(ib.getSkillRequired()); + if (armorSkill == null) { + Logger.error( "Player " + this.getObjectUUID() + + " has armor equipped without the nescessary skill to equip it"); + return ib.getDefense(); + } + + float def = ib.getDefense(); + //apply item defense bonuses + if (armor != null){ + def += armor.getBonus(ModType.DR, SourceType.None); + def *= (1 + armor.getBonusPercent(ModType.DR, SourceType.None)); + } + + + return (def * (1 + ((int) armorSkill.getModifiedAmount() / 50f))); + } + + /** + * @ Calculates Defense for weapon + */ + private float getWeaponDefense(ConcurrentHashMap equipped) { + Item weapon = equipped.get(MBServerStatics.SLOT_MAINHAND); + ItemBase wb = null; + CharacterSkill skill, mastery; + float val = 0; + boolean unarmed = false; + if (weapon == null) { + weapon = equipped.get(MBServerStatics.SLOT_OFFHAND); + if (weapon == null || weapon.getItemBase().isShield()) + unarmed = true; + else + wb = weapon.getItemBase(); + } else + wb = weapon.getItemBase(); + if (wb == null) + unarmed = true; + if (unarmed) { + skill = this.skills.get("Unarmed Combat"); + mastery = this.skills.get("Unarmed Combat Mastery"); + } else { + skill = this.skills.get(wb.getSkillRequired()); + mastery = this.skills.get(wb.getMastery()); + } + if (skill != null) + val += (int) skill.getModifiedAmount() / 2f; + if (mastery != null) + val += (int) mastery.getModifiedAmount() / 2f; + return val; + } + + private static float getModifiedAmount(CharacterSkill skill) { + if (skill == null) + return 0f; + return skill.getModifiedAmount(); + } + + //Call this function to recalculate granted skills and powers for player + public synchronized void calculateSkills() { + //tell the player to applyBonuses because something has changed + + runSkillCalc(); + + //start running the skill/power calculations + } + + //Don't call this function directly. linked from pc.calculateSkills() + //through SkillCalcJob. Designed to only run from one worker thread + public void runSkillCalc() { + try { + + //see if any new skills or powers granted + CharacterSkill.calculateSkills(this); + // calculate granted Trains in powers. + CharacterPower.grantTrains(this); + //see if any new powers unlocked from previous check + CharacterPower.calculatePowers(this); + + } catch (Exception e) { + } + + } + + public static void InitializeSkillsOnLoad(PlayerCharacter pc) { + try { + { + + //see if any new skills or powers granted + CharacterSkill.calculateSkills(pc); + + // calculate granted Trains in powers. + CharacterPower.grantTrains(pc); + + //see if any new powers unlocked from previous check + CharacterPower.calculatePowers(pc); + } + } catch (Exception e) { + Logger.error( e.getMessage()); + } + + } + + //calculate item bonuses here + public void calculateItemBonuses() { + if (this.charItemManager == null || this.bonuses == null) + return; + ConcurrentHashMap equipped = this.charItemManager.getEquipped(); + for (Item item : equipped.values()) { + ItemBase ib = item.getItemBase(); + if (ib == null) + continue; + //TODO add effect bonuses in here for equipped items + } + } + + /** + * @ Defaults ATR, Defense and Damage for player + */ + private void defaultAtrAndDamage(boolean mainHand) { + if (mainHand) { + this.atrHandOne = 0; + this.minDamageHandOne = 0; + this.maxDamageHandOne = 0; + this.rangeHandOne = -1; + this.speedHandOne = 20; + } else { + this.atrHandTwo = 0; + this.minDamageHandTwo = 0; + this.maxDamageHandTwo = 0; + this.rangeHandTwo = -1; + this.speedHandTwo = 20; + } + } + + public void calculateMaxHealthManaStamina() { + float h = 1f; + float m = 0f; + float s = 0f; + float baseHealth = 15f; + float baseMana = 5f; + float baseStamina = 1f; + float promoHealth = 0f; + float promoMana = 0f; + float promoStamina = 0f; + float raceHealth = 0f; + float raceMana = 0f; + float raceStamina = 0f; + float toughness = 0f; + float athletics = 0f; + + //get baseclass modifiers + if (this.baseClass != null) { + baseHealth = this.baseClass.getHealthMod(); + baseMana = this.baseClass.getManaMod(); + baseStamina = this.baseClass.getStaminaMod(); + } else { + //TODO log error here + } + + //get promotion modifiers + if (this.promotionClass != null) { + promoHealth = this.promotionClass.getHealthMod(); + promoMana = this.promotionClass.getManaMod(); + promoStamina = this.promotionClass.getStaminaMod(); + } + + // next get racial modifer + if (this.race != null) { + raceHealth += this.race.getHealthBonus(); + raceMana += this.race.getManaBonus(); + raceStamina += this.race.getStaminaBonus(); + } else { + //TODO log error here + } + + //Get level modifers + float f = 0; + float g = 0; + if (this.level < 10 || this.promotionClass == null) + f = this.level; + else if (this.level < 20) { + f = this.level; + g = this.level - 9; + } else if (level < 30) { + f = (float) (19 + (this.level - 19) * 0.8); + g = (float) (10 + (this.level - 19) * 0.8); + } else if (level < 40) { + f = (float) (27 + (this.level - 29) * 0.6); + g = (float) (18 + (this.level - 29) * 0.6); + } else if (level < 50) { + f = (float) (33 + (this.level - 39) * 0.4); + g = (float) (24 + (this.level - 39) * 0.4); + } else if (level < 60) { + f = (float) (37 + (this.level - 49) * 0.2); + g = (float) (28 + (this.level - 49) * 0.2); + } else { + f = (float) (39 + (this.level - 59) * 0.1); + g = (float) (30 + (this.level - 59) * 0.1); + } + + //get toughness and athletics amount + if (this.skills != null) { + if (this.skills.containsKey("Toughness")) + toughness = this.skills.get("Toughness").getModifiedAmount(); + if (this.skills.containsKey("Athletics")) + athletics = this.skills.get("Athletics").getModifiedAmount(); + } + + h = (((f * baseHealth) + (g * promoHealth)) * (0.3f + (0.005f * this.statConCurrent)) + (this.statConCurrent + raceHealth)) * (1 + (int) toughness / 400f); + m = ((f * baseMana) + (g * promoMana)) * (0.3f + (0.005f * this.statSpiCurrent)) + (this.statSpiCurrent + raceMana); + s = (((f * baseStamina) + (g * promoStamina)) * (0.3f + (0.005f * this.statConCurrent)) + (this.statConCurrent + raceStamina)) * (1 + (int) athletics / 300f); + + // s = f * (baseStamina + 1.75f) * .5f + this.statConCurrent + raceStamina; + // Apply any bonuses from runes and effects + if (this.bonuses != null) { + + + //apply effects + h += this.bonuses.getFloat(ModType.HealthFull, SourceType.None); + m += this.bonuses.getFloat(ModType.ManaFull, SourceType.None); + s += this.bonuses.getFloat(ModType.StaminaFull, SourceType.None); + + h *= (1 +this.bonuses.getFloatPercentAll(ModType.HealthFull, SourceType.None)) ; + m *= (1+this.bonuses.getFloatPercentAll(ModType.ManaFull, SourceType.None)); + s *= (1+this.bonuses.getFloatPercentAll(ModType.StaminaFull, SourceType.None)); + + } + + // Set max health, mana and stamina + if (h > 0) + this.healthMax = h; + else + this.healthMax = 1; + if (m > -1) + this.manaMax = m; + else + this.manaMax = 0; + if (s > -1) + this.staminaMax = s; + else + this.staminaMax = 0; + + // Update health, mana and stamina if needed + if (this.getCurrentHitpoints() > this.healthMax) + this.setHealth(this.healthMax); + if (this.mana.get() > this.manaMax) + this.mana.set(this.manaMax); + if (this.stamina.get() > this.staminaMax) + this.stamina.set(staminaMax); + } + + @Override + public float getPassiveChance(String type, int attackerLevel, boolean fromCombat) { + if (this.skills == null || this.bonuses == null) + return 0f; + + ModType modType = ModType.GetModType(type); + + // must be allowed to use this passive + if (!this.bonuses.getBool(modType, SourceType.None)) + return 0f; + + // must not be stunned + if (this.bonuses.getBool(ModType.Stunned, SourceType.None)) + return 0f; + + // Get base skill amount + CharacterSkill sk = this.skills.get(type); + float amount; + if (sk == null) + amount = CharacterSkill.getQuickMastery(this, type); + else + amount = sk.getModifiedAmount(); + + // Add bonuses + amount += this.bonuses.getFloat(modType, SourceType.None); + + // Add item bonuses and return + if (type.equals(ModType.Dodge) && !fromCombat) + return ((amount / 4) - attackerLevel + this.getLevel()) / 4; + else + return (amount - attackerLevel + this.getLevel()) / 4; + } + + public float getPassiveChance1(ModType modType, SourceType sourceType, int attackerLevel, boolean fromCombat) { + if (this.skills == null || this.bonuses == null) + return 0f; + + // must be allowed to use this passive + if (!this.bonuses.getBool(modType, sourceType)) + return 0f; + + // must not be stunned + if (this.bonuses.getBool(ModType.Stunned, SourceType.None)) + return 0f; + + // Get base skill amount + CharacterSkill sk = this.skills.get(sourceType.name()); + float amount; + if (sk == null) + amount = CharacterSkill.getQuickMastery(this, modType.name()); + else + amount = sk.getModifiedAmount(); + + // Add bonuses + amount += this.bonuses.getFloat(modType, sourceType); + + // Add item bonuses and return + if (sourceType.equals(SourceType.Dodge) && !fromCombat) + return ((amount / 4) - attackerLevel + this.getLevel()) / 4; + else + return (amount - attackerLevel + this.getLevel()) / 4; + } + + public float getRegenModifier(ModType type) { + float regen = 1f; + + if (this.bonuses != null) + // get regen bonus from effects + regen = this.bonuses.getRegen(type); + return regen; + } + + @Override + public boolean canBeLooted() { + return !this.isAlive(); + } + + @Override + public void setLevel(short targetLevel) { + + short tmpLevel; + + tmpLevel = targetLevel; + + tmpLevel = (short) Math.min(tmpLevel, 75); + + while (this.level < tmpLevel) { + grantXP(Experience.getBaseExperience(tmpLevel) - this.exp); + } + + } + + public void ResetLevel(short targetLevel) { + + if (targetLevel > 13){ + ChatManager.chatSystemError(this, "Please choose a level between 1 and 13."); + return; + } + this.promotionClass = null; + if (targetLevel > 10){ + this.level = 10; + this.exp = Experience.getBaseExperience(11); + int maxEXP = Experience.getBaseExperience(targetLevel); //target level exp; + this.overFlowEXP = maxEXP - this.exp; + }else{ + this.level = targetLevel; + this.exp = Experience.getBaseExperience(level); + this.overFlowEXP = 0; + } + + + for (CharacterSkill skill: this.getSkills().values()){ + skill.reset(this, true); + } + + for (CharacterPower power : this.getPowers().values()){ + power.reset(this); + } + + this.recalculatePlayerStats(initialized); + this.recalculate(); + + ChatManager.chatSystemInfo(this, "Character reset to " + targetLevel+ ". All training points have been refunded. Relog to update changes on client."); + + } + + @Override + public void removeFromCache() { + Logger.info("Removing " + this.getName() + " from Object Cache."); + + for (Item e : this.charItemManager.getEquipped().values()) { + e.removeFromCache(); + } + + for (Item i : this.charItemManager.getInventory(true)) { + i.removeFromCache(); + } + + for (Item b : this.charItemManager.getBank()) { + b.removeFromCache(); + } + + if (this.account.getLastCharIDUsed() == this.getObjectUUID()) + for (Item v : this.charItemManager.getVault()) { + v.removeFromCache(); + } + + for (CharacterSkill cs : this.getSkills().values()) { + cs.removeFromCache(); + } + + for (CharacterPower ps : this.getPowers().values()) { + ps.removeFromCache(); + } + + for (CharacterRune cr : this.runes) { + cr.removeFromCache(); + } + + super.removeFromCache(); + } + + public static String getFirstName(int tableId) { + + PlayerCharacter player; + + if (tableId == 0) + return ""; + + player = (PlayerCharacter) DbManager.getObject(GameObjectType.PlayerCharacter, tableId); + + return player.getFirstName(); + } + + public static PlayerCharacter getFromCache(int id) { + return (PlayerCharacter) DbManager.getFromCache(GameObjectType.PlayerCharacter, id); + } + + public static PlayerCharacter getByFirstName(String name) { + + PlayerCharacter returnPlayer = null; + for (AbstractGameObject ago : DbManager.getList(GameObjectType.PlayerCharacter)){ + PlayerCharacter cachePlayer = (PlayerCharacter)ago; + if (!name.equalsIgnoreCase(cachePlayer.getFirstName())) + continue; + if (cachePlayer.isDeleted()) + continue; + returnPlayer = cachePlayer; + break; + } + + return returnPlayer; + } + + public static PlayerCharacter getPlayerCharacter(int uuid) { + + PlayerCharacter outPlayer; + + outPlayer = DbManager.PlayerCharacterQueries.GET_PLAYER_CHARACTER(uuid); + + if (outPlayer != null) + return outPlayer; + + return (PlayerCharacter) DbManager.getFromCache(GameObjectType.PlayerCharacter, uuid); + } + + public void storeIgnoreListDB() { + + } + + public void updateSkillsAndPowersToDatabase() { + if (this.skills != null) + for (CharacterSkill skill : this.skills.values()) { + DbManager.CharacterSkillQueries.UPDATE_TRAINS(skill); + if (this.powers != null) + for (CharacterPower power : this.powers.values()) { + DbManager.CharacterPowerQueries.UPDATE_TRAINS(power); + } + } + } + + @Override + public void updateDatabase() { + } + + @Override + public void runAfterLoad() { + + // Create player bounds object + + // if ((MBServer.getApp() instanceof engine.server.world.WorldServer)) + // DbManager.GuildQueries.LOAD_GUILD_HISTORY_FOR_PLAYER(this); + + Bounds playerBounds = Bounds.borrow(); + playerBounds.setBounds(this.getLoc()); + this.setBounds(playerBounds); + } + + @Override + protected ConcurrentHashMap initializePowers() { + return DbManager.CharacterPowerQueries.GET_POWERS_FOR_CHARACTER(this); + } + + @Override + public final void setFirstName(final String name) { + super.setFirstName(name); + } + + @Override + public void setLastName(final String name) { + super.setLastName(name); + } + + @Override + public short getLevel() { + return this.getPCLevel(); + } + + @Override + public boolean asciiLastName() { + return this._asciiLastName(); + } + + @Override + public void setGuild(Guild value) { + + if (value == null) + value = Guild.getErrantGuild(); + + int guildID = 0; + + if (!value.isErrant()) + guildID = value.getObjectUUID(); + DbManager.PlayerCharacterQueries.UPDATE_GUILD(this, guildID); + super.setGuild(value); + + // Player changed guild so let's invalidate the login server + // cache to reflect this event. + + //Update player bind location; + + Building cityTol = null; + + if (value.getOwnedCity() != null) + cityTol = value.getOwnedCity().getTOL(); + + this.setBindBuildingID(cityTol != null ? cityTol.getObjectUUID() : 0); + //update binds, checks for nation tol if guild tol == null; + PlayerCharacter.getUpdatedBindBuilding(this); + + + DbManager.AccountQueries.INVALIDATE_LOGIN_CACHE(this.getObjectUUID(), "character"); + } + + public long getSummoner(int summoner) { + synchronized (this.summoners) { + if (!this.summoners.containsKey(summoner)) + return 0; + return this.summoners.get(summoner); + } + } + + public void addSummoner(int summoner, long time) { + synchronized (this.summoners) { + this.summoners.put(summoner, time); + } + } + + public void removeSummoner(int summoner) { + synchronized (this.summoners) { + if (this.summoners.containsKey(summoner)) + this.summoners.remove(summoner); + } + } + + public boolean commandSiegeMinion(Mob toCommand) { + if (!toCommand.isSiege()) + return false; + if (toCommand.isPet() || !toCommand.isAlive()) + return false; + + if (toCommand.getGuild().getNation() != this.getGuild().getNation()) + return false; + + if (this.pet != null) { + Mob currentPet = this.pet; + if (!currentPet.isSiege()) { + + currentPet.setCombatTarget(null); + currentPet.setState(STATE.Disabled); + + if (currentPet.getParentZone() != null) + + currentPet.getParentZone().zoneMobSet.remove(currentPet); + + try { + currentPet.clearEffects(); + }catch(Exception e){ + Logger.error( e.getMessage()); + } + currentPet.getPlayerAgroMap().clear(); + WorldGrid.RemoveWorldObject(currentPet); + DbManager.removeFromCache(currentPet); + + } else + if (currentPet.isSiege()) { + currentPet.setMob(); + currentPet.setOwner(null); + currentPet.setCombatTarget(null); + if (currentPet.isAlive()) + WorldGrid.updateObject(currentPet); + } + } + + toCommand.setPet(this, false); + this.setPet(toCommand); + toCommand.setCombatTarget(null); + PetMsg petMsg = new PetMsg(6, toCommand); + Dispatch dispatch = Dispatch.borrow(this, petMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + + if (toCommand.isAlive()) + WorldGrid.updateObject(toCommand); + return true; + } + + public boolean isNoTeleScreen() { + return noTeleScreen; + } + + public void setNoTeleScreen(boolean noTeleScreen) { + this.noTeleScreen = noTeleScreen; + } + + private double getDeltaTime() { + + return (System.currentTimeMillis() - lastUpdateTime) * .001f; + } + + private double getStamDeltaTime() { + + return (System.currentTimeMillis() - lastStamUpdateTime) * .001f; + } + + public boolean isFlying() { + + return this.getAltitude() > 0; + + } + + public boolean isSwimming() { + + // If char is flying they aren't quite swimming + try { + if (this.isFlying()) + return false; + + Zone zone = ZoneManager.findSmallestZone(this.getLoc()); + + if (zone.getSeaLevel() != 0) { + + float localAltitude = this.getLoc().y + this.centerHeight; + if (localAltitude < zone.getSeaLevel()) + return true; + } else { + if (this.getLoc().y + this.centerHeight < 0) + return true; + } + } catch (Exception e){ + Logger.info(this.getName() + e); + } + + return false; + } + + public boolean isSwimming(Vector3fImmutable currentLoc) { + + // If char is flying they aren't quite swimming + try{ + + float localAltitude = HeightMap.getWorldHeight(currentLoc); + + Zone zone = ZoneManager.findSmallestZone(currentLoc); + + if (zone.getSeaLevel() != 0){ + + if (localAltitude < zone.getSeaLevel()) + return true; + }else{ + if (localAltitude < 0) + return true; + } + }catch(Exception e){ + Logger.info(this.getName() + e); + } + + return false; + } + + // Method is called by Server Heartbeat simulation tick. + // Stat regen and transform updates should go in here. + + @Override + public void update() { + + if (this.updateLock.writeLock().tryLock()){ + try{ + + if (!this.isAlive()) + return; + + updateLocation(); + updateMovementState(); + updateRegen(); + + if (this.getStamina() < 10){ + if (this.getAltitude() > 0 || this.getDesiredAltitude() > 0){ + PlayerCharacter.GroundPlayer(this); + updateRegen(); + } + } + + RealmMap.updateRealm(this); + updateBlessingMessage(); + + this.safeZone = this.isInSafeZone(); + + }catch(Exception e){ + Logger.error(e); + }finally{ + this.updateLock.writeLock().unlock(); + } + } + + } + @Override + public void updateFlight() { + + if (this.getAltitude() == 0 && this.getTakeOffTime() == 0) + return; + + if (this.getTakeOffTime() == 0) + return; + + if (this.getAltitude() == this.getDesiredAltitude()){ + if (this.getDesiredAltitude() == 0) + this.syncClient(); + //landing in a building, mark altitude to 0 as player is no longer flying. + if (this.landingRegion != null){ + this.altitude = 0; + this.region = this.landingRegion; + this.loc = this.loc.setY(this.landingRegion.lerpY(this)); + } + else + this.altitude = this.getDesiredAltitude(); + + this.loc = this.loc.setY(HeightMap.getWorldHeight(this) + this.getAltitude()); + + this.setTakeOffTime(0); + MovementManager.finishChangeAltitude(this, this.getDesiredAltitude()); + + return; + } + + this.loc = this.loc.setY(HeightMap.getWorldHeight(this) + this.getAltitude()); + } + + public boolean hasBoon(){ + for (Effect eff : this.getEffects().values()){ + if (eff.getPowerToken() == -587743986 || eff.getPowerToken() == -1660519801 || eff.getPowerToken() == -1854683250) + return true; + } + return false; + } + + public void updateBlessingMessage(){ + + if (this.getTimeStamp("RealmClaim") > System.currentTimeMillis()) + return; + + int count = 0; + + for (Effect eff : this.getEffects().values()){ + if (eff.getPowerToken() == -587743986 || eff.getPowerToken() == -1660519801 || eff.getPowerToken() == -1854683250) + count++; + } + + if (count > 0){ + this.timestamps.put("RealmClaim", DateTime.now().plusMinutes(3).getMillis()); + for (PlayerCharacter toSend : SessionManager.getAllActivePlayerCharacters()){ + ChatManager.chatSystemInfo(toSend, this.getCombinedName() + " is seeking to claim a realm and already has " + count + " blessngs!"); + } + } + } + @Override + public void updateLocation(){ + + + if (!this.isMoving()) + return; + + if (!this.isActive) + return; + + Vector3fImmutable newLoc = this.getMovementLoc(); + + if (this.isAlive() == false || this.getBonuses().getBool(ModType.Stunned, SourceType.None) || this.getBonuses().getBool(ModType.CannotMove, SourceType.None)) { + //Target is stunned or rooted. Don't move + this.stopMovement(newLoc); + this.region = AbstractWorldObject.GetRegionByWorldObject(this); + return; + } + if (newLoc.equals(this.getEndLoc())){ + this.stopMovement(newLoc); + this.region = AbstractWorldObject.GetRegionByWorldObject(this); + if (this.getDebug(1)) + ChatManager.chatSystemInfo( this, + "Arrived at End location. " + this.getEndLoc()); + return; + //Next upda + } + + setLoc(newLoc); + this.region = AbstractWorldObject.GetRegionByWorldObject(this); + + if (this.getDebug(1)) + ChatManager.chatSystemInfo(this, + "Distance to target " + this.getEndLoc().distance2D(this.getLoc()) + " speed " + this.getSpeed()); + + if (this.getStamina() < 10) + MovementManager.sendOOS(this); + + // if (MBServerStatics.MOVEMENT_SYNC_DEBUG || this.getDebug(1)) + // Logger.info("MovementManager", "Updating movement current loc:" + this.getLoc().getX() + " " + this.getLoc().getZ() + // + " end loc: " + this.getEndLoc().getX() + " " + this.getEndLoc().getZ() + " distance " + this.getEndLoc().distance2D(this.getLoc())); + + } + @Override + public void updateMovementState() { + + + if (this.enteredWorld) { + if (!this.lastSwimming) { + boolean enterWater = PlayerCharacter.enterWater(this); + + if (enterWater){ + this.lastSwimming = enterWater; + MovementManager.sendRWSSMsg(this); + + } + } else { + if (PlayerCharacter.LeaveWater(this)){ + this.lastSwimming = false; + if (!this.isMoving()) + MovementManager.sendRWSSMsg(this); + } + + } + + boolean breathe = PlayerCharacter.CanBreathe(this); + + if (breathe != this.canBreathe){ + this.canBreathe = breathe; + // ChatManager.chatSystemInfo(this, "Breathe : " + this.canBreathe); + this.syncClient(); + } + } + + //char is flying + if (this.isFlying() == true) { + this.movementState = MovementState.FLYING; + return; + } + // Char is not moving. Set sitting or idle + if (!this.isMoving()) { + + if (this.sit == true) + this.movementState = MovementState.SITTING; + else + this.movementState = MovementState.IDLE; + + return; + } else { + this.movementState = MovementState.RUNNING; + } + + // Char is swimming // we now are saving lastSwimstate boolean, use this instead of calling getSwimming again. + if (this.lastSwimming == true) { + this.movementState = MovementState.SWIMMING; + return; + } + + // Char is moving, yet not swimming or flying he must be running + this.movementState = MovementState.RUNNING; + + } + @Override + public void updateRegen() { + + float healthRegen = 0f; + float manaRegen = 0f; + float stamRegen = 0f; + + boolean updateClient = false; + + // Early exit if char is dead or disconnected + if ((this.isAlive() == false) + || (this.isActive() == false) || this.getLoc().x == 0 && this.getLoc().z == 0) + return; + + // Calculate Regen amount from last simulation tick + switch (this.movementState) { + + case IDLE: + + healthRegen = ((this.healthMax * MBServerStatics.HEALTH_REGEN_IDLE) + MBServerStatics.HEALTH_REGEN_IDLE_STATIC) * (getRegenModifier(ModType.HealthRecoverRate)); + + if (this.isCasting() || this.isItemCasting()) + healthRegen *= .75f; + // Characters regen mana when in only walk mode and idle + if (this.walkMode) + manaRegen = ((this.manaMax * MBServerStatics.MANA_REGEN_IDLE) * getRegenModifier(ModType.ManaRecoverRate)); + else if (!this.isCasting() && !this.isItemCasting()) + manaRegen = ((this.manaMax * MBServerStatics.MANA_REGEN_IDLE) * getRegenModifier(ModType.ManaRecoverRate)); + else + manaRegen = 0; + + if (!PlayerCharacter.CanBreathe(this)) + stamRegen = MBServerStatics.STAMINA_REGEN_SWIM; + else if ((!this.isCasting() && !this.isItemCasting()) || this.lastMovementState.equals(MovementState.FLYING)) + stamRegen = MBServerStatics.STAMINA_REGEN_IDLE * getRegenModifier(ModType.StaminaRecoverRate); + else + stamRegen =0 ; + break; + case SITTING: + healthRegen = ((this.healthMax * MBServerStatics.HEALTH_REGEN_SIT) + MBServerStatics.HEALTH_REGEN_SIT_STATIC) * getRegenModifier(ModType.HealthRecoverRate); + manaRegen = (this.manaMax * MBServerStatics.MANA_REGEN_SIT) * ( getRegenModifier(ModType.ManaRecoverRate)); + stamRegen = MBServerStatics.STAMINA_REGEN_SIT * getRegenModifier(ModType.StaminaRecoverRate); + break; + case RUNNING: + if (this.walkMode == true) { + healthRegen = ((this.healthMax * MBServerStatics.HEALTH_REGEN_WALK) + MBServerStatics.HEALTH_REGEN_IDLE_STATIC) * getRegenModifier(ModType.HealthRecoverRate); + manaRegen = this.manaMax * MBServerStatics.MANA_REGEN_WALK * getRegenModifier(ModType.ManaRecoverRate); + stamRegen = MBServerStatics.STAMINA_REGEN_WALK; + } else { + healthRegen =0; + manaRegen = 0; + + if (this.combat == true) + stamRegen = MBServerStatics.STAMINA_REGEN_RUN_COMBAT; + else + stamRegen = MBServerStatics.STAMINA_REGEN_RUN_NONCOMBAT; + } + break; + case FLYING: + + float seventyFive = this.staminaMax * .75f; + float fifty = this.staminaMax *.5f; + float twentyFive = this.staminaMax *.25f; + + if (this.getDesiredAltitude() == 0 && this.getAltitude() <= 10){ + if (this.isCombat()) + stamRegen = 0; + else + stamRegen = MBServerStatics.STAMINA_REGEN_IDLE * getRegenModifier(ModType.StaminaRecoverRate); + } + else if (!this.useFlyMoveRegen()){ + + healthRegen = ((this.healthMax * MBServerStatics.HEALTH_REGEN_IDLE) + MBServerStatics.HEALTH_REGEN_IDLE_STATIC) * ( getRegenModifier(ModType.HealthRecoverRate)); + + if (this.isCasting() || this.isItemCasting()) + healthRegen *= .75f; + // Characters regen mana when in only walk mode and idle + if (this.walkMode) + manaRegen = (this.manaMax * MBServerStatics.MANA_REGEN_IDLE + (this.getSpiMod() * .015f))* ( getRegenModifier(ModType.ManaRecoverRate)); + else if (!this.isCasting() && !this.isItemCasting()) + manaRegen = (this.manaMax * MBServerStatics.MANA_REGEN_IDLE + (this.getSpiMod() * .015f)) * ( getRegenModifier(ModType.ManaRecoverRate)); + else + manaRegen = 0; + + if (!this.isItemCasting() && !this.isCasting() || this.getTakeOffTime() != 0) + stamRegen = MBServerStatics.STAMINA_REGEN_FLY_IDLE; + else + stamRegen = -1f; + } + else + if (this.walkMode == true) { + healthRegen = ((this.healthMax * MBServerStatics.HEALTH_REGEN_WALK) + MBServerStatics.HEALTH_REGEN_IDLE_STATIC) * getRegenModifier(ModType.HealthRecoverRate); + manaRegen = ((this.manaMax * MBServerStatics.MANA_REGEN_WALK)+ (this.getSpiMod() * .015f)) * (getRegenModifier(ModType.ManaRecoverRate)); + stamRegen = MBServerStatics.STAMINA_REGEN_FLY_WALK; + } else { + healthRegen = 0; + manaRegen = 0; + if (this.isCombat()) + stamRegen = MBServerStatics.STAMINA_REGEN_FLY_RUN_COMBAT; + else + stamRegen = MBServerStatics.STAMINA_REGEN_FLY_RUN; + } + + float oldStamina = this.stamina.get(); + + if (FastMath.between(oldStamina, 0, twentyFive) && !this.wasTripped25){ + updateClient = true; + this.wasTripped25 = true; + this.wasTripped50 = false; + this.wasTripped75 = false; + }else if (FastMath.between(oldStamina, twentyFive, fifty) && !this.wasTripped50){ + updateClient = true; + this.wasTripped25 = false; + this.wasTripped50 = true; + this.wasTripped75 = false; + }else if (FastMath.between(oldStamina, fifty, seventyFive) && !this.wasTripped75){ + updateClient = true; + this.wasTripped25 = false; + this.wasTripped50 = false; + this.wasTripped75 = true; + } + break; + case SWIMMING: + if (this.walkMode == true) { + healthRegen = ((this.healthMax * MBServerStatics.HEALTH_REGEN_WALK) + MBServerStatics.HEALTH_REGEN_IDLE_STATIC) * getRegenModifier(ModType.HealthRecoverRate); + manaRegen = ((this.manaMax * MBServerStatics.MANA_REGEN_WALK)+ (this.getSpiMod() * .015f)) * ( getRegenModifier(ModType.ManaRecoverRate)); + stamRegen = MBServerStatics.STAMINA_REGEN_SWIM; + } else { + healthRegen = 0; + manaRegen = 0; + stamRegen = MBServerStatics.STAMINA_REGEN_SWIM; + + if (this.combat == true) + stamRegen += MBServerStatics.STAMINA_REGEN_RUN_COMBAT; + else + stamRegen += MBServerStatics.STAMINA_REGEN_RUN_NONCOMBAT; + } + break; + } + + // Are we drowning? + if ((this.getStamina() <= 0) + && (PlayerCharacter.CanBreathe(this) == false)) + healthRegen = (this.healthMax * -.03f); + + // Multiple regen values by current deltaTime + // Logger.info("", healthRegen + ""); + healthRegen *= getDeltaTime(); + manaRegen *= getDeltaTime(); + stamRegen *= getStamDeltaTime(); + + boolean workedHealth = false; + boolean workedMana = false; + boolean workedStamina = false; + + float old, mod; + while(!workedHealth || !workedMana || !workedStamina) { + if (!this.isAlive() || !this.isActive()) + return; + if (!workedHealth) { + old = this.health.get(); + mod = old + healthRegen; + if (mod > this.healthMax) + mod = healthMax; + else if (mod <= 0) { + if (this.isAlive.compareAndSet(true, false)) + killCharacter("Water"); + return; + } + workedHealth = this.health.compareAndSet(old, mod); + } + if (!workedStamina) { + old = this.stamina.get(); + mod = old + stamRegen; + if (mod > this.staminaMax) + mod = staminaMax; + else if (mod < 0) + mod = 0; + workedStamina = this.stamina.compareAndSet(old, mod); + } + if (!workedMana) { + old = this.mana.get(); + mod = old + manaRegen; + if (mod > this.manaMax) + mod = manaMax; + else if (mod < 0) + mod = 0; + workedMana = this.mana.compareAndSet(old, mod); + } + } + + if (updateClient) + this.syncClient(); + + // Reset this char's frame time. + this.lastUpdateTime = System.currentTimeMillis(); + this.lastStamUpdateTime = System.currentTimeMillis(); + + } + + public synchronized void updateStamRegen(long time) { + + boolean disable = true; + + if (disable) + return; + + float stamRegen = 0f; + + // Early exit if char is dead or disconnected + if ((this.isAlive() == false) + || (this.isActive() == false) || this.getLoc().x == 0 && this.getLoc().z == 0) + return; + + // Calculate Regen amount from last simulation tick + switch (this.movementState) { + + case IDLE: + if (!PlayerCharacter.CanBreathe(this)) + stamRegen = MBServerStatics.STAMINA_REGEN_SWIM; + else if ((!this.isCasting() && !this.isItemCasting()) || this.lastMovementState.equals(MovementState.FLYING)) + stamRegen = MBServerStatics.STAMINA_REGEN_IDLE * getRegenModifier(ModType.StaminaRecoverRate); + else + stamRegen =0 ; + break; + case SITTING: + stamRegen = MBServerStatics.STAMINA_REGEN_SIT * getRegenModifier(ModType.StaminaRecoverRate); + break; + case RUNNING: + if (this.walkMode == true) { + stamRegen = MBServerStatics.STAMINA_REGEN_WALK; + } else { + if (this.combat == true) + stamRegen = MBServerStatics.STAMINA_REGEN_RUN_COMBAT; + else + stamRegen = MBServerStatics.STAMINA_REGEN_RUN_NONCOMBAT; + } + break; + case FLYING: + + if (this.getDesiredAltitude() == 0 && this.getAltitude() <= 10){ + if (this.isCombat()) + stamRegen = 0; + else + stamRegen = MBServerStatics.STAMINA_REGEN_IDLE * getRegenModifier(ModType.StaminaRecoverRate); + } + else if (!this.isMoving()){ + + + if (!this.isItemCasting() && !this.isCasting() || this.getTakeOffTime() != 0) + stamRegen = MBServerStatics.STAMINA_REGEN_FLY_IDLE; + else + stamRegen = -1f; + + } + else + + if (this.walkMode == true) { + + stamRegen = MBServerStatics.STAMINA_REGEN_FLY_WALK; + } else { + if (this.isCombat()) + stamRegen = MBServerStatics.STAMINA_REGEN_FLY_RUN_COMBAT; + else + stamRegen = MBServerStatics.STAMINA_REGEN_FLY_RUN; + } + break; + case SWIMMING: + if (this.walkMode == true) { + stamRegen = MBServerStatics.STAMINA_REGEN_SWIM; + } else { + stamRegen = MBServerStatics.STAMINA_REGEN_SWIM; + } + break; + } + + + + + // Multiple regen values by current deltaTime + // Logger.info("", healthRegen + ""); + + stamRegen *= (time * .001f); + + + + boolean workedStamina = false; + + + float old, mod; + while( !workedStamina) { + if (!this.isAlive() || !this.isActive()) + return; + + if (!workedStamina) { + old = this.stamina.get(); + mod = old + stamRegen; + if (mod > this.staminaMax) + mod = staminaMax; + else if (mod < 0) + mod = 0; + workedStamina = this.stamina.compareAndSet(old, mod); + } + + } + + } + + public void syncClient() { + + ModifyHealthMsg modifyHealthMsg = new ModifyHealthMsg(null, this, 0, 1, 1, -1984683793, "", 0, 652920987); + //mhm.setOmitFromChat(0); + Dispatch dispatch = Dispatch.borrow(this, modifyHealthMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + + } + + public MovementState getMovementState() { + return movementState; + } + + public boolean isHasAnniversery() { + return hasAnniversery; + } + + public void setHasAnniversery(boolean hasAnniversery) { + DbManager.PlayerCharacterQueries.SET_ANNIVERSERY(this, hasAnniversery); + this.hasAnniversery = hasAnniversery; + } + + public int getSpamCount() { + return spamCount; + } + + public void setSpamCount(int spamCount) { + this.spamCount = spamCount; + } + + + public String getHash() { + return hash; + } + + public void setHash() { + + this.hash = DataWarehouse.hasher.encrypt(this.getObjectUUID()); + + // Write hash to player character table + + DataWarehouse.writeHash(DataRecordType.CHARACTER, this.getObjectUUID()); + } + + public AtomicInteger getGuildStatus() { + return guildStatus; + } + + public static int GetPlayerRealmTitle(PlayerCharacter player){ + + if (player.getGuild().isErrant()) + return 0; + if (!player.getGuild().isGuildLeader(player.getObjectUUID())) + return 0; + if (player.getGuild().getOwnedCity() == null) + return 10; + if (player.getGuild().getOwnedCity().getRealm() == null) + return 10; + if (player.getGuild().getOwnedCity().getRealm().getRulingCity() == null) + return 10; + + if (player.getGuild().getOwnedCity().getRealm().getRulingCity().getObjectUUID() != player.getGuild().getOwnedCity().getObjectUUID()) + return 10; + int realmTitle = 1; + if (player.getGuild().getSubGuildList() == null || player.getGuild().getSubGuildList().isEmpty()) + return 11; + for (Guild subGuild: player.getGuild().getSubGuildList()){ + if (subGuild.getOwnedCity() == null) + continue; + if (subGuild.getOwnedCity().getRealm() == null) + continue; + if (subGuild.getOwnedCity().getRealm().getRulingCity() == null) + continue; + if (subGuild.getOwnedCity().getRealm().getRulingCity().getObjectUUID() != subGuild.getOwnedCity().getObjectUUID()) + continue; + realmTitle++; + } + + if (realmTitle < 3) + return 11; + else if (realmTitle < 5) + return 12; + else + return 13; + } + public static void UpdateClientPlayerRank(PlayerCharacter pc){ + if (pc == null) + return; + boolean disable = true; + + if (disable) + return; + UpdateCharOrMobMessage ucm = new UpdateCharOrMobMessage(pc,2,pc.getRank()); + DispatchMessage.sendToAllInRange(pc, ucm); + } + + public void setLastRealmID(int lastRealmID) { + this.lastRealmID = lastRealmID; + } + + public int getLastRealmID() { + return lastRealmID; + } + + public int getSubRaceID() { + return subRaceID; + } + + public void setSubRaceID(int subRaceID) { + this.subRaceID = subRaceID; + } + + public ArrayList getGuildHistory() { + return guildHistory; + } + + public void setGuildHistory(ArrayList guildHistory) { + this.guildHistory = guildHistory; + } + + public void moveTo(Vector3fImmutable endLoc){ + this.setInBuilding(-1); + this.setInFloorID(-1); + MoveToPointMsg moveToMsg = new MoveToPointMsg(); + moveToMsg.setStartCoord(this.getLoc()); + moveToMsg.setEndCoord(endLoc); + moveToMsg.setInBuilding(-1); + moveToMsg.setUnknown01(-1); + moveToMsg.setSourceType(GameObjectType.PlayerCharacter.ordinal()); + moveToMsg.setSourceID(this.getObjectUUID()); + + Dispatch dispatch = Dispatch.borrow(this, moveToMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + + try { + MovementManager.movement(moveToMsg, this); + } catch (MsgSendException e) { + // TODO Auto-generated catch block + Logger.error("Player.MoveTo", this.getName() + " tripped error " + e.getMessage()); + } + + } + + public void updateScaleHeight(){ + + float strengthScale = 0; + float unknownScale1 = 0; + float unknownScale2 = 0; + float unknownScale3 = 0; + + float scaleHeight = 0; + + if ((int) this.statStrBase > 40) + strengthScale = ((int) this.statStrBase - 40)* 0.0024999999f; //Y scale ? + + unknownScale1 = (float) (((int) this.statStrBase * 0.0024999999f + strengthScale + 0.89999998) * race.getRaceType().getScaleHeight()); + strengthScale = (int) this.statStrBase * 0.0037499999f + strengthScale + 0.85000002f; //strengthScale is different for x and z + + unknownScale2 = strengthScale * race.getRaceType().getScaleHeight(); //x scale? + unknownScale3 = strengthScale * race.getRaceType().getScaleHeight(); //z Scale? + + + + scaleHeight = (1.5f + unknownScale1); + + + + this.characterHeight = scaleHeight; + + this.centerHeight = scaleHeight; + + } + + public int getOverFlowEXP() { + return overFlowEXP; + } + + public void setOverFlowEXP(int overFlowEXP) { + this.overFlowEXP = overFlowEXP; + } + + public static void GroundPlayer(PlayerCharacter groundee){ + if (groundee.getDesiredAltitude() == 0 && groundee.getAltitude() == 0) + return; + groundee.setAltitude(groundee.getAltitude()); + groundee.setDesiredAltitude(0); + groundee.setTakeOffTime(System.currentTimeMillis()); + + ChangeAltitudeMsg msg = ChangeAltitudeMsg.GroundPlayerMsg(groundee); + // force a landing + DispatchMessage.dispatchMsgToInterestArea(groundee, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + + } + + public MovementState getLastMovementState() { + return lastMovementState; + } + + public void setLastMovementState(MovementState lastMovementState) { + this.lastMovementState = lastMovementState; + } + @Override + public final void setIsCasting(final boolean isCasting) { + if (this.isCasting != isCasting) + this.update(); + this.isCasting = isCasting; + } + @Override + public void setItemCasting(boolean itemCasting) { + if (this.itemCasting != itemCasting) + this.dynamicUpdate(UpdateType.REGEN); + this.itemCasting = itemCasting; + } + + public void resetRegenUpdateTime(){ + this.lastUpdateTime = System.currentTimeMillis(); + this.lastStamUpdateTime = System.currentTimeMillis(); + } + + public float getCharacterHeight() { + return characterHeight; + } + + public void setCharacterHeight(float characterHeight) { + this.characterHeight = characterHeight; + } + + public void setCenterHeight(float centerHeight) { + this.centerHeight = centerHeight; + } + + public static boolean CanBreathe(PlayerCharacter breather){ + try{ + if (breather.isFlying()) + return true; + Zone zone = ZoneManager.findSmallestZone(breather.getLoc()); + + if (zone.getSeaLevel() != 0){ + + float localAltitude = breather.getLoc().y; + + + if (localAltitude + breather.characterHeight < zone.getSeaLevel() -2) + return false; + + if (breather.isMoving()){ + if (localAltitude + breather.characterHeight < zone.getSeaLevel()) + return false; + } + }else{ + if (breather.getLoc().y + breather.characterHeight < -2) + return false; + + if (breather.isMoving()){ + if (breather.getLoc().y + breather.characterHeight < 0) + return false; + } + } + + + }catch(Exception e){ + Logger.info(breather.getName() + e); + } + + + return true; + } + + public static boolean enterWater(PlayerCharacter enterer){ + + try{ + if (enterer.isFlying()) + return false; + + + + Zone zone = ZoneManager.findSmallestZone(enterer.getLoc()); + + if (zone.getSeaLevel() != 0){ + + float localAltitude = enterer.getLoc().y + enterer.characterHeight ; + + + if (localAltitude < zone.getSeaLevel()) + return true; + }else{ + if (enterer.getLoc().y + enterer.characterHeight < 0) + return true; + } + }catch(Exception e){ + Logger.info(enterer.getName() + e); + } + + return false; + + } + + public static boolean LeaveWater(PlayerCharacter leaver){ + + try{ + + + Zone zone = ZoneManager.findSmallestZone(leaver.getLoc()); + + float leaveWater = leaver.centerHeight; + + if (leaver.isMoving()) + leaveWater = 1f; + + + if (zone.getSeaLevel() != 0){ + + float localAltitude = leaver.getLoc().y; + + + if (localAltitude + leaveWater < zone.getSeaLevel()) + return false; + }else{ + if (leaver.getLoc().y + leaveWater < 0) + return false; + } + }catch(Exception e){ + Logger.info(leaver.getName() + e); + } + + return true; + } + + public boolean isEnteredWorld() { + return enteredWorld; + } + + public void setEnteredWorld(boolean enteredWorld) { + this.enteredWorld = enteredWorld; + } + + public long getChannelMute() { + return channelMute; + } + + public void setChannelMute(long channelMute) { + this.channelMute = channelMute; + } + + public boolean isLastSwimming() { + return lastSwimming; + } + + public boolean isTeleporting() { + return isTeleporting; + } + + public void setTeleporting(boolean isTeleporting) { + this.isTeleporting = isTeleporting; + } + + @Override + public final void teleport(final Vector3fImmutable targetLoc) { + locationLock.writeLock().lock(); + try{ + MovementManager.translocate(this, targetLoc,null); + }catch(Exception e){ + Logger.error(e); + }finally{ + locationLock.writeLock().unlock(); + } + } + + public ReadWriteLock getTeleportLock() { + return teleportLock; + } + + public static boolean CanBindToBuilding(PlayerCharacter player, int buildingID){ + if (buildingID == 0) + return false; + + Building bindBuilding = BuildingManager.getBuildingFromCache(buildingID); + + if (bindBuilding == null) + return false; + + if(!BuildingManager.playerCanManage(player, bindBuilding)) + return false; + + return true; + } + + public float getBargain(){ + float bargain = 0; + + + CharacterSkill bargainSkill = this.getSkills().get(engine.Enum.CharacterSkills.Bargaining.name()); + + if (bargainSkill != null) + bargain = bargainSkill.getModifiedAmountBeforeMods(); + + if (bargain > 100) + bargain = 100; + + bargain *= .01f; + + return bargain; + } +} diff --git a/src/engine/objects/PlayerFriends.java b/src/engine/objects/PlayerFriends.java new file mode 100644 index 00000000..2d2cdf08 --- /dev/null +++ b/src/engine/objects/PlayerFriends.java @@ -0,0 +1,122 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum.DispatchChannel; +import engine.gameManager.DbManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.msg.UpdateFriendStatusMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.HashSet; + +public class PlayerFriends { + + public int playerUID; + public int friendUID; + + public static HashMap > PlayerFriendsMap = new HashMap<>(); + + /** + * ResultSet Constructor + */ + + public PlayerFriends(ResultSet rs) throws SQLException { + this.playerUID = rs.getInt("playerUID"); + this.friendUID = rs.getInt("friendUID"); + + //cache player friends. + //hashset already created, just add to set. + if (PlayerFriendsMap.containsKey(playerUID)){ + HashSet playerFriendSet = PlayerFriendsMap.get(playerUID); + playerFriendSet.add(friendUID); + //hashset not yet created, create new set, and add to map. + }else{ + HashSet playerFriendSet = new HashSet<>(); + playerFriendSet.add(friendUID); + PlayerFriendsMap.put(this.playerUID, playerFriendSet); + } + + } + + public PlayerFriends(int playerUID, int friendUID) { + super(); + this.playerUID = playerUID; + this.friendUID = friendUID; + } + + public int getPlayerUID() { + return playerUID; + } + + public static void AddToFriends(int playerID, int friendID){ + HashSet friends = PlayerFriendsMap.get(playerID); + + if (friends != null){ + //already in friends list, don't do anything. + if (friends.contains(friendID)) + return; + + DbManager.PlayerCharacterQueries.ADD_FRIEND(playerID, friendID); + friends.add(friendID); + }else{ + friends = new HashSet<>(); + DbManager.PlayerCharacterQueries.ADD_FRIEND(playerID, friendID); + friends.add(friendID); + PlayerFriendsMap.put(playerID, friends); + } + } + + public static void RemoveFromFriends(int playerID, int friendID){ + + if (!CanRemove(playerID, friendID)) + return; + +HashSet friends = PlayerFriendsMap.get(playerID); + + if (friends != null){ + DbManager.PlayerCharacterQueries.REMOVE_FRIEND(playerID, friendID); + friends.remove(friendID); + } + } + + public static boolean CanRemove(int playerID, int toRemove){ + if (PlayerFriendsMap.get(playerID) == null) + return false; + + if (PlayerFriendsMap.get(playerID).isEmpty()) + return false; + + if (!PlayerFriendsMap.get(playerID).contains(toRemove)) + return false; + + return true; + } + + public static void SendFriendsStatus(PlayerCharacter player, boolean online ){ + HashSet friendsSet = PlayerFriends.PlayerFriendsMap.get(player.getObjectUUID()); + if (friendsSet != null){ + for(int friendID: friendsSet){ + PlayerCharacter friend = SessionManager.getPlayerCharacterByID(friendID); + if (friend == null) + continue; + UpdateFriendStatusMessage outMsg = new UpdateFriendStatusMessage(player); + outMsg.online = online; + Dispatch dispatch = Dispatch.borrow(friend, outMsg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY); + } + } + + } +} diff --git a/src/engine/objects/Portal.java b/src/engine/objects/Portal.java new file mode 100644 index 00000000..498e0921 --- /dev/null +++ b/src/engine/objects/Portal.java @@ -0,0 +1,169 @@ +package engine.objects; + +import engine.Enum.RunegateType; +import engine.InterestManagement.WorldGrid; +import engine.gameManager.ConfigManager; +import engine.job.JobScheduler; +import engine.jobs.CloseGateJob; +import engine.math.Vector3fImmutable; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.util.HashSet; + +/* A Runegate object holds an array of these + * portals. This class controls their triggers + * and visual effects. + */ + +public class Portal { + + private boolean active; + private RunegateType sourceGateType; + private RunegateType portalType; + private RunegateType destinationGateType; + private final Vector3fImmutable portalLocation; + private long lastActive = 0; + + public Portal(RunegateType gateType, RunegateType portalType, RunegateType destinationGate) { + + Building gateBuilding; + + this.active = false; + this.sourceGateType = gateType; + this.destinationGateType = destinationGate; + this.portalType = portalType; + + gateBuilding = this.sourceGateType.getGateBuilding(); + + if (gateBuilding == null) { + Logger.error("Gate building " + this.sourceGateType.getGateUUID() + " for " + this.sourceGateType.name() + " missing"); + } + + this.portalLocation = gateBuilding.getLoc().add(new Vector3fImmutable(portalType.getOffset().x, 6, portalType.getOffset().y)); + } + + public boolean isActive() { + + return this.active; + + } + + public void deactivate() { + + Building sourceBuilding; + + // Remove effect bit from the runegate building, which turns off this + // portal type's particle effect + + sourceBuilding = this.sourceGateType.getGateBuilding(); + sourceBuilding.removeEffectBit(portalType.getEffectFlag()); + this.active = false; + sourceBuilding.updateEffects(); + } + + public void activate(boolean autoClose) { + + Building sourceBuilding; + + + // Apply effect bit to the runegate building, which turns on this + // portal type's particle effect + + sourceBuilding = this.sourceGateType.getGateBuilding(); + sourceBuilding.addEffectBit(portalType.getEffectFlag()); + this.lastActive = System.currentTimeMillis(); + this.active = true; + + // Do not update effects at bootstrap as it + // tries to send a dispatch. + + if (ConfigManager.worldServer.isRunning == true) + sourceBuilding.updateEffects(); + + if (autoClose == true) { + CloseGateJob cgj = new CloseGateJob(sourceBuilding, portalType); + JobScheduler.getInstance().scheduleJob(cgj, MBServerStatics.RUNEGATE_CLOSE_TIME); + } + } + + public void collide() { + + HashSet playerList; + + playerList = WorldGrid.getObjectsInRangePartial(this.portalLocation, 2, MBServerStatics.MASK_PLAYER); + + if (playerList.isEmpty()) + return; + + for (AbstractWorldObject player : playerList) { + + onEnter((PlayerCharacter) player); + + } + } + + public void onEnter(PlayerCharacter player) { + + if (player.getTimeStamp("lastMoveGate") < this.lastActive) + return; + Building gateBuilding; + + gateBuilding = destinationGateType.getGateBuilding(); + + if (gateBuilding != null){ + player.teleport(gateBuilding.getLoc()); + player.setSafeMode(); + } + + } + + /** + * @return the sourceGateType + */ + public RunegateType getSourceGateType() { + return sourceGateType; + } + + /** + * @param sourceGateType the sourceGateType to set + */ + public void setSourceGateType(RunegateType sourceGateType) { + this.sourceGateType = sourceGateType; + } + + /** + * @return the portalType + */ + public RunegateType getPortalType() { + return portalType; + } + + /** + * @param portalType the portalType to set + */ + public void setPortalType(RunegateType portalType) { + this.portalType = portalType; + } + + /** + * @return the destinationGateType + */ + public RunegateType getDestinationGateType() { + return destinationGateType; + } + + /** + * @param destinationGateType the destinationGateType to set + */ + public void setDestinationGateType(RunegateType destinationGateType) { + this.destinationGateType = destinationGateType; + } + + /** + * @return the portalLocation + */ + public Vector3fImmutable getPortalLocation() { + return portalLocation; + } +} diff --git a/src/engine/objects/PowerGrant.java b/src/engine/objects/PowerGrant.java new file mode 100644 index 00000000..fb5a8c31 --- /dev/null +++ b/src/engine/objects/PowerGrant.java @@ -0,0 +1,138 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; + + + + +public class PowerGrant extends AbstractGameObject { + + private int token; + private ConcurrentHashMap runeGrants = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private static ConcurrentHashMap grantedPowers = null; + + /** + * ResultSet Constructor + */ + public PowerGrant(ResultSet rs) throws SQLException { + super(); + this.token = rs.getInt("powerToken"); + runeGrants.put(rs.getInt("runeID"), rs.getShort("grantAmount")); + } + + /* + * Getters + */ + + public ConcurrentHashMap getRuneGrants() { + return this.runeGrants; + } + + public int getToken() { + return this.token; + } + + private void addRuneGrant(int runeID, short amount) { + this.runeGrants.put(runeID, amount); + } + + + /* + * Database + */ + + public static Short getGrantedTrains(int token, PlayerCharacter pc) { + if (pc == null) + return (short) 0; + + if (PowerGrant.grantedPowers == null) + fillGrantedPowers(); + + if (PowerGrant.grantedPowers.containsKey(token)) { + PowerGrant pg = PowerGrant.grantedPowers.get(token); + ConcurrentHashMap runeGrants = pg.runeGrants; + ArrayList toks = new ArrayList<>(); + + //get race ID + Race race = pc.getRace(); + if (race != null) + toks.add(race.getRaceRuneID()); + + //get baseClass ID + BaseClass bc = pc.getBaseClass(); + if (bc != null) + toks.add(bc.getObjectUUID()); + + //get promoClass ID + PromotionClass pcc = pc.getPromotionClass(); + if (pcc != null) + toks.add(pcc.getObjectUUID()); + + //get promotion and base class combined ID + if (bc != null && pcc != null) + toks.add( ((pcc.getObjectUUID() * 10) + bc.getObjectUUID()) ); + + //get any other rune IDs + ArrayList runes = pc.getRunes(); + for (CharacterRune rune : runes) + toks.add(rune.getRuneBaseID()); + + //Add any power bonuses granted from runes up + short amount = (short) 0; + for (Integer tok : toks) { + if (runeGrants.containsKey(tok)) + amount += runeGrants.get(tok); + } + + return amount; + } else + return (short) 0; + } + + public static void fillGrantedPowers() { + PowerGrant.grantedPowers = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + PreparedStatementShared ps = null; + try { + ps = prepareStatement("SELECT * FROM static_power_powergrant"); + ResultSet rs = ps.executeQuery(); + if (PowerGrant.grantedPowers.size() > 0) { + rs.close(); + return; + } + while (rs.next()) { + int token = rs.getInt("powerToken"); + PowerGrant pg = null; + if (PowerGrant.grantedPowers.containsKey(token)) { + pg = PowerGrant.grantedPowers.get(token); + pg.addRuneGrant(rs.getInt("runeID"), rs.getShort("grantAmount")); + } else { + pg = new PowerGrant(rs); + PowerGrant.grantedPowers.put(token, pg); + } + } + rs.close(); + } catch (SQLException e) { + Logger.error( "SQL Error number: " + e.getErrorCode(), e); + } finally { + ps.release(); + } + } + + @Override + public void updateDatabase() {} +} diff --git a/src/engine/objects/PowerReq.java b/src/engine/objects/PowerReq.java new file mode 100644 index 00000000..7c5548d3 --- /dev/null +++ b/src/engine/objects/PowerReq.java @@ -0,0 +1,194 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.gameManager.PowersManager; +import engine.powers.PowersBase; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.concurrent.ConcurrentHashMap; + + +public class PowerReq extends AbstractGameObject implements Comparable { + + private PowersBase powersBase; + private int token; + private short level; + private ConcurrentHashMap powerReqs; + private ConcurrentHashMap skillReqs; + + private static ConcurrentHashMap> runePowers = fillRunePowers(); + private static ArrayList powersForAll = new ArrayList<>(); + + /** + * No Table ID Constructor + */ + public PowerReq(PowersBase powersBase, short level, ConcurrentHashMap powerReqs, ConcurrentHashMap skillReqs) { + super(); + this.powersBase = powersBase; + this.level = level; + this.powerReqs = powerReqs; + this.skillReqs = skillReqs; + if (this.powersBase != null) + this.token = this.powersBase.getToken(); + else + this.token = 0; + } + + /** + * Normal Constructor + */ + public PowerReq(PowersBase powersBase, short level, ConcurrentHashMap powerReqs, ConcurrentHashMap skillReqs, int newUUID) { + super(newUUID); + this.powersBase = powersBase; + this.level = level; + this.powerReqs = powerReqs; + this.skillReqs = skillReqs; + if (this.powersBase != null) + this.token = this.powersBase.getToken(); + else + this.token = 0; + } + + /** + * ResultSet Constructor + */ + public PowerReq(ResultSet rs) throws SQLException { + super(rs); + this.token = rs.getInt("powerToken"); + this.powersBase = PowersManager.getPowerByToken(this.token); + this.level = rs.getShort("level"); + int type = rs.getInt("type"); + this.powerReqs = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + this.skillReqs = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + if (type == 1) + this.skillReqs.put(rs.getInt("requiredToken"), rs.getByte("requiredAmount")); + else if (type == 2) + this.powerReqs.put(rs.getInt("requiredToken"), rs.getByte("requiredAmount")); + } + + /* + * Getters + */ + public PowersBase getPowersBase() { + if (this.powersBase == null) { + this.powersBase = PowersManager.getPowerByToken(this.token); + } + return this.powersBase; + } + + public short getLevel() { + return this.level; + } + + public ConcurrentHashMap getPowerReqs() { + return this.powerReqs; + } + + public ConcurrentHashMap getSkillReqs() { + return this.skillReqs; + } + + public int getToken() { + return this.token; + } + + private void addPower(int token, byte amount) { + this.powerReqs.put(token, amount); + } + + private void addSkill(int token, byte amount) { + this.skillReqs.put(token, amount); + } + + + @Override + public int compareTo(PowerReq n) throws ClassCastException { + if (n.level == this.level) + return 0; + else if (this.level > n.level) + return 1; + else + return -1; + } + + + /* + * Database + */ + + public static ArrayList getPowerReqsForRune(int id) { +// if (PowerReq.runePowers == null) +// fillRunePowers(); + if (PowerReq.runePowers.containsKey(id)) + return PowerReq.runePowers.get(id); + return new ArrayList<>(); + } + + public static ConcurrentHashMap> fillRunePowers() { + PowerReq.runePowers = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + PreparedStatementShared ps = null; + try { + ps = prepareStatement("SELECT * FROM static_power_powerrequirement"); + ResultSet rs = ps.executeQuery(); + if (PowerReq.runePowers.size() > 0) { + rs.close(); + return PowerReq.runePowers; + } + while (rs.next()) { + ArrayList runePR = null; + int runeID = rs.getInt("runeID"); + int token = rs.getInt("powerToken"); + if (PowerReq.runePowers.containsKey(runeID)) + runePR = PowerReq.runePowers.get(runeID); + else { + runePR = new ArrayList<>(); + PowerReq.runePowers.put(runeID, runePR); + } + boolean found = false; + for (PowerReq pr : runePR) { + if (pr.token == token) { + int type = rs.getInt("type"); + if (type == 1) + pr.addSkill(rs.getInt("requiredToken"), rs.getByte("requiredAmount")); + else + pr.addPower(rs.getInt("requiredToken"), rs.getByte("requiredAmount")); + found = true; + } + } + if (!found) { + PowerReq pr = new PowerReq(rs); + runePR.add(pr); + } + } + rs.close(); + + //order the lists by level so prerequisites are met + for (ArrayList runePR : PowerReq.runePowers.values()) { + Collections.sort(runePR); + } + } catch (SQLException e) { + Logger.error( "SQL Error number: " + e.getErrorCode(), e); + } finally { + ps.release(); + } + return PowerReq.runePowers; + } + + @Override + public void updateDatabase() { + + } +} \ No newline at end of file diff --git a/src/engine/objects/PowersBaseAttribute.java b/src/engine/objects/PowersBaseAttribute.java new file mode 100644 index 00000000..5a479a0b --- /dev/null +++ b/src/engine/objects/PowersBaseAttribute.java @@ -0,0 +1,65 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + package engine.objects; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class PowersBaseAttribute extends AbstractGameObject { + + private final short attributeID; + private final short modValue; + private final short castTime; + private final short duration; + private final short recycleTime; + + /** + * ResultSet Constructor + */ + public PowersBaseAttribute(ResultSet rs) throws SQLException { + super(rs); + + this.attributeID = rs.getShort("attributeID"); + this.modValue = rs.getShort("modValue"); + this.castTime = rs.getShort("castTime"); + this.duration = rs.getShort("duration"); + this.recycleTime = rs.getShort("recycleTime"); + } + + /* + * Getters + */ + public short getAttributeID() { + return attributeID; + } + + public short getModValue() { + return modValue; + } + + public short getCastTime() { + return castTime; + } + + public short getDuration() { + return duration; + } + + public short getRecycleTime() { + return recycleTime; + } + + + @Override + public void updateDatabase() { + // TODO Create update logic. + } +} diff --git a/src/engine/objects/PreparedStatementShared.java b/src/engine/objects/PreparedStatementShared.java new file mode 100644 index 00000000..436956fe --- /dev/null +++ b/src/engine/objects/PreparedStatementShared.java @@ -0,0 +1,1264 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.gameManager.DbManager; +import engine.job.JobScheduler; +import engine.jobs.BasicScheduledJob; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.*; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.concurrent.ConcurrentHashMap; + +/** + * A thread-safe sharing implementation of {@link PreparedStatement}. + *

+ * All of the methods from the PreparedStatement interface simply check to see + * that the PreparedStatement is active, and call the corresponding method on + * that PreparedStatement. + * + * @author Burfo + * @see PreparedStatement + * + **/ +public class PreparedStatementShared implements PreparedStatement { + private static final ConcurrentHashMap> statementList = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private static final ArrayList statementListDelegated = new ArrayList<>(); + private static final String ExceptionMessage = "PreparedStatementShared object " + "was accessed after being released."; + private static boolean debuggingIsOn; + + private PreparedStatement ps = null; + private int sqlHash; + private String sql; + private long delegatedTime; + + //debugging variables + private StackTraceElement[] stackTrace; + private DebugParam[] variables; + private String filteredSql; + + private class DebugParam { + private Object debugObject; + private boolean valueAssigned; + + public DebugParam(Object debugObject){ + this.debugObject = debugObject; + valueAssigned = true; + } + + public Object getDebugObject(){ + return debugObject; + } + + public boolean isValueAssigned(){ + return valueAssigned; + } + } + + @Override + public boolean isCloseOnCompletion() { + return true; + } + @Override + public void closeOnCompletion() { + Logger.warn( "Prepared Statement Closed"); + } + + /** + * Generates a new PreparedStatementShared based on the specified sql. + * + * @param sql + * Query string to generate the PreparedStatement + * @throws SQLException + **/ + public PreparedStatementShared(String sql) throws SQLException { + this.sqlHash = sql.hashCode(); + this.sql = sql; + this.delegatedTime = System.currentTimeMillis(); + this.ps = getFromPool(sql, sqlHash); + if (this.ps == null) { + this.ps = createNew(sql, sqlHash); + } + + + + if (debuggingIsOn) { + //see if there are any '?' in the statement that are not bind variables + //and filter them out. + boolean isString = false; + char[] sqlString = this.sql.toCharArray(); + for (int i = 0; i < sqlString.length; i++){ + if (sqlString[i] == '\'') + isString = !isString; + //substitute the ? with an unprintable character if is in a string + if (sqlString[i] == '?' && isString) + sqlString[i] = '\u0007'; + } + this.filteredSql = new String(sqlString); + + //find out how many variables are present in statement. + int count = 0; + int index = -1; + while ((index = filteredSql.indexOf('?',index+1)) != -1){ + count++; + } + + //create variables array with size equal to count of variables + this.variables = new DebugParam[count]; + + this.stackTrace = Thread.currentThread().getStackTrace(); + + } else { + this.stackTrace = null; + this.variables = null; + this.filteredSql = null; + } + + synchronized (statementListDelegated) { + statementListDelegated.add(this); + } + + } + + private static PreparedStatement getFromPool(String sql, int sqlHash) throws SQLException { + PreparedStatement ps = null; + + if (statementList.containsKey(sqlHash)) { + LinkedList list = statementList.get(sqlHash); + if (list == null) { // Shouldn't happen b/c no keys are ever removed + throw new AssertionError("list cannot be null."); + } + boolean success = false; + synchronized (list) { + do { + ps = list.pollFirst(); + if (ps == null) { + break; + } + if (ps.isClosed()) { // should rarely happen + Logger.warn("A closed PreparedStatement was removed " + + "from AbstractGameObject statementList. " + "SQL: " + sql); + } else { + success = true; + } + } while (!success); + } + + if (ps != null) { + if (MBServerStatics.DB_DEBUGGING_ON_BY_DEFAULT) { + Logger.info("Found cached PreparedStatement for SQL hash: " + sqlHash + + " SQL String: " + sql); + } + } + } + return ps; + } + + private static PreparedStatement createNew(String sql, int sqlHash) throws SQLException { + statementList.putIfAbsent(sqlHash, new LinkedList<>()); + return DbManager.prepareStatement(sql); + } + + /** + * Releases the use of a PreparedStatementShared that was generated by + * {@link AbstractGameObject#prepareStatement}, making it available for use + * by another query. + *

+ * Do not utilize or modify the object after calling this method. + *

+ * Example: + * + *

+	 * @code
+	 * PreparedStatementShared ps = prepareStatement(...);
+	 * ps.executeUpdate();
+	 * ps.release();
+	 * ps = null;}
+	 * 
+ **/ + public void release() { + if (this.ps == null) { + return; + } // nothing to release + if (statementListDelegated.contains(this)) { + synchronized (statementListDelegated) { + statementListDelegated.remove(this); + } + try { + if (this.ps.isClosed()) { + return; + } + this.ps.clearParameters(); + this.variables = null; + } catch (SQLException ignore) { + } + + // add back to pool + LinkedList list = statementList.get(this.sqlHash); + if (list == null) { + return; + } + synchronized (list) { + list.add(this.ps); + } + } + // clear values from this object so caller cannot use it after it has + // been released + this.ps = null; + this.sqlHash = 0; + this.sql = ""; + this.delegatedTime = 0; + this.stackTrace = null; + } + + /** + * Determines if the object is in a usable state. + * + * @return True if the object is in a useable state. + **/ + public boolean isUsable() { + if (ps == null) { + return false; + } + try { + if (ps.isClosed()) { + return false; + } + } catch (SQLException e) { + return false; + } + return true; + } + + private String getTraceInfo() { + if (stackTrace == null) { + return ""; + } + + if (stackTrace.length > 3) { + return stackTrace[3].getClassName() + '.' + stackTrace[3].getMethodName(); + } else if (stackTrace.length == 0) { + return ""; + } else { + return stackTrace[stackTrace.length - 1].getClassName() + '.' + stackTrace[stackTrace.length - 1].getMethodName(); + } + } + + public static void submitPreparedStatementsCleaningJob() { + JobScheduler.getInstance().scheduleJob(new BasicScheduledJob("cleanUnreleasedStatements", PreparedStatementShared.class), 1000 * 60 * 2); // 2 + // minutes + } + + public static void cleanUnreleasedStatements() { + long now = System.currentTimeMillis(); + long timeLimit = 120000; // 2 minutes + + synchronized (statementListDelegated) { + Iterator iterator = statementListDelegated.iterator(); + while (iterator.hasNext()) { + PreparedStatementShared pss = iterator.next(); + if ((pss.delegatedTime + timeLimit) >= now) { + continue; + } + iterator.remove(); + + Logger.warn("Forcefully released after being held for > 2 minutes." + " SQL STRING: \"" + + pss.sql + "\" METHOD: " + pss.getTraceInfo()); + } + } + + submitPreparedStatementsCleaningJob(); // resubmit + } + + @Override + public boolean equals(Object obj) { + if (ps == null || obj == null) { + return false; + } + + if (obj instanceof PreparedStatementShared) { + return this.ps.equals(((PreparedStatementShared) obj).ps); + } + + if (obj instanceof PreparedStatement) { + return this.ps.equals(obj); + } + + return false; + } + + @Override + public String toString(){ + if (!debuggingIsOn || variables == null) { + return "SQL: " + this.sql + " (enable DB debugging for more data)"; + } + + String out; + + out = "SQL: [" + this.sql + "] "; + out += "VARIABLES[count=" + variables.length + "]: "; + + for (int i=0; i variables.length){ + throw new SQLException("Parameter index of " + parameterIndex + + " exceeds actual parameter count of " + this.variables.length); + } + + this.variables[parameterIndex-1] = new DebugParam(obj); + } + + private void logExceptionAndRethrow(SQLException e) throws SQLException { + Logger.error("SQL operation failed: (" + + e.getMessage() + ") " + this.toString(), e); + throw e; + } + + @Override + public void clearParameters() throws SQLException { + if (this.ps == null) { + throw new SQLException(ExceptionMessage); + } + + this.ps.clearParameters(); + for (int i=0; i")); + this.ps.setAsciiStream(parameterIndex, x); + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { + if (this.ps == null) { + throw new SQLException(ExceptionMessage); + } + saveObject(parameterIndex, (x==null?"NULL":"")); + this.ps.setBinaryStream(parameterIndex, x); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { + if (this.ps == null) { + throw new SQLException(ExceptionMessage); + } + saveObject(parameterIndex, (x==null?"NULL":"")); + this.ps.setBlob(parameterIndex, x); + } + + @Override + public void setBlob(int parameterIndex, InputStream x, long length) throws SQLException { + if (this.ps == null) { + throw new SQLException(ExceptionMessage); + } + saveObject(parameterIndex, (x==null?"NULL":"")); + this.ps.setCharacterStream(parameterIndex, reader); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException { + if (this.ps == null) { + throw new SQLException(ExceptionMessage); + } + saveObject(parameterIndex, (reader==null?"NULL":"")); + this.ps.setClob(parameterIndex, reader); + } + + @Override + public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { + if (this.ps == null) { + throw new SQLException(ExceptionMessage); + } + saveObject(parameterIndex, (reader==null?"NULL":"")); + this.ps.setNCharacterStream(parameterIndex, value); + } + + @Override + public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { + if (this.ps == null) { + throw new SQLException(ExceptionMessage); + } + saveObject(parameterIndex, (value==null?"NULL":"")); + this.ps.setNClob(parameterIndex, value); + } + + @Override + public void setNClob(int parameterIndex, Reader reader) throws SQLException { + if (this.ps == null) { + throw new SQLException(ExceptionMessage); + } + saveObject(parameterIndex, (reader==null?"NULL":"")); + this.ps.setNClob(parameterIndex, reader); + } + + @Override + public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { + if (this.ps == null) { + throw new SQLException(ExceptionMessage); + } + saveObject(parameterIndex, (reader==null?"NULL":" iface) throws SQLException { + if (this.ps == null) { + throw new SQLException(ExceptionMessage); + } + return this.ps.isWrapperFor(iface); + } + + @Override + public T unwrap(Class iface) throws SQLException { + if (this.ps == null) { + throw new SQLException(ExceptionMessage); + } + return this.ps.unwrap(iface); + } + +} diff --git a/src/engine/objects/ProducedItem.java b/src/engine/objects/ProducedItem.java new file mode 100644 index 00000000..1284384d --- /dev/null +++ b/src/engine/objects/ProducedItem.java @@ -0,0 +1,236 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum.DispatchChannel; +import engine.gameManager.PowersManager; +import engine.net.DispatchMessage; +import engine.net.client.msg.ItemProductionMsg; +import engine.powers.EffectsBase; +import org.joda.time.DateTime; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Date; + +public class ProducedItem { + + private int ID; + private int npcUID; + private int itemBaseID; + private DateTime dateToUpgrade; + private boolean isRandom; + + private String prefix; + private String suffix; + private String name; + private int amount; + private int producedItemID = 0; + private boolean inForge; + private int value; + + private int playerID; + + + + /** + * ResultSet Constructor + */ + + public ProducedItem(ResultSet rs) throws SQLException { + this.ID = rs.getInt("ID"); + this.npcUID = rs.getInt("npcUID"); + this.itemBaseID = rs.getInt("itemBaseID"); + + Date sqlDateTime = rs.getTimestamp("dateToUpgrade"); + + if (sqlDateTime != null) + this.dateToUpgrade = new DateTime(sqlDateTime); + else + dateToUpgrade = null; + this.isRandom = rs.getBoolean("isRandom"); + this.prefix = rs.getString("prefix"); + this.suffix = rs.getString("suffix"); + this.name = rs.getString("name"); + this.inForge = rs.getBoolean("inForge"); + this.value = rs.getInt("value"); + this.playerID = rs.getInt("playerID"); + } + + public ProducedItem(int ID,int npcUID, int itemBaseID, DateTime dateToUpgrade, boolean isRandom, String prefix, String suffix, String name, int playerID) { + super(); + this.ID = ID; + this.npcUID = npcUID; + this.itemBaseID = itemBaseID; + this.dateToUpgrade = dateToUpgrade; + this.isRandom = isRandom; + this.prefix = prefix; + this.suffix = suffix; + this.name = name; + this.value = 0; + this.playerID = playerID; + + + + } + + public int getNpcUID() { + return npcUID; + } + public DateTime getDateToUpgrade() { + return dateToUpgrade; + } + + public int getItemBaseID() { + return itemBaseID; + } + + public void setItemBaseID(int itemBaseID) { + this.itemBaseID = itemBaseID; + } + + public boolean isRandom() { + return isRandom; + } + + public void setRandom(boolean isRandom) { + this.isRandom = isRandom; + } + + public String getPrefix() { + return prefix; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + public String getSuffix() { + return suffix; + } + + public void setSuffix(String suffix) { + this.suffix = suffix; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getID() { + return ID; + } + + public void setID(int iD) { + ID = iD; + } + + public int getAmount() { + return amount; + } + + public void setAmount(int amount) { + this.amount = amount; + } + + + public int getProducedItemID() { + return producedItemID; + } + + public void setProducedItemID(int producedItemID) { + this.producedItemID = producedItemID; + } + + public boolean isInForge() { + return inForge; + } + + public void setInForge(boolean inForge) { + this.inForge = inForge; + } + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } + + public boolean finishProduction(){ + NPC npc = NPC.getFromCache(this.getNpcUID()); + + if (npc == null) + return false; + + + //update the client to ID the item in the window when item finishes rolling. + //If there is more than 1 item left to roll, complete the item and throw it in inventory + //and reproduce next item. + + try{ + + if(this.getAmount() > 1){ + this.setAmount(this.getAmount() - 1); + npc.completeItem(this.getProducedItemID()); + + int pToken = 0; + int sToken = 0; + + if (!this.isRandom()){ + EffectsBase eb = PowersManager.getEffectByIDString(this.getPrefix()); + if (eb != null) + pToken = eb.getToken(); + eb = PowersManager.getEffectByIDString(this.getSuffix()); + if (eb != null) + sToken = eb.getToken(); + + } + + Item item = npc.produceItem(0,this.getAmount(),this.isRandom(),pToken,sToken,this.getName(),this.getItemBaseID()); + + if (item == null) + return false; + + }else{ + + //update item to complete + MobLoot targetItem = MobLoot.getFromCache(this.getProducedItemID()); + + if (targetItem == null) + return false; + + ItemProductionMsg outMsg = new ItemProductionMsg(npc.getBuilding(), npc, targetItem, 8, true); + + + + DispatchMessage.dispatchMsgToInterestArea(npc, outMsg, DispatchChannel.SECONDARY, 700, false, false); + } + + }catch(Exception e){ + Logger.error(e); + return false; + } + return true; + } + + public int getPlayerID() { + return playerID; + } + + + +} diff --git a/src/engine/objects/PromotionClass.java b/src/engine/objects/PromotionClass.java new file mode 100644 index 00000000..dcccda72 --- /dev/null +++ b/src/engine/objects/PromotionClass.java @@ -0,0 +1,203 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum.GameObjectType; +import engine.gameManager.DbManager; +import engine.net.ByteBufferWriter; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; + + +public class PromotionClass extends AbstractGameObject { + + private final String name; + private final String description; + private int token = 0; + + private final float healthMod; + private final float manaMod; + private final float staminaMod; + + private final ArrayList allowedRunes; + private final ArrayList skillsGranted; + private final ArrayList powersGranted; + private final ArrayList effectsGranted; + private final ArrayList effectsGrantedFighter; + private final ArrayList effectsGrantedHealer; + private final ArrayList effectsGrantedRogue; + private final ArrayList effectsGrantedMage; + private ArrayList effectsList = new ArrayList<>(); + + /** + * No Table ID Constructor + */ + public PromotionClass(String name, String description, + ArrayList allowedRunes, ArrayList skillsGranted, ArrayList powersGranted) { + super(); + this.name = name; + this.description = description; + this.allowedRunes = allowedRunes; + this.skillsGranted = skillsGranted; + this.powersGranted = powersGranted; + this.healthMod = 0f; + this.manaMod = 0f; + this.staminaMod = 0f; + this.effectsGranted = new ArrayList<>(); + this.effectsGrantedFighter = new ArrayList<>(); + this.effectsGrantedHealer = new ArrayList<>(); + this.effectsGrantedRogue = new ArrayList<>(); + this.effectsGrantedMage = new ArrayList<>(); + } + + /** + * Normal Constructor + */ + public PromotionClass(String name, String description, + ArrayList allowedRunes, ArrayList skillsGranted, ArrayList powersGranted, int newUUID) { + super(newUUID); + this.name = name; + this.description = description; + this.allowedRunes = allowedRunes; + this.skillsGranted = skillsGranted; + this.powersGranted = powersGranted; + this.healthMod = 0f; + this.manaMod = 0f; + this.staminaMod = 0f; + this.effectsGranted = new ArrayList<>(); + this.effectsGrantedFighter = new ArrayList<>(); + this.effectsGrantedHealer = new ArrayList<>(); + this.effectsGrantedRogue = new ArrayList<>(); + this.effectsGrantedMage = new ArrayList<>(); + } + + /** + * ResultSet Constructor + */ + public PromotionClass(ResultSet rs) throws SQLException { + super(rs); + + this.name = rs.getString("name"); + this.description = rs.getString("description"); + this.token = rs.getInt("token"); + this.healthMod = rs.getFloat("healthMod"); + this.manaMod = rs.getFloat("manaMod"); + this.staminaMod = rs.getFloat("staminaMod"); + this.allowedRunes = DbManager.PromotionQueries.GET_ALLOWED_RUNES(this); + this.skillsGranted = DbManager.SkillReqQueries.GET_REQS_FOR_RUNE(this.getObjectUUID()); + this.powersGranted = PowerReq.getPowerReqsForRune(this.getObjectUUID()); + this.effectsGranted = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE(this.getObjectUUID()); + this.effectsGrantedFighter = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE((this.getObjectUUID() * 10) + 2500); + this.effectsGrantedHealer = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE((this.getObjectUUID() * 10) + 2501); + this.effectsGrantedRogue = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE((this.getObjectUUID() * 10) + 2502); + this.effectsGrantedMage = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE((this.getObjectUUID() * 10) + 2503); + this.effectsList = DbManager.MobBaseQueries.GET_RUNEBASE_EFFECTS(this.getObjectUUID()); + + + } + + /* + * Getters + */ + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public int getToken() { + return this.token; + } + + public float getHealthMod() { + return this.healthMod; + } + + public float getManaMod() { + return this.manaMod; + } + + public float getStaminaMod() { + return this.staminaMod; + } + + public boolean isAllowedRune(int token) { + for (int b : this.allowedRunes) { + if (token == b) { + return true; + } + } + return false; + } + + public ArrayList getRuneList() { + return this.allowedRunes; + } + + public ArrayList getSkillsGranted() { + return this.skillsGranted; + } + + public ArrayList getPowersGranted() { + return this.powersGranted; + } + + public ArrayList getEffectsGranted() { + return this.effectsGranted; + } + + public ArrayList getEffectsGranted(int baseClassID) { + if (baseClassID == 2500) + return this.effectsGrantedFighter; + else if (baseClassID == 2501) + return this.effectsGrantedHealer; + else if (baseClassID == 2502) + return this.effectsGrantedRogue; + else if (baseClassID == 2503) + return this.effectsGrantedMage; + else + return new ArrayList<>(); + } + + /* + * Serializing + */ + + public static void serializeForClientMsg(PromotionClass promotionClass, ByteBufferWriter writer) { + writer.putInt(3); // For BaseClass + writer.putInt(0); // Pad + writer.putInt(promotionClass.getObjectUUID()); + writer.putInt(promotionClass.getObjectType().ordinal()); + writer.putInt(promotionClass.getObjectUUID()); + } + + @Override + public void updateDatabase() { + // TODO Create update logic. + } + + public static PromotionClass GetPromtionClassFromCache(int runeID){ + if (runeID == 0) + return null; + return (PromotionClass) DbManager.getFromCache(GameObjectType.PromotionClass, runeID); + } + + public ArrayList getEffectsList() { + return effectsList; + } + + public void setEffectsList(ArrayList effectsList) { + this.effectsList = effectsList; + } +} diff --git a/src/engine/objects/Race.java b/src/engine/objects/Race.java new file mode 100644 index 00000000..28a3467f --- /dev/null +++ b/src/engine/objects/Race.java @@ -0,0 +1,368 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum; +import engine.Enum.RaceType; +import engine.gameManager.DbManager; +import engine.net.ByteBufferWriter; +import engine.server.MBServerStatics; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.concurrent.ConcurrentHashMap; + +public class Race { + + // Local class cache + + private static ConcurrentHashMap _raceByID = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + + private final String name; + private final String description; + private final int raceRuneID; + private Enum.RaceType raceType; + private final short strStart; + private final short strMin; + private final short strMax; + + private final short dexStart; + private final short dexMin; + private final short dexMax; + + private final short conStart; + private final short conMin; + private final short conMax; + + private final short intStart; + private final short intMin; + private final short intMax; + + private final short spiStart; + private final short spiMin; + private final short spiMax; + + private final short healthBonus; + private final short manaBonus; + private final short staminaBonus; + + private final byte startingPoints; + + private final float minHeight; + private final float strHeightMod; + + private int token = 0; + + private HashSet hairStyles; + private HashSet beardStyles; + + private final HashSet skinColors; + private final HashSet beardColors; + private final HashSet hairColors; + + private final ArrayList baseClasses; + private ArrayList effectsList = new ArrayList<>(); + + + private final ArrayList skillsGranted; + private final ArrayList powersGranted; + + public static void loadAllRaces() { + Race._raceByID = DbManager.RaceQueries.LOAD_ALL_RACES(); + } + + + @Override + public boolean equals(Object object) { + + if ((object instanceof Race) == false) + return false; + + Race race = (Race) object; + + return this.raceRuneID == race.raceRuneID; + } + + @Override + public int hashCode() { + return this.raceRuneID; + } + + /** + * ResultSet Constructor + */ + public Race(ResultSet rs) throws SQLException { + + this.raceRuneID = rs.getInt("ID"); + this.raceType = Enum.RaceType.getRaceTypebyRuneID(raceRuneID); + this.name = rs.getString("name"); + this.description = rs.getString("description"); + this.strStart = rs.getShort("strStart"); + this.strMin = rs.getShort("strMin"); + this.strMax = rs.getShort("strMax"); + this.dexStart = rs.getShort("dexStart"); + this.dexMin = rs.getShort("dexMin"); + this.dexMax = rs.getShort("dexMax"); + this.conStart = rs.getShort("conStart"); + this.conMin = rs.getShort("conMin"); + this.conMax = rs.getShort("conMax"); + this.intStart = rs.getShort("intStart"); + this.intMin = rs.getShort("intMin"); + this.intMax = rs.getShort("intMax"); + this.spiStart = rs.getShort("spiStart"); + this.spiMin = rs.getShort("spiMin"); + this.spiMax = rs.getShort("spiMax"); + this.token = rs.getInt("token"); + this.healthBonus = rs.getShort("healthBonus"); + this.manaBonus = rs.getShort("manaBonus"); + this.staminaBonus = rs.getShort("staminaBonus"); + this.startingPoints = rs.getByte("startingPoints"); + this.raceType = RaceType.getRaceTypebyRuneID(this.raceRuneID); + this.minHeight = rs.getFloat("minHeight"); + this.strHeightMod = rs.getFloat("strHeightMod"); + this.hairStyles = DbManager.RaceQueries.HAIR_STYLES_FOR_RACE(raceRuneID); + this.beardStyles = DbManager.RaceQueries.BEARD_STYLES_FOR_RACE(raceRuneID); + this.skinColors = DbManager.RaceQueries.SKIN_COLOR_FOR_RACE(raceRuneID); + this.beardColors = DbManager.RaceQueries.BEARD_COLORS_FOR_RACE(raceRuneID); + this.hairColors = DbManager.RaceQueries.HAIR_COLORS_FOR_RACE(raceRuneID); + this.baseClasses = DbManager.BaseClassQueries.GET_BASECLASS_FOR_RACE(raceRuneID); + this.skillsGranted = DbManager.SkillReqQueries.GET_REQS_FOR_RUNE(raceRuneID); + this.powersGranted = PowerReq.getPowerReqsForRune(raceRuneID); + this.effectsList = DbManager.MobBaseQueries.GET_RUNEBASE_EFFECTS(this.raceRuneID); + + } + + /* + * Getters + */ + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public short getStrStart() { + return strStart; + } + + public short getStrMin() { + return strMin; + } + + public short getStrMax() { + return strMax; + } + + public short getDexStart() { + return dexStart; + } + + public short getDexMin() { + return dexMin; + } + + public short getDexMax() { + return dexMax; + } + + public short getConStart() { + return conStart; + } + + public short getConMin() { + return conMin; + } + + public short getConMax() { + return conMax; + } + + public short getIntStart() { + return intStart; + } + + public short getIntMin() { + return intMin; + } + + public short getIntMax() { + return intMax; + } + + public short getSpiStart() { + return spiStart; + } + + public short getSpiMin() { + return spiMin; + } + + public short getSpiMax() { + return spiMax; + } + + public byte getStartingPoints() { + return startingPoints; + } + + public int getToken() { + return token; + } + + public final HashSet getHairStyles() { + return hairStyles; + } + + public final HashSet getBeardStyles() { + return beardStyles; + } + + public final HashSet getBeardColors() { + return beardColors; + } + + public HashSet getHairColors() { + return hairColors; + } + + public HashSet getSkinColors() { + return skinColors; + } + + public int getNumSkinColors() { + return this.skinColors.size(); + } + + public int getNumHairColors() { + return this.hairColors.size(); + } + + public int getNumBeardColors() { + return this.beardColors.size(); + } + + public final ArrayList getValidBaseClasses() { + return baseClasses; + } + + public ArrayList getAllowedRunes() { + return RuneBase.AllowedRaceRunesMap.get(raceRuneID); + } + + public ArrayList getSkillsGranted() { + return this.skillsGranted; + } + + public ArrayList getPowersGranted() { + return this.powersGranted; + } + + public ArrayList getEffectsGranted() { + return RuneBaseEffect.RuneIDBaseEffectMap.get(this.raceRuneID); + } + + /* + * Validators + */ + public boolean isValidBeardStyle(int id) { + return this.beardStyles.contains(id); + } + + public boolean isValidBeardColor(int id) { + return this.beardColors.contains(id); + } + + public boolean isValidHairStyle(int id) { + return this.hairStyles.contains(id); + } + + public boolean isValidHairColor(int id) { + return this.hairColors.contains(id); + } + + public boolean isValidSkinColor(int id) { + return this.skinColors.contains(id); + } + + public boolean isAllowedRune(RuneBase rb) { + + if (this.getAllowedRunes() != null) + if (this.getAllowedRunes().contains(rb.getObjectUUID())) + return true; + if (RuneBase.AllowedBaseClassRunesMap.containsKey(111111)){ + if (RuneBase.AllowedRaceRunesMap.get(111111).contains(rb.getObjectUUID())) + return true; + } + return false; + } + + public float getHealthBonus() { + return this.healthBonus; + } + + public float getManaBonus() { + return this.manaBonus; + } + + public float getStaminaBonus() { + return this.staminaBonus; + } + + public float getMinHeight() { + return this.minHeight; + } + + public float getStrHeightMod() { + return this.strHeightMod; + } + + public float getHeight(short str) { + return this.minHeight + (this.strHeightMod * str); + } + + public float getCenterHeight(short str) { + return getHeight(str) / 2f; + } + + + /* + * Serializing + */ + + public void serializeForClientMsg(ByteBufferWriter writer) { + writer.putInt(1); // For Race + writer.putInt(0); // Pad + writer.putInt(this.raceRuneID); + + writer.putInt(Enum.GameObjectType.Race.ordinal()); + writer.putInt(raceRuneID); + } + + public static Race getRace(int id) { + return _raceByID.get(id); + } + + public int getRaceRuneID() { + return raceRuneID; + } + + public Enum.RaceType getRaceType() { + return raceType; + } + + + public ArrayList getEffectsList() { + return effectsList; + } +} diff --git a/src/engine/objects/Realm.java b/src/engine/objects/Realm.java new file mode 100644 index 00000000..3936d85a --- /dev/null +++ b/src/engine/objects/Realm.java @@ -0,0 +1,460 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.objects; + +import engine.Enum; +import engine.InterestManagement.RealmMap; +import engine.db.archive.DataWarehouse; +import engine.db.archive.RealmRecord; +import engine.gameManager.DbManager; +import engine.gameManager.PowersManager; +import engine.net.ByteBufferWriter; +import engine.powers.PowersBase; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.net.UnknownHostException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.HashSet; +import java.util.concurrent.ConcurrentHashMap; + +import static engine.Enum.CharterType; + + +public class Realm { + + // Internal class cache + + private static ConcurrentHashMap _realms = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + + private final float mapR; //Red color + private final float mapG; //Green color + private final float mapB; //Blue color + private final float mapA; //Alpha color + private final boolean canBeClaimed; + private final boolean canPlaceCities; + private final int numCities; + private final String realmName; + private int rulingCityUUID; + private int rulingCharacterUUID; + private int rulingCharacterOrdinal; + private String rulingCharacterName; + private int rulingNationUUID; + private GuildTag rulingNationTags; + private String rulingNationName; + private int charterType; + public LocalDateTime ruledSince; + private final float mapY1; + private final float mapX1; + private final float mapY2; + private final float mapX2; + private final int stretchX; + private final int stretchY; + private final int locX; + private final int locY; + private final int realmID; + private final HashSet cities = new HashSet<>(); + private String hash; + + /** + * ResultSet Constructor + */ + public Realm(ResultSet rs) throws SQLException, UnknownHostException { + + this.mapR = rs.getFloat("mapR"); + this.mapG = rs.getFloat("mapG"); + this.mapB = rs.getFloat("mapB"); + this.mapA = rs.getFloat("mapA"); + this.canBeClaimed = rs.getBoolean("canBeClaimed"); + this.canPlaceCities = rs.getBoolean("canPlaceCities"); + this.numCities = rs.getInt("numCities"); + this.realmName = rs.getString("realmName"); + this.rulingCityUUID = rs.getInt("rulingCityUID"); + this.charterType = rs.getInt("charterType"); + + java.sql.Timestamp ruledTimeStamp = rs.getTimestamp("ruledSince"); + + if (ruledTimeStamp != null) + this.ruledSince = LocalDateTime.ofInstant(ruledTimeStamp.toInstant(), ZoneId.systemDefault()); + + this.mapY1 = rs.getFloat("mapY1"); + this.mapX1 = rs.getFloat("mapX1"); + this.mapY2 = rs.getFloat("mapY2"); + this.mapX2 = rs.getFloat("mapX2"); + this.stretchX = rs.getInt("stretchX"); + this.stretchY = rs.getInt("stretchY"); + this.locX = rs.getInt("locX"); + this.locY = rs.getInt("locY"); + this.realmID = rs.getInt("realmID"); + this.hash = rs.getString("hash"); + } + + /* + * Getters + */ + public boolean isRuled() { + return (this.rulingCityUUID != 0); + } + + public float getMapR() { + return this.mapR; + } + + public float getMapG() { + return this.mapG; + } + + public float getMapB() { + return this.mapB; + } + + public float getMapA() { + return this.mapA; + } + + public boolean getCanBeClaimed() { + return this.canBeClaimed; + } + + public boolean getCanPlaceCities() { + return this.canPlaceCities; + } + + public int getNumCities() { + return this.numCities; + } + + public String getRealmName() { + return this.realmName; + } + + public City getRulingCity() { + return City.getCity(this.rulingCityUUID); + } + + public float getMapY1() { + return this.mapY1; + } + + public float getMapX1() { + return this.mapX1; + } + + public float getMapY2() { + return this.mapY2; + } + + public float getMapX2() { + return this.mapX2; + } + + public int getStretchX() { + return this.stretchX; + } + + public int getStretchY() { + return this.stretchY; + } + + public int getlocX() { + return this.locX; + } + + public int getlocY() { + return this.locY; + } + + public int getRealmID() { + return this.realmID; + } + + public void addCity(int cityUUID) { + if (!this.cities.add(cityUUID)) + this.cities.add(cityUUID); + } + + public void removeCity(int cityUUID) { + this.cities.remove(cityUUID); + } + + public boolean isRealmFull() { + + return this.cities.size() >= this.numCities; + } + + public boolean isRealmFullAfterBane() { + + return this.cities.size() > this.numCities; + } + + public static void configureAllRealms() { + + Realm serverRealm; + int realmID; + + for (Enum.RealmType realmType : Enum.RealmType.values()) { + + realmID = realmType.getRealmID(); + // Don't serialize seafloor + + if (realmID == 0) + continue; + + serverRealm = Realm.getRealm(realmID); + serverRealm.configure(); + + } + } + + // Call this after changing ownership before you serialize a realm + + public void configure() { + + PlayerCharacter rulingCharacter; + + // Configure what exactly? We won't send any of it. + + if (this.rulingCityUUID == 0) + return; + if (this.getRulingCity() == null) + return; + if (this.getRulingCity().getTOL() == null) + return; + + rulingCharacter = PlayerCharacter.getPlayerCharacter(this.getRulingCity().getTOL().getOwnerUUID()); + if (rulingCharacter == null){ + Logger.info( this.realmName + " failed to load " + this.getRulingCity().getCityName() + " ID : " + this.rulingCityUUID); + return; + } + + this.rulingCharacterUUID = rulingCharacter.getObjectUUID(); + this.rulingCharacterOrdinal = rulingCharacter.getObjectType().ordinal(); + this.rulingCharacterName = rulingCharacter.getFirstName() + ' ' + rulingCharacter.getLastName(); + this.rulingNationUUID = rulingCharacter.getGuild().getNation().getObjectUUID(); + this.rulingNationName = rulingCharacter.getGuild().getNation().getName(); + this.rulingNationTags = rulingCharacter.getGuild().getNation().getGuildTag(); + } + + public void serializeForClientMsg(ByteBufferWriter writer) { + + writer.putFloat(this.mapR); + writer.putFloat(this.mapG); + writer.putFloat(this.mapB); + writer.putFloat(this.mapA); + writer.put((byte) (this.canBeClaimed ? 0x1 : 0x0)); + writer.put((byte) (this.canPlaceCities ? 0x1 : 0x0)); + writer.putInt(this.numCities); + writer.putFloat(this.mapR); + writer.putFloat(this.mapG); + writer.putFloat(this.mapB); + writer.putFloat(this.mapA); + writer.putString(this.realmName); + + if (isRuled() == true) { + writer.putInt(Enum.GameObjectType.Guild.ordinal()); + writer.putInt(rulingNationUUID); + + writer.putInt(rulingCharacterOrdinal); + writer.putInt(rulingCharacterUUID); + + writer.putInt(Enum.GameObjectType.City.ordinal()); + writer.putInt(rulingCityUUID); + + writer.putLocalDateTime(this.ruledSince); + + writer.putString(rulingNationName); + GuildTag._serializeForDisplay(rulingNationTags,writer); + writer.putString(rulingCharacterName); + writer.putInt(0xB); // Display Title: enum index starts at 10. + } else { + if (this.rulingCityUUID != 0) + Logger.error( "Failed to Load realm info for city" + this.rulingCityUUID); + writer.putLong(0); + writer.putLong(0); + writer.putLong(0); + writer.put((byte) 1); + writer.put((byte) 0); + writer.putInt(0x64); + writer.put((byte) 0); + writer.put((byte) 0); + writer.put((byte) 0); + writer.putInt(0); + writer.putInt(0x10); + writer.putInt(0x10); + writer.putInt(0x10); + writer.putInt(0x0); + writer.putInt(0x0); + writer.putInt(0); + writer.putInt(0); + } + writer.putInt(0); // Male/Female + writer.putInt(this.charterType); // Charter Type + writer.putFloat(this.mapY1); + writer.putFloat(this.mapX1); + writer.putFloat(this.mapY2); + writer.putFloat(this.mapX2); + writer.putInt(this.stretchX); + writer.putInt(this.stretchY); + writer.putInt(this.locX); + writer.putInt(this.locY); + } + + public void updateDatabase() { + DbManager.RealmQueries.REALM_UPDATE(this); + } + + public static Realm getRealm(int realmID) { + return _realms.get(realmID); + } + + /** + * @return the charterType + */ + public int getCharterType() { + return charterType; + } + + /** + * @param charterType the charterType to set + */ + public void setCharterType(int charterType) { + this.charterType = charterType; + } + + public void abandonRealm() { + + // Push event to warehouse + + RealmRecord realmRecord = RealmRecord.borrow(this, Enum.RecordEventType.LOST); + DataWarehouse.pushToWarehouse(realmRecord); + + // No longer own a realm + this.getRulingCity().getGuild().setRealmsOwned(this.getRulingCity().getGuild().getRealmsOwned() - 1); + if (!this.getRulingCity().getGuild().getNation().equals(this.getRulingCity().getGuild())) + this.getRulingCity().getGuild().getNation().setRealmsOwned(this.getRulingCity().getGuild().getNation().getRealmsOwned() - 1); + + // Configure realm + this.charterType = 0; + this.rulingCityUUID = 0; + this.ruledSince = null; + + this.updateDatabase(); + } + + public void claimRealmForCity(City city, int charterType) { + + // Configure realm + this.charterType = charterType; + this.rulingCityUUID = city.getObjectUUID(); + this.ruledSince = LocalDateTime.now(); + this.configure(); + this.updateDatabase(); + + // Push event to warehouse + + RealmRecord realmRecord = RealmRecord.borrow(this, Enum.RecordEventType.CAPTURE); + DataWarehouse.pushToWarehouse(realmRecord); + + } + + public static boolean HasAllBlessings(PlayerCharacter claimer) { + + if (claimer == null) + return false; + + PowersBase powerBlessing = PowersManager.getPowerByIDString("BLS-POWER"); + if (!claimer.effects.containsKey(Integer.toString(powerBlessing.getActions().get(0).getUUID()))) + return false; + PowersBase wisdomBlessing = PowersManager.getPowerByIDString("BLS-POWER"); + if (!claimer.effects.containsKey(Integer.toString(wisdomBlessing.getActions().get(0).getUUID()))) + return false; + PowersBase fortuneBlessing = PowersManager.getPowerByIDString("BLS-FORTUNE"); + return claimer.effects.containsKey(Integer.toString(fortuneBlessing.getActions().get(0).getUUID())); + } + + public static float getRealmHealthMod(City city) { + Realm serverRealm; + int charterType; + float returnBonus = 0.0f; + + serverRealm = RealmMap.getRealmForCity(city); + charterType = serverRealm.charterType; + + switch (charterType) { + + case 762228431: + returnBonus = 0.0f; + break; + case -15978914: + returnBonus = -.15f; + break; + case -600065291: + returnBonus = .15f; + break; + default: + break; + } + + return returnBonus; + } + + public static int getRealmMesh(City city) { + Realm serverRealm; + CharterType charterType; + + serverRealm = city.getRealm(); + charterType = CharterType.getCharterTypeByID(serverRealm.charterType); + + return charterType.getMeshID(); + } + + public static void loadAllRealms() { + + _realms = DbManager.RealmQueries.LOAD_ALL_REALMS(); + + } + + public String getHash() { + return hash; + } + + public void setHash() { + + this.hash = DataWarehouse.hasher.encrypt(this.realmID); + + // Write hash to player character table + + DataWarehouse.writeHash(Enum.DataRecordType.REALM, this.realmID); + } + + /* *** Keeping around in case needed for server wipe or some other emergency + + public static void backfillRealms() { + + // Backfill realm records + + for (Realm realm : _realms.values()) { + + realm.setHash(); + + if ( (realm.isRuled() == true) && + (DataWarehouse.recordExists(Enum.DataRecordType.REALM, realm.getRealmID()) == false)) { + RealmRecord realmRecord = RealmRecord.borrow(realm, Enum.RecordEventType.CAPTURE); + DataWarehouse.pushToWarehouse(realmRecord); + } + + } + } + */ +} diff --git a/src/engine/objects/Regions.java b/src/engine/objects/Regions.java new file mode 100644 index 00000000..0a910d8d --- /dev/null +++ b/src/engine/objects/Regions.java @@ -0,0 +1,384 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.InterestManagement.WorldGrid; +import engine.gameManager.BuildingManager; +import engine.math.Bounds; +import engine.math.FastMath; +import engine.math.Vector3f; +import engine.math.Vector3fImmutable; +import engine.server.MBServerStatics; + +import java.util.ArrayList; +import java.util.HashMap; + + + +public class Regions { + + public int room; + public boolean outside; + public int level; + public final boolean stairs; + public final boolean exit; + public static HashMap FurnitureRegionMap = new HashMap<>(); + public Vector3fImmutable lowLerp; + public Vector3fImmutable highLerp; + public Vector3f center; + public float regionDistanceSquared; + + public ArrayList regionPoints; + + public int parentBuildingID; + + + public Regions(ArrayList regionPoints, int level, int room, boolean outside, boolean exit,boolean stairs, Vector3f center, int parentBuildingID) { + super(); + + this.level = level; + this.room = room; + + this.outside = (outside); + this.regionPoints = regionPoints; + this.exit = exit; + this.stairs = stairs; + this.center = center; + this.parentBuildingID = parentBuildingID; + //order regionpoints clockwise starting from top left, and ending bottom left. + + ArrayList top = new ArrayList<>(); + ArrayList bottom = new ArrayList<>(); + + for (Vector3f point : this.regionPoints){ + if (point.y > center.y) + top.add(point); + else if (point.y < center.y) + bottom.add(point); + } + + + if (top.size() == 2 && bottom.size() == 2){ + Vector3f topLeft = Vector3f.min(top.get(0), top.get(1)); + Vector3f topRight = Vector3f.max(top.get(0), top.get(1)); + + Vector3f topCenter = topLeft.lerp(topRight, .5f); + + Vector3f bottomLeft = Vector3f.min(bottom.get(0), bottom.get(1)); + Vector3f bottomRight = Vector3f.max(bottom.get(0), bottom.get(1)); + + Vector3f bottomCenter = bottomLeft.lerp(bottomRight, .5f); + + this.lowLerp = new Vector3fImmutable(bottomCenter); + this.highLerp = new Vector3fImmutable(topCenter); + + } else if (top.size() == 2 && bottom.size() == 1){ + Vector3f topLeft = Vector3f.min(top.get(0), top.get(1)); + Vector3f topRight = Vector3f.max(top.get(0), top.get(1)); + + Vector3f topCenter = topLeft.lerp(topRight, .5f); + + Vector3f topCopy = topRight.subtract2D(topLeft); + topCopy.normalize(); + + float topMagnitude = topRight.subtract2D(topLeft).length(); + + topCopy.multLocal(topMagnitude); + + Vector3f bottomLeft = null; + Vector3f bottomRight = null; + if (bottom.get(0).distance2D(topLeft) <= bottom.get(0).distance2D(topRight)) + bottomLeft = bottom.get(0); + else if (bottom.get(0).distance2D(topRight) <= bottom.get(0).distance2D(topLeft)) + bottomRight = bottom.get(0); + //find bottom right point + + if (bottomLeft != null){ + bottomRight = bottomLeft.add(topCopy); + }else if (bottomRight != null){ + bottomLeft = bottomRight.subtract(topCopy); + } + + + + Vector3f bottomCenter = bottomLeft.lerp(bottomRight, .5f); + + this.lowLerp = new Vector3fImmutable(bottomCenter); + this.highLerp = new Vector3fImmutable(topCenter); + + }else if (bottom.size() == 2 && top.size() == 1){ + Vector3f topLeft = Vector3f.min(bottom.get(0), bottom.get(1)); + Vector3f topRight = Vector3f.max(bottom.get(0), bottom.get(1)); + + Vector3f topCenter = topLeft.lerp(topRight, .5f); + + Vector3f topCopy = topRight.subtract2D(topLeft); + topCopy.normalize(); + + float topMagnitude = topRight.subtract2D(topLeft).length(); + + topCopy.multLocal(topMagnitude); + + Vector3f bottomLeft = null; + Vector3f bottomRight = null; + if (top.get(0).distance2D(topLeft) < top.get(0).distance2D(topRight)) + bottomLeft = bottom.get(0); + else if (top.get(0).distance2D(topRight) < top.get(0).distance2D(topLeft)) + bottomRight = bottom.get(0); + //find bottom right point + + if (bottomLeft != null){ + bottomRight = bottomLeft.add(topCopy); + }else if (bottomRight != null){ + bottomLeft = bottomRight.subtract(topCopy); + } + + + + Vector3f bottomCenter = bottomLeft.lerp(bottomRight, .5f); + + this.lowLerp = new Vector3fImmutable(bottomCenter); + this.highLerp = new Vector3fImmutable(topCenter); + } + + if (this.lowLerp == null) + this.lowLerp = new Vector3fImmutable(this.regionPoints.get(0)); + + if (this.highLerp == null){ + this.highLerp = new Vector3fImmutable(this.regionPoints.get(2)); + } + + this.regionDistanceSquared = this.lowLerp.distanceSquared2D(this.highLerp); + } + + public int getRoom() { + return room; + } + + + + + public boolean isOutside() { + return outside; + } + + + public int getLevel() { + return level; + } + + public boolean collides(Vector3fImmutable collisionPoint){ + + //test if inside triangle // Regions either have 3 or 4 points + if (this.regionPoints.size() == 3){ + float regionArea = FastMath.area(regionPoints.get(0).x, regionPoints.get(0).z, regionPoints.get(1).x, regionPoints.get(1).z, regionPoints.get(2).x, regionPoints.get(2).z); + float collisionArea1 = FastMath.area(collisionPoint.x, collisionPoint.z, regionPoints.get(0).x, regionPoints.get(0).z,regionPoints.get(1).x, regionPoints.get(1).z); + float collisionArea2 = FastMath.area(collisionPoint.x, collisionPoint.z, regionPoints.get(1).x, regionPoints.get(1).z,regionPoints.get(2).x, regionPoints.get(2).z); + float collisionArea3 = FastMath.area(collisionPoint.x, collisionPoint.z, regionPoints.get(0).x, regionPoints.get(0).z,regionPoints.get(2).x, regionPoints.get(2).z); + + if ((collisionArea1 + collisionArea2 + collisionArea3) == regionArea) + return true; + + }else{ + + int i; + int j; + for (i = 0, j = this.regionPoints.size() - 1; i < this.regionPoints.size(); j = i++) { + if ((regionPoints.get(i).z > collisionPoint.z) != (regionPoints.get(j).z > collisionPoint.z) && + (collisionPoint.x < (regionPoints.get(j).x - regionPoints.get(i).x) * (collisionPoint.z - regionPoints.get(i).z) / (regionPoints.get(j).z-regionPoints.get(i).z) + regionPoints.get(i).x)) { + return true; + } + } + + } + + return false; + } + + public boolean isPointInPolygon( Vector3fImmutable point) + { + boolean inside = false; + for (int i = 0, j = regionPoints.size()-1; i < regionPoints.size(); j = i++) + { + if (((regionPoints.get(i).z > point.z) != (regionPoints.get(j).z > point.z)) && + (point.x < (regionPoints.get(j).x - regionPoints.get(i).x) * (point.z - regionPoints.get(i).z) / (regionPoints.get(j).z - regionPoints.get(i).z) + regionPoints.get(i).x)) + inside = !inside; + } + return inside; + } + + public static boolean CanEnterRegion(AbstractWorldObject worldObject, Regions toEnter){ + + if (worldObject.getRegion() == null) + if (toEnter.level == 0 || toEnter.room == -1 || toEnter.exit) + return true; + else + return false; + + if (worldObject.getRegion().equals(toEnter)) + return true; + + if (worldObject.getRegion().level == toEnter.level) + return true; + + //next region is stairs, if they are on the same level as stairs or 1 up, world object can enter. + if (toEnter.stairs) + if (worldObject.getRegion().level == toEnter.level || toEnter.level - 1 == worldObject.getRegion().level) + return true; + if (worldObject.getRegion().stairs){ + + boolean movingUp = false; + + boolean movingDown = false; + float yLerp = worldObject.getRegion().lerpY(worldObject); + + if (yLerp == (worldObject.getRegion().highLerp.y)) + movingUp = true; + else if (yLerp == (worldObject.getRegion().lowLerp.y)) + movingDown = true; + //Stairs are always considered on the bottom floor. + + + if (movingUp){ + if(toEnter.level == worldObject.getRegion().level + 1) + return true; + }else if (movingDown) + if (toEnter.level == worldObject.getRegion().level) + return true; + + } + + return false; + } + + public float lerpY (AbstractWorldObject lerper){ + + Vector3fImmutable lengthVector = this.highLerp.subtract2D(this.lowLerp); + Vector3fImmutable characterVector = lerper.getLoc().subtract2D(this.lowLerp); + float lengthVectorMagnitude = lengthVector.magnitude(); + float characterVectorMagnitude = characterVector.magnitude(); + float percentDistance = characterVectorMagnitude/lengthVectorMagnitude; + float interpolatedY = this.lowLerp.interpolate(this.highLerp, percentDistance).y; + + if (interpolatedY > this.highLerp.y) + interpolatedY = this.highLerp.y; + else if (interpolatedY < this.lowLerp.y) + interpolatedY = this.lowLerp.y; + return interpolatedY; + } + + + public boolean isStairs() { + return stairs; + } + + + public boolean isExit() { + return exit; + } + +public static float GetMagnitudeOfRegionSlope(Regions region){ + Vector3fImmutable lengthVector = region.highLerp.subtract2D(region.lowLerp); + return lengthVector.magnitude(); + } + +public static float GetMagnitudeOfPlayerOnRegionSlope(Regions region, PlayerCharacter player){ + Vector3fImmutable characterVector = player.getLoc().subtract2D(region.lowLerp); + return characterVector.magnitude(); + } + +public static float SlopeLerpPercent(PlayerCharacter player, Regions region){ + + float lengthVectorMagnitude = Regions.GetMagnitudeOfRegionSlope(region); + float characterVectorMagnitude = Regions.GetMagnitudeOfPlayerOnRegionSlope(region, player); + float percentDistance = characterVectorMagnitude/lengthVectorMagnitude * 2; + return percentDistance; +} + +public static boolean CanEnterFromOutsideBuilding(Building building,Regions region){ + if (!region.outside) + return false; + if (region.lowLerp.y - building.getLoc().y > 1 ) + return false; + + return true; +} + +public static boolean CanEnterNextLevel(Regions fromRegion,Regions toRegion, AbstractWorldObject worldObject){ + + if (fromRegion == null) + return false; + + if (toRegion == null) + return false; + + // regions are the same, no need to go any further. + if (fromRegion.equals(toRegion)) + return true; + + //cant move up a level without stairs. + if (!fromRegion.stairs) + return false; + + boolean movingUp = false; + + Vector3fImmutable closestPoint = Vector3fImmutable.ClosestPointOnLine(fromRegion.lowLerp, fromRegion.highLerp, worldObject.getLoc()); + + //Closest point of a region higher than current region will always return highlerp. + if (closestPoint.equals(fromRegion.highLerp)) + movingUp = true; + //Stairs are always considered on the bottom floor. + + if (movingUp){ + if(toRegion.level != fromRegion.level + 1) + return false; + }else if (toRegion.level != fromRegion.level) + return false; + return true; +} + +public static boolean IsGroundLevel(Regions region, Building building){ + + if (region.lowLerp.y - building.getLoc().y > 1) + return false; + return true; +} + +public static Building GetBuildingForRegion(Regions region){ + return BuildingManager.getBuildingFromCache(region.parentBuildingID); +} +public static Regions GetRegionForTeleport(Vector3fImmutable location){ + Regions region = null; + + + //Find building + for (AbstractWorldObject awo:WorldGrid.getObjectsInRangePartial(location, MBServerStatics.STRUCTURE_LOAD_RANGE, MBServerStatics.MASK_BUILDING)){ + Building building = (Building)awo; + if (!Bounds.collide(location, building.getBounds())) + continue; + + //find regions that intersect x and z, check if object can enter. + for (Regions toEnter: building.getBounds().getRegions()){ + if (toEnter.isPointInPolygon(location)){ + + if (region == null) + region = toEnter; + else // we're using a low level to high level tree structure, database not always in order low to high. + //check for highest level index. + if(region != null && toEnter.highLerp.y > region.highLerp.y) + region = toEnter; + + + } + } + } + return region; +} +} diff --git a/src/engine/objects/Resists.java b/src/engine/objects/Resists.java new file mode 100644 index 00000000..4342ae3b --- /dev/null +++ b/src/engine/objects/Resists.java @@ -0,0 +1,562 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum; +import engine.Enum.DamageType; +import engine.Enum.ModType; +import engine.Enum.SourceType; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.powers.EffectsBase; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashSet; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; + +public class Resists { + + private ConcurrentHashMap resists = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private ConcurrentHashMap immuneTo = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private DamageType protection; + private int protectionTrains=0; + private boolean immuneToAll; + private static ConcurrentHashMap mobResists = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + + /** + * Generic Constructor + */ + + public Resists(String type) { + switch (type) { + case "Building": + setBuildingResists(); + break; + case "Mine": + setMineResists(); + break; + default: + setGenericResists(); + break; + } + } + + public Resists(Resists r) { + for (DamageType dt : r.resists.keySet()) + this.resists.put(dt, r.resists.get(dt)); + for (DamageType dt : r.immuneTo.keySet()) + this.immuneTo.put(dt, r.immuneTo.get(dt)); + this.protection = r.protection; + this.protectionTrains = r.protectionTrains; + this.immuneToAll = r.immuneToAll; + } + + /** + * Generic Constructor for player + */ + public Resists(PlayerCharacter pc) { + setGenericResists(); + } + + public Resists(Mob mob) { + setGenericResists(); + } + + /** + * Called for mobBase when getting from the db fails + */ + public Resists(MobBase mobBase) { + setGenericResists(); + } + /** + * Database Constructor + */ + public Resists(ResultSet rs) throws SQLException { + this.immuneToAll = false; + this.resists.put(DamageType.Slash, rs.getFloat("slash")); + this.resists.put(DamageType.Crush, rs.getFloat("crush")); + this.resists.put(DamageType.Pierce, rs.getFloat("pierce")); + this.resists.put(DamageType.Magic, rs.getFloat("magic")); + this.resists.put(DamageType.Bleed, rs.getFloat("bleed")); + this.resists.put(DamageType.Poison, rs.getFloat("poison")); + this.resists.put(DamageType.Mental, rs.getFloat("mental")); + this.resists.put(DamageType.Holy, rs.getFloat("holy")); + this.resists.put(DamageType.Unholy, rs.getFloat("unholy")); + this.resists.put(DamageType.Lightning, rs.getFloat("lightning")); + this.resists.put(DamageType.Fire, rs.getFloat("fire")); + this.resists.put(DamageType.Cold, rs.getFloat("cold")); + this.resists.put(DamageType.Healing, 0f); + } + + /** + * Create generic resists for buildings + */ + public final void setBuildingResists() { + this.immuneToAll = false; + this.resists.put(DamageType.Slash, 85f); + this.resists.put(DamageType.Crush, 85f); + this.resists.put(DamageType.Siege, 0f); + this.immuneTo.put(DamageType.Pierce, true); + this.immuneTo.put(DamageType.Magic, true); + this.immuneTo.put(DamageType.Bleed, true); + this.immuneTo.put(DamageType.Poison, true); + this.immuneTo.put(DamageType.Mental, true); + this.immuneTo.put(DamageType.Holy, true); + this.immuneTo.put(DamageType.Unholy, true); + this.immuneTo.put(DamageType.Lightning, true); + this.immuneTo.put(DamageType.Fire, true); + this.immuneTo.put(DamageType.Cold, true); + + } + + /** + * Create generic resists for mines + */ + public final void setMineResists() { + this.immuneToAll = false; + this.immuneTo.put(DamageType.Slash, true); + this.immuneTo.put(DamageType.Crush, true); + this.immuneTo.put(DamageType.Pierce, true); + this.immuneTo.put(DamageType.Magic, true); + this.immuneTo.put(DamageType.Bleed, true); + this.immuneTo.put(DamageType.Poison, true); + this.immuneTo.put(DamageType.Mental, true); + this.immuneTo.put(DamageType.Holy, true); + this.immuneTo.put(DamageType.Unholy, true); + this.immuneTo.put(DamageType.Lightning, true); + this.immuneTo.put(DamageType.Fire, true); + this.immuneTo.put(DamageType.Cold, true); + this.resists.put(DamageType.Siege, 0f); + } + + /** + * Create generic resists + */ + public final void setGenericResists() { + this.immuneToAll = false; + this.resists.put(DamageType.Slash, 0f); + this.resists.put(DamageType.Crush, 0f); + this.resists.put(DamageType.Pierce, 0f); + this.resists.put(DamageType.Magic, 0f); + this.resists.put(DamageType.Bleed, 0f); + this.resists.put(DamageType.Poison, 0f); + this.resists.put(DamageType.Mental, 0f); + this.resists.put(DamageType.Holy, 0f); + this.resists.put(DamageType.Unholy, 0f); + this.resists.put(DamageType.Lightning, 0f); + this.resists.put(DamageType.Fire, 0f); + this.resists.put(DamageType.Cold, 0f); + this.resists.put(DamageType.Healing, 0f); + this.immuneTo.put(DamageType.Siege, true); + + } + + /** + * Get a resist + */ + public float getResist(DamageType type, int trains) { + //get resisted amount + Float amount = 0f; + if (this.resists.containsKey(type)) + amount = this.resists.get(type); + + //add protection + if (trains > 0 && protection != null && type.equals(this.protection)) { + float prot = 50 + this.protectionTrains - trains; + amount += (prot >= 0) ? prot : 0; + } + + if (amount == null) + return 0f; + if (amount > 75f) + return 75f; + return amount; + } + + /** + * get immuneTo + */ + public boolean immuneTo(DamageType type) { + if (this.immuneTo.containsKey(type)) + return this.immuneTo.get(type); + else + return false; + } + + /** + * get immuneToAll + */ + public boolean immuneToAll() { + return this.immuneToAll; + } + + public boolean immuneToPowers() { + return immuneTo(DamageType.Powers); + } + + public boolean immuneToAttacks() { + return immuneTo(DamageType.Attack); + } + + public boolean immuneToSpires() { + return immuneTo(DamageType.Spires); + } + + /** + * gets immuneTo(type) and immuneToAll + */ + public boolean isImmune(DamageType type) { + if (this.immuneToAll) + return true; + return this.immuneTo(type); + } + + /** + * Set a resist + */ + public void setResist(DamageType type, float value) { + this.resists.put(type, value); + } + + /** + * add to a resist + */ + public void incResist(DamageType type, float value) { + Float amount = this.resists.get(type); + if (amount == null) + this.resists.put(type, value); + else + this.resists.put(type, amount + value); + } + + /** + * subtract from a resist + */ + public void decResist(DamageType type, float value) { + Float amount = this.resists.get(type); + if (amount == null) + this.resists.put(type, (0 - value)); + else + this.resists.put(type, amount - value); + } + + /** + * set immunities from mobbase + */ + public void setImmuneTo(int immune) { + setImmuneTo(DamageType.Stun, ((immune & 1) != 0)); + setImmuneTo(DamageType.PowerBlock, ((immune & 2) != 0)); + setImmuneTo(DamageType.Drain, ((immune & 4) != 0)); + setImmuneTo(DamageType.Snare, ((immune & 8) != 0)); + setImmuneTo(DamageType.Siege, ((immune & 16) != 0)); + setImmuneTo(DamageType.Slash, ((immune & 32) != 0)); + setImmuneTo(DamageType.Crush, ((immune & 64) != 0)); + setImmuneTo(DamageType.Pierce, ((immune & 128) != 0)); + setImmuneTo(DamageType.Magic, ((immune & 256) != 0)); + setImmuneTo(DamageType.Bleed, ((immune & 512) != 0)); + setImmuneTo(DamageType.Poison, ((immune & 1024) != 0)); + setImmuneTo(DamageType.Mental, ((immune & 2048) != 0)); + setImmuneTo(DamageType.Holy, ((immune & 4096) != 0)); + setImmuneTo(DamageType.Unholy, ((immune & 8192) != 0)); + setImmuneTo(DamageType.Lightning, ((immune & 16384) != 0)); + setImmuneTo(DamageType.Fire, ((immune & 32768) != 0)); + setImmuneTo(DamageType.Cold, ((immune & 65536) != 0)); + setImmuneTo(DamageType.Steel, ((immune & 131072) != 0)); + } + + /** + * set/unset immuneTo + */ + public void setImmuneTo(DamageType type, boolean value) { + this.immuneTo.put(type, value); + } + + /** + * set immuneToAll + */ + public void setImmuneToAll(boolean value) { + this.immuneToAll = value; + } + + + /** + * set resists from mobbase + */ + public void setMobResists(int resistID) { + //TODO add this in later + //calls `static_npc_mob_resists` table WHERE `ID`='resistID' + } + + /** + * get Damage after resist + * Expects heals as negative damage and damage as positive damage for fortitudes. + */ + public float getResistedDamage(AbstractCharacter source, AbstractCharacter target, DamageType type, float damage, int trains) { + //handle fortitudes + damage = handleFortitude(target, type, damage); + + //check to see if any damage absorbers should cancel + float damageAfterResists = damage * (1 - (this.getResist(type, trains) / 100)); + if (target != null) { + //debug damage shields if any found + if (source.getDebug(2) && source.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) { + Effect da = target.getDamageAbsorber(); + if (da != null && da.getEffectsBase() != null) { + EffectsBase eb = da.getEffectsBase(); + String text = "Damage: " + damage + '\n'; + text += "Damage after resists: " + damageAfterResists + '\n'; + text += "Attack damage type: " + type.name() + '\n'; + text += "Fortitude damage types; " + eb.getDamageTypes() + '\n'; + text += "Fortitude damage before attack: " + da.getDamageAmount() + '\n'; + text += "Fortitude total health: " + eb.getDamageAmount(da.getTrains()) + '\n'; + text += "Fortitude trains: " + da.getTrains(); + ChatManager.chatSystemInfo((PlayerCharacter) source, text); + } + } + target.cancelOnTakeDamage(type, (damageAfterResists)); + } + return damageAfterResists; + } + + //Handle Fortitudes + private static float handleFortitude(AbstractCharacter target, DamageType type, float damage) { + if (target == null || !(target.getObjectType().equals(Enum.GameObjectType.PlayerCharacter))) + return damage; + PlayerBonuses bonus = target.getBonuses(); + + //see if there is a fortitude + float damageCap = bonus.getFloatPercentAll(ModType.DamageCap, SourceType.None); + if (damageCap == 0f || type == DamageType.Healing) + return damage; + + //is fortitude, Are we under the cap? + float maxHealth = target.getHealthMax(); + float capFire = maxHealth * (damageCap); + if (damage < capFire) + return damage; + + //let's see if valid damagetype to apply it + boolean exclusive; + HashSet forts = bonus.getList(ModType.IgnoreDamageCap); + if (forts == null) { + exclusive = true; + forts = bonus.getList(ModType.ExclusiveDamageCap); + } else + exclusive = false; + if (forts == null || !isValidDamageCapType(forts, type, exclusive)) + return damage; + + float adjustedDamage = bonus.getFloatPercentAll(ModType.AdjustAboveDmgCap, SourceType.None); + //Adjust damage down and return new amount + float aadc = 1 +adjustedDamage; + return capFire * aadc; + } + + //Test if Damagetype is valid for foritude + private static boolean isValidDamageCapType(HashSet forts, DamageType damageType, boolean exclusive) { + for (SourceType fort: forts) { + DamageType dt = DamageType.valueOf(fort.name()); + + if (dt == DamageType.None) + continue; + + if (dt == damageType) { + return exclusive; + } + } + return !exclusive; + } + + + /** + * Calculate Current Resists for Player + */ + public static void calculateResists(AbstractCharacter ac) { + if (ac.getResists() != null) + ac.getResists().calculateResists(ac, true); + else + Logger.error("Unable to find resists for character " + ac.getObjectUUID()); + } + + public void calculateResists(AbstractCharacter ac, boolean val) { + this.immuneTo.clear(); + + // get resists for runes + PlayerBonuses rb = ac.getBonuses(); + float slash = 0f, crush = 0f, pierce = 0f, magic = 0f, bleed = 0f, mental = 0f, holy = 0f, unholy = 0f, poison = 0f, lightning = 0f, fire = 0f, cold = 0f, healing = 0f; + + if (rb != null) { + // Handle immunities + if (rb.getBool(ModType.ImmuneTo, SourceType.Stun)) + this.immuneTo.put(DamageType.Stun, true); + if (rb.getBool(ModType.ImmuneTo, SourceType.Blind)) + this.immuneTo.put(DamageType.Blind, true); + if (rb.getBool(ModType.ImmuneToAttack, SourceType.None)) + this.immuneTo.put(DamageType.Attack, true); + if (rb.getBool(ModType.ImmuneToPowers, SourceType.None)) + this.immuneTo.put(DamageType.Powers, true); + if (rb.getBool(ModType.ImmuneTo, SourceType.Powerblock)) + this.immuneTo.put(DamageType.Powerblock, true); + if (rb.getBool(ModType.ImmuneTo, SourceType.DeBuff)) + this.immuneTo.put(DamageType.DeBuff, true); + if (rb.getBool(ModType.ImmuneTo, SourceType.Fear)) + this.immuneTo.put(DamageType.Fear, true); + if (rb.getBool(ModType.ImmuneTo, SourceType.Charm)) + this.immuneTo.put(DamageType.Charm, true); + if (rb.getBool(ModType.ImmuneTo, SourceType.Root)) + this.immuneTo.put(DamageType.Root, true); + if (rb.getBool(ModType.ImmuneTo, SourceType.Snare)) + this.immuneTo.put(DamageType.Snare, true); + + // Handle resists + slash += rb.getFloat(ModType.Resistance, SourceType.Slash); + crush += rb.getFloat(ModType.Resistance, SourceType.Crush); + pierce += rb.getFloat(ModType.Resistance, SourceType.Pierce); + magic += rb.getFloat(ModType.Resistance, SourceType.Magic); + bleed += rb.getFloat(ModType.Resistance, SourceType.Bleed); + poison += rb.getFloat(ModType.Resistance, SourceType.Poison); + mental += rb.getFloat(ModType.Resistance, SourceType.Mental); + holy += rb.getFloat(ModType.Resistance, SourceType.Holy); + unholy += rb.getFloat(ModType.Resistance, SourceType.Unholy); + lightning += rb.getFloat(ModType.Resistance, SourceType.Lightning); + fire += rb.getFloat(ModType.Resistance, SourceType.Fire); + cold += rb.getFloat(ModType.Resistance, SourceType.Cold); + healing += rb.getFloat(ModType.Resistance, SourceType.Healing); // DamageType.Healing.name()); + + //HHO + +// String protectionString = rb.getString("protection"); +// +// if (protectionString.isEmpty()) +// this.protection = null; +// else try { +// this.protection = DamageType.valueOf(rb.getString("protection")); +// } catch (IllegalArgumentException e) { +// Logger.error( "No enum for: " + protectionString); +// this.protection = null; +// } +// this.protectionTrains = rb.getFloat("protection"); + } + + // get resists from equipment + if (ac.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) { + if (ac.getCharItemManager() != null && ac.getCharItemManager().getEquipped() != null) { + float[] phys = { 0f, 0f, 0f }; + ConcurrentHashMap equip = ac.getCharItemManager().getEquipped(); + + // get base physical resists + phys = Resists.getArmorResists(equip.get(MBServerStatics.SLOT_HELMET), phys); + phys = Resists.getArmorResists(equip.get(MBServerStatics.SLOT_CHEST), phys); + phys = Resists.getArmorResists(equip.get(MBServerStatics.SLOT_ARMS), phys); + phys = Resists.getArmorResists(equip.get(MBServerStatics.SLOT_GLOVES), phys); + phys = Resists.getArmorResists(equip.get(MBServerStatics.SLOT_LEGGINGS), phys); + phys = Resists.getArmorResists(equip.get(MBServerStatics.SLOT_FEET), phys); + slash += phys[0]; + crush += phys[1]; + pierce += phys[2]; + + } + } + + this.resists.put(DamageType.Slash, slash); + this.resists.put(DamageType.Crush, crush); + this.resists.put(DamageType.Pierce, pierce); + this.resists.put(DamageType.Magic, magic); + this.resists.put(DamageType.Bleed, bleed); + this.resists.put(DamageType.Poison, poison); + this.resists.put(DamageType.Mental, mental); + this.resists.put(DamageType.Holy, holy); + this.resists.put(DamageType.Unholy, unholy); + this.resists.put(DamageType.Lightning, lightning); + this.resists.put(DamageType.Fire, fire); + this.resists.put(DamageType.Cold, cold); + this.resists.put(DamageType.Healing, healing); + + this.immuneTo.put(DamageType.Siege, true); + + // debug printing of resists + // printResists(pc); + } + + private static float[] getArmorResists(Item armor, float[] phys) { + if (armor == null) + return phys; + ItemBase ab = armor.getItemBase(); + if (ab == null) + return phys; + phys[0] += ab.getSlashResist(); + phys[1] += ab.getCrushResist(); + phys[2] += ab.getPierceResist(); + return phys; + } + + public void printResistsToClient(PlayerCharacter pc) { + for (DamageType dt : resists.keySet()) + ChatManager.chatSystemInfo(pc, " resist." + dt.name() + ": " + resists.get(dt)); + for (DamageType dt : immuneTo.keySet()) + ChatManager.chatSystemInfo(pc, " immuneTo." + dt.name() + ": " + immuneTo.get(dt)); + ChatManager.chatSystemInfo(pc, " immuneToAll: " + this.immuneToAll); + if (protection != null) + ChatManager.chatSystemInfo(pc, " Protection: " + protection.name() + ", Trains: " + protectionTrains); + else + ChatManager.chatSystemInfo(pc, " Protection: None"); + } + + public String getResists(PlayerCharacter pc) { + String out = pc.getName(); + + out += "Resists: "; + Iterator it = this.resists.keySet().iterator(); + while (it.hasNext()) { + DamageType damType = it.next(); + String dtName = damType.name(); + out += dtName + '=' + this.resists.get(dtName) + ", "; + } + + out += "ImmuneTo: "; + it = this.immuneTo.keySet().iterator(); + while (it.hasNext()) { + DamageType damType = it.next(); + + String dtName = damType.name(); + out += dtName + '=' + this.resists.get(dtName) + ", "; + } + + if (protection != null) + out += "Protection: " + protection.name() + ", Trains: " + protectionTrains; + else + out += "Protection: none"; + + return out; + } + + /** + * Get mob resists from db if there, otherwise set defaults + */ + public static Resists getResists(int resistID) { + //check cache first + if (mobResists.containsKey(resistID)) + return new Resists(mobResists.get(resistID)); + + //get from database + Resists resists = DbManager.ResistQueries.GET_RESISTS_FOR_MOB(resistID); + if (resists != null) { + mobResists.put(resistID, resists); + return new Resists(resists); + } + + //failed, may want to debug this + return null; + } +} diff --git a/src/engine/objects/Resource.java b/src/engine/objects/Resource.java new file mode 100644 index 00000000..e8b72cd4 --- /dev/null +++ b/src/engine/objects/Resource.java @@ -0,0 +1,69 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.server.MBServerStatics; + +import java.util.concurrent.ConcurrentHashMap; + +public enum Resource { + + ADAMANT("DefaultAdamant", 1557001525, 10, 1580003), + AGATE("DefaultAgate", -1096157543, 20, 1580009), + ANTIMONY("DefaultAntimony", 1256147265, 10, 1580014), + AZOTH("DefaultAzoth", -1205326951, 20, 1580012), + BLOODSTONE("DefaultBloodstone", -1912381716, 5, 1580020), + BRONZEWOOD("DefaultBronzewood", -519681813, 30, 1580006), + COAL("DefaultCoal", -1672872311, 30, 1580008), + DIAMOND("DefaultDiamond", 1540225085, 20, 1580010), + GALVOR("DefaultGalvor", -1683992404, 5, 1580017), + IRON("DefaultIron", -1673518119, 20, 1580002), + LUMBER("DefaultLumber", -1628412684, 100, 1580004), + MANDRAKE("DefaultMandrake", -1519910613, 10, 1580007), + MITHRIL("DefaultMithril", 626743397, 5, 1580021), + OAK("DefaultOak", -1653034775, 30, 1580005), + OBSIDIAN("DefaultObsidian", 778019055, 5, 1580019), + ONYX("DefaultOnyx", -1675952151, 10, 1580011), + ORICHALK("DefaultOrichalk", -1468730955, 30, 1580013), + QUICKSILVER("DefaultQuicksilver", -2081208434, 10, 1580016), + STONE("DefaultStone", -1094703863, 100, 1580000), + SULFUR("DefaultSulfur", -1763687412, 10, 1580015), + TRUESTEEL("DefaultTruesteel", -169012482, 20, 1580001), + WORMWOOD("DefaultWormwood", 1204785075, 5, 1580018), + GOLD("DefaultGold", -1670881623, 50000, 7); + + public final String name; + public final int hash; + public final int baseProduction; + public final int UUID; + public static ConcurrentHashMap resourceByHash; + + Resource(String name, int hash, int baseProduction, int uuid) { + this.name = name; + this.hash = hash; + this.baseProduction = baseProduction; + this.UUID = uuid; + } + + public static Resource GetResourceByHash(int hash){ + for (Resource resource: Resource.values()){ + if (hash == resource.hash) + return resource; + } + return Resource.MITHRIL; + } + + //load lookups via hashes + static { + resourceByHash = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + for (Resource r : Resource.values()) + resourceByHash.put(r.hash, r); + } +} diff --git a/src/engine/objects/RuneBase.java b/src/engine/objects/RuneBase.java new file mode 100644 index 00000000..9738b4bc --- /dev/null +++ b/src/engine/objects/RuneBase.java @@ -0,0 +1,217 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum; +import engine.gameManager.DbManager; +import engine.net.ByteBufferWriter; +import engine.server.MBServerStatics; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; + + +public class RuneBase extends AbstractGameObject { + + private final String name; + private final String description; + private final int type; + private final byte subtype; + + private final ConcurrentHashMap race = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private final ConcurrentHashMap baseClass = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private final ConcurrentHashMap promotionClass = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private final ConcurrentHashMap discipline = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private final ArrayList overwrite = new ArrayList<>(); + private int levelRequired = 1; + + private ArrayList effectsList = new ArrayList<>(); + + public static HashMap> AllowedBaseClassRunesMap = new HashMap<>(); + public static HashMap> AllowedRaceRunesMap = new HashMap<>(); + /** + * No Table ID Constructor + */ + public RuneBase(String name, String description, int type, byte subtype, ArrayList attrs) { + super(); + + this.name = name; + this.description = description; + this.type = type; + this.subtype = subtype; + + } + + /** + * Normal Constructor + */ + public RuneBase(String name, String description, int type, byte subtype, ArrayList attrs, int newUUID) { + super(newUUID); + + this.name = name; + this.description = description; + this.type = type; + this.subtype = subtype; + + } + + /** + * ResultSet Constructor + */ + public RuneBase(ResultSet rs) throws SQLException { + super(rs); + + this.name = rs.getString("name"); + this.description = rs.getString("description"); + this.type = rs.getInt("type"); + this.subtype = rs.getByte("subtype"); + + DbManager.RuneBaseQueries.GET_RUNE_REQS(this); + this.effectsList = DbManager.MobBaseQueries.GET_RUNEBASE_EFFECTS(this.getObjectUUID()); + } + + @Override + public boolean equals(Object obj) { + + if (!super.equals(obj)) { + return false; + } + + if(obj instanceof RuneBase) { + RuneBase rbObj = (RuneBase) obj; + if (!this.name.equals(rbObj.name)) { + return false; + } + + if (!this.description.equals(rbObj.description)) { + return false; + } + + if (this.type != rbObj.type) { + return false; + } + + if (this.subtype != rbObj.subtype) { + return false; + } + + + return true; + } + + return false; + } + + /* + * Getters + */ + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public int getType() { + return type; + } + + /** + * @return the subtype + */ + public byte getSubtype() { + return subtype; + } + + /** + * @return the attrs + */ + public ArrayList getAttrs() { + return RuneBaseAttribute.runeBaseAttributeMap.get(this.getObjectUUID()); + } + + public ConcurrentHashMap getRace() { + return this.race; + } + + public ConcurrentHashMap getBaseClass() { + return this.baseClass; + } + + public ConcurrentHashMap getPromotionClass() { + return this.promotionClass; + } + + public ConcurrentHashMap getDiscipline() { + return this.discipline; + } + + public ArrayList getOverwrite() { + return this.overwrite; + } + + public int getLevelRequired() { + return this.levelRequired; + } + + public void setLevelRequired(int levelRequired) { + this.levelRequired = levelRequired; + } + + public static RuneBase getRuneBase(int tableId) { + + if (tableId == 0) + return null; + + RuneBase rb = (RuneBase) DbManager.getFromCache(Enum.GameObjectType.RuneBase, tableId); + + if (rb != null) + return rb; + + return DbManager.RuneBaseQueries.GET_RUNEBASE(tableId); + } + + /* + * Serializing + */ + + public static void serializeForClientMsg(RuneBase runeBase,ByteBufferWriter writer) { + writer.putInt(runeBase.type); + writer.putInt(0); // Pad + writer.putInt(runeBase.getObjectUUID()); + writer.putInt(runeBase.getObjectType().ordinal()); + writer.putInt(runeBase.getObjectUUID()); + + } + + @Override + public void updateDatabase() { + // TODO Auto-generated method stub + } + + /** + * @return the effectsList + */ + public ArrayList getEffectsList() { + return effectsList; + } + + public static void LoadAllRuneBases(){ + + DbManager.RuneBaseQueries.LOAD_ALL_RUNEBASES(); + RuneBase.AllowedBaseClassRunesMap = DbManager.RuneBaseQueries.LOAD_ALLOWED_STARTING_RUNES_FOR_BASECLASS(); + RuneBase.AllowedRaceRunesMap = DbManager.RuneBaseQueries.LOAD_ALLOWED_STARTING_RUNES_FOR_RACE(); + } + +} diff --git a/src/engine/objects/RuneBaseAttribute.java b/src/engine/objects/RuneBaseAttribute.java new file mode 100644 index 00000000..5ea74162 --- /dev/null +++ b/src/engine/objects/RuneBaseAttribute.java @@ -0,0 +1,107 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum.GameObjectType; +import engine.gameManager.DbManager; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; + + +public class RuneBaseAttribute extends AbstractGameObject { + + private short attributeID; + private short modValue; + + private int runeBaseID; + + public static HashMap> runeBaseAttributeMap = new HashMap<>(); + + + /** + * No Table ID Constructor + */ + public RuneBaseAttribute(short attributeID, short modValue) { + super(); + + this.attributeID = attributeID; + this.modValue = modValue; + } + + /** + * Normal + */ + public RuneBaseAttribute(short attributeID, short modValue, int newUUID) { + super(newUUID); + + this.attributeID = attributeID; + this.modValue = modValue; + } + /** + * ResultSet Constructor + */ + public RuneBaseAttribute(ResultSet rs) throws SQLException { + super(rs); + + this.attributeID = rs.getShort("attributeID"); + this.modValue = rs.getShort("modValue"); + this.runeBaseID = rs.getInt("RuneBaseID"); + } + + /* + * Getters + */ + public short getAttributeID() { + return attributeID; + } + + public short getModValue() { + return modValue; + } + + public static void LoadAllAttributes(){ + DbManager.RuneBaseAttributeQueries.GET_ATTRIBUTES_FOR_RUNEBASE(); + + + //cache attributeLists for rune. + for (AbstractGameObject ago : DbManager.getList(GameObjectType.RuneBaseAttribute)){ + + RuneBaseAttribute runeBaseAttribute = (RuneBaseAttribute)ago; + + int runeBaseID = ((RuneBaseAttribute)runeBaseAttribute).runeBaseID; + if (runeBaseAttributeMap.get(runeBaseID) == null){ + ArrayList attributeList = new ArrayList<>(); + attributeList.add(runeBaseAttribute); + runeBaseAttributeMap.put(runeBaseID, attributeList); + } + else{ + ArrayListattributeList = runeBaseAttributeMap.get(runeBaseID); + attributeList.add(runeBaseAttribute); + runeBaseAttributeMap.put(runeBaseID, attributeList); + } + + } + + } + + /* + * Utils + */ + + + + @Override + public void updateDatabase() { + // TODO Auto-generated method stub + } +} diff --git a/src/engine/objects/RuneBaseEffect.java b/src/engine/objects/RuneBaseEffect.java new file mode 100644 index 00000000..f9d79828 --- /dev/null +++ b/src/engine/objects/RuneBaseEffect.java @@ -0,0 +1,72 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.gameManager.DbManager; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; + + +public class RuneBaseEffect extends AbstractGameObject { + + private byte type; + private String name; + private short amount; + private int runeBaseID; + + public static HashMap> RuneIDBaseEffectMap = new HashMap<>(); + /** + * ResultSet Constructor + */ + public RuneBaseEffect(ResultSet rs) throws SQLException { + super(rs); + this.type = rs.getByte("type"); + this.name = rs.getString("name"); + this.amount = rs.getShort("amount"); + this.runeBaseID = rs.getInt("runeID"); + } + + /* + * Getters + */ + + public int getType() { + return this.type; + } + + public String getName() { + return this.name; + } + + public short getAmount() { + return this.amount; + } + + + @Override + public void updateDatabase() { + + } + + public int getRuneBaseID() { + return runeBaseID; + } + + public static void LoadRuneBaseEffects(){ + //cache runebase effects. + DbManager.RuneBaseEffectQueries.GET_ALL_RUNEBASE_EFFECTS(); + //store runebase effects in new hashmap. + RuneBaseEffect.RuneIDBaseEffectMap = DbManager.RuneBaseEffectQueries.LOAD_BASEEFFECTS_FOR_RUNEBASE(); + } + +} \ No newline at end of file diff --git a/src/engine/objects/Runegate.java b/src/engine/objects/Runegate.java new file mode 100644 index 00000000..ad67bbaf --- /dev/null +++ b/src/engine/objects/Runegate.java @@ -0,0 +1,220 @@ +package engine.objects; + +import engine.Enum.RunegateType; +import engine.gameManager.BuildingManager; +import engine.net.ByteBufferWriter; + +import java.util.ArrayList; + +/* Runegates are tied to particular buildings at + * bootstrap. They aren't tighly coupled, with + * the Runegate merely toggling effect bits on it's + * parent building. + */ + +public class Runegate { + + // Runegate class Instance variables + private static final Runegate[] _runegates = new Runegate[9]; + + private final Portal[] _portals; + private final RunegateType gateType; + + private Runegate(RunegateType gateType) { + + this._portals = new Portal[8]; + this.gateType = gateType; + + // Each Runegate has a different destination + // for each portal opened. + configureGatePortals(); + + // Chaos, Khar and Oblivion are on by default + + _portals[RunegateType.CHAOS.ordinal()].activate(false); + _portals[RunegateType.OBLIV.ordinal()].activate(false); + _portals[RunegateType.MERCHANT.ordinal()].activate(false); + + } + + public void activatePortal(RunegateType gateType) { + + this._portals[gateType.ordinal()].activate(true); + + } + + public void deactivatePortal(RunegateType gateType) { + + this._portals[gateType.ordinal()].deactivate(); + + } + + public RunegateType getGateType() { + return this.gateType; + } + + public static Runegate[] getRunegates() { + return Runegate._runegates; + } + + public Portal[] getPortals() { + + return this._portals; + + } + + public void collidePortals() { + + for (Portal portal : this.getPortals()) { + + if (portal.isActive()) + portal.collide(); + } + } + + public static void loadAllRunegates() { + + for (RunegateType runegateType : RunegateType.values()) { + _runegates[runegateType.ordinal()] = new Runegate(runegateType); + } + + } + + public void _serializeForEnterWorld(ByteBufferWriter writer) { + + Building gateBuilding; + + gateBuilding = BuildingManager.getBuilding(this.gateType.getGateUUID()); + + writer.putInt(gateBuilding.getObjectType().ordinal()); + writer.putInt(gateBuilding.getObjectUUID()); + writer.putString(gateBuilding.getParentZone().getName()); + writer.putFloat(gateBuilding.getLoc().getLat()); + writer.putFloat(gateBuilding.getLoc().getAlt()); + writer.putFloat(gateBuilding.getLoc().getLong()); + } + + private void configureGatePortals() { + + // Source gate type, portal type and destination gate type; + switch (this.gateType) { + + case EARTH: + _portals[RunegateType.EARTH.ordinal()] = new Portal(RunegateType.EARTH, RunegateType.EARTH, RunegateType.EARTH); + _portals[RunegateType.AIR.ordinal()] = new Portal(RunegateType.EARTH, RunegateType.AIR, RunegateType.AIR); + _portals[RunegateType.FIRE.ordinal()] = new Portal(RunegateType.EARTH, RunegateType.FIRE, RunegateType.FORBID); + _portals[RunegateType.WATER.ordinal()] = new Portal(RunegateType.EARTH, RunegateType.WATER, RunegateType.WATER); + _portals[RunegateType.SPIRIT.ordinal()] = new Portal(RunegateType.EARTH, RunegateType.SPIRIT, RunegateType.SPIRIT); + _portals[RunegateType.CHAOS.ordinal()] = new Portal(RunegateType.EARTH, RunegateType.CHAOS, RunegateType.CHAOS); + _portals[RunegateType.OBLIV.ordinal()] = new Portal(RunegateType.EARTH, RunegateType.OBLIV, RunegateType.OBLIV); + _portals[RunegateType.MERCHANT.ordinal()] = new Portal(RunegateType.EARTH, RunegateType.MERCHANT, RunegateType.MERCHANT); + break; + + case AIR: + _portals[RunegateType.EARTH.ordinal()] = new Portal(RunegateType.AIR, RunegateType.EARTH, RunegateType.EARTH); + _portals[RunegateType.AIR.ordinal()] = new Portal(RunegateType.AIR, RunegateType.AIR, RunegateType.FORBID); + _portals[RunegateType.FIRE.ordinal()] = new Portal(RunegateType.AIR, RunegateType.FIRE, RunegateType.FIRE); + _portals[RunegateType.WATER.ordinal()] = new Portal(RunegateType.AIR, RunegateType.WATER, RunegateType.WATER); + _portals[RunegateType.SPIRIT.ordinal()] = new Portal(RunegateType.AIR, RunegateType.SPIRIT, RunegateType.SPIRIT); + _portals[RunegateType.CHAOS.ordinal()] = new Portal(RunegateType.AIR, RunegateType.CHAOS, RunegateType.CHAOS); + _portals[RunegateType.OBLIV.ordinal()] = new Portal(RunegateType.AIR, RunegateType.OBLIV, RunegateType.OBLIV); + _portals[RunegateType.MERCHANT.ordinal()] = new Portal(RunegateType.AIR, RunegateType.MERCHANT, RunegateType.MERCHANT); + + break; + + case FIRE: + _portals[RunegateType.EARTH.ordinal()] = new Portal(RunegateType.FIRE, RunegateType.EARTH, RunegateType.EARTH); + _portals[RunegateType.AIR.ordinal()] = new Portal(RunegateType.FIRE, RunegateType.AIR, RunegateType.AIR); + _portals[RunegateType.FIRE.ordinal()] = new Portal(RunegateType.FIRE, RunegateType.FIRE, RunegateType.FORBID); + _portals[RunegateType.WATER.ordinal()] = new Portal(RunegateType.FIRE, RunegateType.WATER, RunegateType.WATER); + _portals[RunegateType.SPIRIT.ordinal()] = new Portal(RunegateType.FIRE, RunegateType.SPIRIT, RunegateType.SPIRIT); + _portals[RunegateType.CHAOS.ordinal()] = new Portal(RunegateType.FIRE, RunegateType.CHAOS, RunegateType.CHAOS); + _portals[RunegateType.OBLIV.ordinal()] = new Portal(RunegateType.FIRE, RunegateType.OBLIV, RunegateType.OBLIV); + _portals[RunegateType.MERCHANT.ordinal()] = new Portal(RunegateType.FIRE, RunegateType.MERCHANT, RunegateType.MERCHANT); + break; + + case WATER: + _portals[RunegateType.EARTH.ordinal()] = new Portal(RunegateType.WATER, RunegateType.EARTH, RunegateType.EARTH); + _portals[RunegateType.AIR.ordinal()] = new Portal(RunegateType.WATER, RunegateType.AIR, RunegateType.AIR); + _portals[RunegateType.FIRE.ordinal()] = new Portal(RunegateType.WATER, RunegateType.FIRE, RunegateType.FIRE); + _portals[RunegateType.WATER.ordinal()] = new Portal(RunegateType.WATER, RunegateType.WATER, RunegateType.FORBID); + _portals[RunegateType.SPIRIT.ordinal()] = new Portal(RunegateType.WATER, RunegateType.SPIRIT, RunegateType.SPIRIT); + _portals[RunegateType.CHAOS.ordinal()] = new Portal(RunegateType.WATER, RunegateType.CHAOS, RunegateType.CHAOS); + _portals[RunegateType.OBLIV.ordinal()] = new Portal(RunegateType.WATER, RunegateType.OBLIV, RunegateType.OBLIV); + _portals[RunegateType.MERCHANT.ordinal()] = new Portal(RunegateType.WATER, RunegateType.MERCHANT, RunegateType.MERCHANT); + break; + + case SPIRIT: + _portals[RunegateType.EARTH.ordinal()] = new Portal(RunegateType.SPIRIT, RunegateType.EARTH, RunegateType.EARTH); + _portals[RunegateType.AIR.ordinal()] = new Portal(RunegateType.SPIRIT, RunegateType.AIR, RunegateType.AIR); + _portals[RunegateType.FIRE.ordinal()] = new Portal(RunegateType.SPIRIT, RunegateType.FIRE, RunegateType.FIRE); + _portals[RunegateType.WATER.ordinal()] = new Portal(RunegateType.SPIRIT, RunegateType.WATER, RunegateType.WATER); + _portals[RunegateType.SPIRIT.ordinal()] = new Portal(RunegateType.SPIRIT, RunegateType.SPIRIT, RunegateType.FORBID); + _portals[RunegateType.CHAOS.ordinal()] = new Portal(RunegateType.SPIRIT, RunegateType.CHAOS, RunegateType.CHAOS); + _portals[RunegateType.OBLIV.ordinal()] = new Portal(RunegateType.SPIRIT, RunegateType.OBLIV, RunegateType.OBLIV); + _portals[RunegateType.MERCHANT.ordinal()] = new Portal(RunegateType.SPIRIT, RunegateType.MERCHANT, RunegateType.MERCHANT); + break; + + case CHAOS: + _portals[RunegateType.EARTH.ordinal()] = new Portal(RunegateType.CHAOS, RunegateType.EARTH, RunegateType.EARTH); + _portals[RunegateType.AIR.ordinal()] = new Portal(RunegateType.CHAOS, RunegateType.AIR, RunegateType.AIR); + _portals[RunegateType.FIRE.ordinal()] = new Portal(RunegateType.CHAOS, RunegateType.FIRE, RunegateType.FIRE); + _portals[RunegateType.WATER.ordinal()] = new Portal(RunegateType.CHAOS, RunegateType.WATER, RunegateType.WATER); + _portals[RunegateType.SPIRIT.ordinal()] = new Portal(RunegateType.CHAOS, RunegateType.SPIRIT, RunegateType.SPIRIT); + _portals[RunegateType.CHAOS.ordinal()] = new Portal(RunegateType.CHAOS, RunegateType.CHAOS, RunegateType.MERCHANT); + _portals[RunegateType.OBLIV.ordinal()] = new Portal(RunegateType.CHAOS, RunegateType.OBLIV, RunegateType.OBLIV); + _portals[RunegateType.MERCHANT.ordinal()] = new Portal(RunegateType.CHAOS, RunegateType.MERCHANT, RunegateType.MERCHANT); + break; + + case OBLIV: + _portals[RunegateType.EARTH.ordinal()] = new Portal(RunegateType.OBLIV, RunegateType.EARTH, RunegateType.EARTH); + _portals[RunegateType.AIR.ordinal()] = new Portal(RunegateType.OBLIV, RunegateType.AIR, RunegateType.AIR); + _portals[RunegateType.FIRE.ordinal()] = new Portal(RunegateType.OBLIV, RunegateType.FIRE, RunegateType.FIRE); + _portals[RunegateType.WATER.ordinal()] = new Portal(RunegateType.OBLIV, RunegateType.WATER, RunegateType.WATER); + _portals[RunegateType.SPIRIT.ordinal()] = new Portal(RunegateType.OBLIV, RunegateType.SPIRIT, RunegateType.SPIRIT); + _portals[RunegateType.CHAOS.ordinal()] = new Portal(RunegateType.OBLIV, RunegateType.CHAOS, RunegateType.CHAOS); + _portals[RunegateType.OBLIV.ordinal()] = new Portal(RunegateType.OBLIV, RunegateType.OBLIV, RunegateType.MERCHANT); + _portals[RunegateType.MERCHANT.ordinal()] = new Portal(RunegateType.OBLIV, RunegateType.MERCHANT, RunegateType.MERCHANT); + break; + + case MERCHANT: + _portals[RunegateType.EARTH.ordinal()] = new Portal(RunegateType.MERCHANT, RunegateType.EARTH, RunegateType.EARTH); + _portals[RunegateType.AIR.ordinal()] = new Portal(RunegateType.MERCHANT, RunegateType.AIR, RunegateType.AIR); + _portals[RunegateType.FIRE.ordinal()] = new Portal(RunegateType.MERCHANT, RunegateType.FIRE, RunegateType.FIRE); + _portals[RunegateType.WATER.ordinal()] = new Portal(RunegateType.MERCHANT, RunegateType.WATER, RunegateType.WATER); + _portals[RunegateType.SPIRIT.ordinal()] = new Portal(RunegateType.MERCHANT, RunegateType.SPIRIT, RunegateType.SPIRIT); + _portals[RunegateType.CHAOS.ordinal()] = new Portal(RunegateType.MERCHANT, RunegateType.CHAOS, RunegateType.CHAOS); + _portals[RunegateType.OBLIV.ordinal()] = new Portal(RunegateType.MERCHANT, RunegateType.OBLIV, RunegateType.OBLIV); + _portals[RunegateType.MERCHANT.ordinal()] = new Portal(RunegateType.MERCHANT, RunegateType.MERCHANT, RunegateType.FORBID); + break; + + case FORBID: + _portals[RunegateType.EARTH.ordinal()] = new Portal(RunegateType.FORBID, RunegateType.EARTH, RunegateType.EARTH); + _portals[RunegateType.AIR.ordinal()] = new Portal(RunegateType.FORBID, RunegateType.AIR, RunegateType.AIR); + _portals[RunegateType.FIRE.ordinal()] = new Portal(RunegateType.FORBID, RunegateType.FIRE, RunegateType.FIRE); + _portals[RunegateType.WATER.ordinal()] = new Portal(RunegateType.FORBID, RunegateType.WATER, RunegateType.WATER); + _portals[RunegateType.SPIRIT.ordinal()] = new Portal(RunegateType.FORBID, RunegateType.SPIRIT, RunegateType.SPIRIT); + _portals[RunegateType.CHAOS.ordinal()] = new Portal(RunegateType.FORBID, RunegateType.CHAOS, RunegateType.CHAOS); + _portals[RunegateType.OBLIV.ordinal()] = new Portal(RunegateType.FORBID, RunegateType.OBLIV, RunegateType.OBLIV); + _portals[RunegateType.MERCHANT.ordinal()] = new Portal(RunegateType.FORBID, RunegateType.MERCHANT, RunegateType.MERCHANT); + break; + + } + + } + + public static ArrayList GetAllOpenGateIDStrings(){ + ArrayList openGateIDStrings = new ArrayList<>(); + + openGateIDStrings.add("TRA-003"); + openGateIDStrings.add("TRA-004"); + openGateIDStrings.add("TRA-005"); + openGateIDStrings.add("TRA-006"); + openGateIDStrings.add("TRA-007"); + openGateIDStrings.add("TRA-008"); + openGateIDStrings.add("TRA-009"); + openGateIDStrings.add("TRA-010"); + return openGateIDStrings; + } + +} diff --git a/src/engine/objects/Shrine.java b/src/engine/objects/Shrine.java new file mode 100644 index 00000000..86cd9a99 --- /dev/null +++ b/src/engine/objects/Shrine.java @@ -0,0 +1,355 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum; +import engine.Enum.BuildingGroup; +import engine.Enum.ShrineType; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; + +public class Shrine extends AbstractWorldObject implements Comparable { + + private final ShrineType shrineType; + private Integer favors; + private final int buildingID; + + public static ConcurrentHashMap shrinesByBuildingUUID = new ConcurrentHashMap<>(); + + /** + * ResultSet Constructor + */ + public Shrine(ResultSet rs) throws SQLException { + super(rs); + this.shrineType = ShrineType.valueOf(rs.getString("shrine_type")); + this.favors = rs.getInt("shrine_favors"); + this.buildingID = rs.getInt("parent"); + shrinesByBuildingUUID.put(this.buildingID, this); + } + + // Decays this shrine's favor by 10% + + public void decay() { + + if (this.getFavors() == 0) + return; + + int decayAmount = (int) (this.getFavors() - (this.getFavors() *.10f)); + + if (decayAmount < 0) + decayAmount = 0; + + if (!DbManager.ShrineQueries.updateFavors(this, decayAmount, this.getFavors())) { + Logger.error("Shrine Decay", "Error writing to DB. UUID: " + this.getObjectUUID()); + return; + } + this.favors = decayAmount; + + Logger.info( shrineType.name() + " uuid:" + this.getObjectUUID() + " Amount: " + this.getFavors() *.10f ); + + } + + public synchronized boolean addFavor(PlayerCharacter boonOwner, Item boonItem) { + + if (boonOwner == null) + return false; + + if (boonItem == null) + return false; + + ItemBase ib = boonItem.getItemBase(); + + if (ib == null) + return false; + + if (!boonOwner.getCharItemManager().doesCharOwnThisItem(boonItem.getObjectUUID())) + return false; + + ArrayList boonList = Boon.GetBoonsForItemBase.get(ib.getUUID()); + + if (boonList == null) + return false; + + for (Boon boon : boonList) { + + ShrineType boonShrineType = boon.getShrineType(); + + if (boonShrineType != shrineType) + continue; + + //Same Shrine Type, add favors and stop loop. + int amount = boon.getAmount() * boonItem.getNumOfItems(); + int oldAmount = this.favors; + + if (!DbManager.ShrineQueries.updateFavors(this, this.favors + amount, oldAmount)) { + ChatManager.chatSystemError(boonOwner, "Failed to add boon to shrine."); + return false; + } + + this.favors += amount; + boonOwner.getCharItemManager().delete(boonItem); + boonOwner.getCharItemManager().updateInventory(); + return true; + } + return false; + } + + public synchronized boolean takeFavor(PlayerCharacter boonOwner) { + + if (boonOwner == null) + return false; + + int oldAmount = this.favors; + int newAmount = this.favors - 1; + + if (!DbManager.ShrineQueries.updateFavors(this, newAmount, oldAmount)) { + ChatManager.chatSystemError(boonOwner, "Failed to add boon to shrine."); + return false; + } + this.favors = newAmount; + return true; + } + + public static boolean canTakeFavor(PlayerCharacter grantee, Shrine shrine) { + + if (shrine.shrineType.isRace()) + switch (grantee.getRaceID()) { + case 2000: + case 2001: + if (shrine.shrineType == ShrineType.Aelfborn) + return true; + break; + case 2002: + case 2003: + if (shrine.shrineType == ShrineType.Aracoix) + return true; + break; + case 2004: + case 2005: + if (shrine.shrineType == ShrineType.Centaur) + return true; + break; + case 2006: + if (shrine.shrineType == ShrineType.Dwarf) + return true; + break; + case 2008: + case 2009: + if (shrine.shrineType == ShrineType.Elf) + return true; + break; + case 2010: + case 2027: + if (shrine.shrineType == ShrineType.HalfGiant) + return true; + break; + case 2011: + case 2012: + if (shrine.shrineType == ShrineType.Human) + return true; + break; + case 2013: + case 2014: + if (shrine.shrineType == ShrineType.Irekei) + return true; + break; + case 2015: + case 2016: + if (shrine.shrineType == ShrineType.Shade) + return true; + break; + case 2017: + if (shrine.shrineType == ShrineType.Minotaur) + return true; + break; + + case 2025: + case 2026: + if (shrine.shrineType == ShrineType.Nephilim) + return true; + break; + case 2028: + case 2029: + if (shrine.shrineType == ShrineType.Vampire) + return true; + break; + + } + else + switch (grantee.getPromotionClassID()) { + case 2504: + if (shrine.shrineType == ShrineType.Assassin) + return true; + break; + case 2505: + if (shrine.shrineType == ShrineType.Barbarian) + return true; + break; + case 2506: + if (shrine.shrineType == ShrineType.Bard) + return true; + break; + case 2507: + if (shrine.shrineType == ShrineType.Channeler) + return true; + break; + case 2508: + if (shrine.shrineType == ShrineType.Confessor) + return true; + break; + case 2509: + if (shrine.shrineType == ShrineType.Crusader) + return true; + break; + case 2510: + if (shrine.shrineType == ShrineType.Druid) + return true; + break; + case 2511: + if (shrine.shrineType == ShrineType.Fury) + return true; + break; + case 2512: + if (shrine.shrineType == ShrineType.Huntress) + return true; + break; + case 2513: + if (shrine.shrineType == ShrineType.Prelate) + return true; + break; + case 2514: + if (shrine.shrineType == ShrineType.Ranger) + return true; + break; + case 2515: + if (shrine.shrineType == ShrineType.Scout) + return true; + break; + case 2516: + if (shrine.shrineType == ShrineType.Templar) + return true; + break; + case 2517: + if (shrine.shrineType == ShrineType.Warlock) + return true; + break; + case 2518: + if (shrine.shrineType == ShrineType.Warrior) + return true; + break; + case 2519: + if (shrine.shrineType == ShrineType.Priest) + return true; + break; + case 2520: + if (shrine.shrineType == ShrineType.Thief) + return true; + break; + case 2521: + if (shrine.shrineType == ShrineType.Wizard) + return true; + break; + case 2523: + if (shrine.shrineType == ShrineType.Doomsayer) + return true; + break; + case 2524: + if (shrine.shrineType == ShrineType.Sentinel) + return true; + break; + case 2525: + if (shrine.shrineType == ShrineType.Necromancer) + return true; + break; + case 2526: + if (shrine.shrineType == ShrineType.Nightstalker) + return true; + break; + } + + return false; + } + + public static ShrineType getShrineTypeByBlueprintUUID(int blueprintUUID) { + + for (ShrineType shrineType : ShrineType.values()) { + + if (shrineType.getBlueprintUUID() == blueprintUUID) + return shrineType; + } + return null; + } + + @Override + public int compareTo(Shrine other) { + return other.favors.compareTo(this.favors); + } + + public int getRank() { + return shrineType.getShrinesCopy().indexOf(this); + } + + public ShrineType getShrineType() { + return shrineType; + } + + public static void RemoveShrineFromCacheByBuilding(Building building) { + + if (building.getBlueprint() != null && building.getBlueprint().getBuildingGroup() == BuildingGroup.SHRINE) { + Shrine shrine = Shrine.shrinesByBuildingUUID.get(building.getObjectUUID()); + + if (shrine != null) { + shrine.shrineType.RemoveShrineFromServerList(shrine); + Shrine.shrinesByBuildingUUID.remove(building.getObjectUUID()); + DbManager.removeFromCache(Enum.GameObjectType.Shrine, + shrine.getObjectUUID()); + } + } + + } + + @Override + public void updateDatabase() { + // TODO Auto-generated method stub + + } + + public int getFavors() { + return favors; + } + + public int getBuildingID() { + return buildingID; + } + + @Override + public void runAfterLoad() { + // TODO Auto-generated method stub + + } + + + @Override + public void removeFromCache() { + // TODO Auto-generated method stub + + } + + public void setFavors(Integer favors) { + this.favors = favors; + } + +} diff --git a/src/engine/objects/SkillReq.java b/src/engine/objects/SkillReq.java new file mode 100644 index 00000000..99f4dea9 --- /dev/null +++ b/src/engine/objects/SkillReq.java @@ -0,0 +1,97 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.gameManager.DbManager; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; + + +public class SkillReq extends AbstractGameObject { + + + private int skillID; + private short level; + private ArrayList skillReqs; + + /* This shouldn't be used + public SkillReq(SkillsBase skillsBase, short level, ArrayListskillReqs) { + + super(); + this.skillsBase = skillsBase; + this.level = level; + this.skillReqs = skillReqs; + } + */ + + /* This shouldn't be used + public SkillReq(SkillsBase skillsBase, short level, ArrayListskillReqs, int newUUID) { + + super(newUUID); + this.skillsBase = skillsBase; + this.level = level; + this.skillReqs = skillReqs; + } + */ + + /* This shouldn't be used + public SkillReq(SkillReq a, int newUUID) { + super(a, newUUID); + this.skillsBase = a.skillsBase; + this.level = a.level; + this.skillReqs = a.skillReqs; + } + */ + + /** + * ResultSet Constructor + */ + public SkillReq(ResultSet rs) throws SQLException { + super(rs, 0); + this.skillID = rs.getInt("skillID"); + this.level = rs.getShort("level"); + skillReqs = new ArrayList<>(0); + + int skillReq; + skillReq = rs.getInt("skillreq1"); + if (skillReq > 0) skillReqs.add((byte)skillReq); + skillReq = rs.getInt("skillreq2"); + if (skillReq > 0) skillReqs.add((byte)skillReq); + skillReq = rs.getInt("skillreq3"); + if (skillReq > 0) skillReqs.add((byte)skillReq); + } + + /* + * Getters + */ + public SkillsBase getSkillsBase() { + return DbManager.SkillsBaseQueries.GET_BASE(this.skillID); + } + + public int getSkillID() { + return this.skillID; + } + + public short getLevel() { + return this.level; + } + + public ArrayList getSkillReqs() { + return this.skillReqs; + } + + + @Override + public void updateDatabase() { + + } +} \ No newline at end of file diff --git a/src/engine/objects/SkillsBase.java b/src/engine/objects/SkillsBase.java new file mode 100644 index 00000000..2c37fab7 --- /dev/null +++ b/src/engine/objects/SkillsBase.java @@ -0,0 +1,160 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + package engine.objects; + +import engine.Enum.SourceType; +import engine.gameManager.DbManager; +import engine.server.MBServerStatics; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; + + +public class SkillsBase extends AbstractGameObject { + + private final String name; + private final String nameNoSpace; + private final String description; + private final int token; + private final short strMod; + private final short dexMod; + private final short conMod; + private final short intMod; + private final short spiMod; + public static ConcurrentHashMap skillsCache = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + public static ConcurrentHashMap tokenCache = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + public static HashMap> runeSkillsCache = new HashMap<>(); + public SourceType sourceType; + /** + * No Table ID Constructor + */ + public SkillsBase(String name, String description, int token, short strMod, + short dexMod, short conMod, short intMod, short spiMod) { + super(); + this.name = name; + this.nameNoSpace = name.replace(" ", ""); + this.sourceType = SourceType.GetSourceType(this.nameNoSpace.replace(",", "")); + this.description = description; + this.token = token; + this.strMod = strMod; + this.dexMod = dexMod; + this.conMod = conMod; + this.intMod = intMod; + this.spiMod = spiMod; + } + + /** + * Normal Constructor + */ + public SkillsBase(String name, String description, int token, short strMod, + short dexMod, short conMod, short intMod, short spiMod, int newUUID) { + super(newUUID); + this.name = name; + this.nameNoSpace = name.replace(" ", ""); + this.description = description; + this.token = token; + this.strMod = strMod; + this.dexMod = dexMod; + this.conMod = conMod; + this.intMod = intMod; + this.spiMod = spiMod; + } + + /** + * ResultSet Constructor + */ + public SkillsBase(ResultSet rs) throws SQLException { + super(rs); + + this.name = rs.getString("name"); + this.nameNoSpace = name.replace(" ", ""); + this.description = rs.getString("description"); + this.sourceType = SourceType.GetSourceType(this.nameNoSpace.replace("-", "").replace("\"", "").replace(",", "")); + this.token = rs.getInt("token"); + this.strMod = rs.getShort("strMod"); + this.dexMod = rs.getShort("dexMod"); + this.conMod = rs.getShort("conMod"); + this.intMod = rs.getShort("intMod"); + this.spiMod = rs.getShort("spiMod"); + } + + /* + * Getters + */ + public String getName() { + return name; + } + + public String getNameNoSpace() { + return nameNoSpace; + } + + + public String getDescription() { + return description; + } + + public int getToken() { + return this.token; + } + + public short getStrMod() { + return this.strMod; + } + + public short getDexMod() { + return this.dexMod; + } + + public short getConMod() { + return this.conMod; + } + + public short getIntMod() { + return this.intMod; + } + + public short getSpiMod() { + return this.spiMod; + } + + public static SkillsBase getFromCache(String name) { + if (skillsCache.containsKey(name)) + return skillsCache.get(name); + else + return null; + } + + public static SkillsBase getFromCache(int token) { + if (tokenCache.containsKey(token)) + return tokenCache.get(token); + else + return null; + } + + public static void putInCache(SkillsBase sb) { + + if(sb == null) + return; + + DbManager.addToCache(sb); + skillsCache.putIfAbsent(sb.name, sb); + tokenCache.putIfAbsent(sb.token, sb); + } + + + + @Override + public void updateDatabase() { + // TODO Auto-generated method stub + } +} diff --git a/src/engine/objects/SkillsBaseAttribute.java b/src/engine/objects/SkillsBaseAttribute.java new file mode 100644 index 00000000..c8ce47a2 --- /dev/null +++ b/src/engine/objects/SkillsBaseAttribute.java @@ -0,0 +1,70 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + package engine.objects; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class SkillsBaseAttribute extends AbstractGameObject { + + private short attributeID; + private short modValue; + + /** + * No Table ID Constructor + */ + public SkillsBaseAttribute(short attributeID, short modValue) { + super(); + + this.attributeID = attributeID; + this.modValue = modValue; + } + + /** + * Normal Constructor + */ + public SkillsBaseAttribute(short attributeID, short modValue, int newUUID) { + super(newUUID); + + this.attributeID = attributeID; + this.modValue = modValue; + } + + /** + * ResultSet Constructor + */ + public SkillsBaseAttribute(ResultSet rs) throws SQLException { + super(rs); + + this.attributeID = rs.getShort("attributeID"); + this.modValue = rs.getShort("modValue"); + } + + /* + * Getters + */ + public short getAttributeID() { + return attributeID; + } + + public short getModValue() { + return modValue; + } + + + /* + * Database + */ + @Override + public void updateDatabase() { + // TODO Auto-generated method stub + } +} diff --git a/src/engine/objects/SpecialLoot.java b/src/engine/objects/SpecialLoot.java new file mode 100644 index 00000000..8afa6657 --- /dev/null +++ b/src/engine/objects/SpecialLoot.java @@ -0,0 +1,77 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.gameManager.DbManager; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; + +public class SpecialLoot extends AbstractGameObject { + + private int itemID; + private int dropChance; + private boolean dropOnDeath; + private boolean noSteal; + private int lootSetID; + + public static HashMap> LootMap = new HashMap<>(); + /** + * ResultSet Constructor + */ + public SpecialLoot(ResultSet rs) throws SQLException { + super(rs); + this.itemID = rs.getInt("itemID"); + this.dropChance = rs.getInt("dropChance"); + this.dropOnDeath = rs.getBoolean("dropOnDeath"); + this.noSteal = rs.getBoolean("noSteal"); + } + + public SpecialLoot(ResultSet rs,boolean specialLoot) throws SQLException { + super(rs); + + this.lootSetID = rs.getInt("lootSet"); + this.itemID = rs.getInt("itemID"); + this.dropChance = rs.getInt("dropChance"); + this.dropOnDeath = false; + this.noSteal = true; + } + + /* + * Getters + */ + + public int getItemID() { + return this.itemID; + } + + public int getDropChance() { + return this.dropChance; + } + + public boolean dropOnDeath() { + return this.dropOnDeath; + } + + public boolean noSteal() { + return this.noSteal; + } + + public static ArrayList getSpecialLoot(int mobbaseID) { + return DbManager.SpecialLootQueries.GET_SPECIALLOOT(mobbaseID); + } + + @Override + public void updateDatabase() { + + } +} \ No newline at end of file diff --git a/src/engine/objects/StaticColliders.java b/src/engine/objects/StaticColliders.java new file mode 100644 index 00000000..4794d676 --- /dev/null +++ b/src/engine/objects/StaticColliders.java @@ -0,0 +1,101 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.gameManager.DbManager; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; + +public class StaticColliders { + + private int meshID; + private float startX; + private float startY; + private float endX; + private float endY; + private int doorID; + public static HashMap> _staticColliders = new HashMap<>(); + private boolean link = false; + + + + + /** + * ResultSet Constructor + */ + + public StaticColliders(ResultSet rs) throws SQLException { + this.meshID = rs.getInt("meshID"); + this.startX = rs.getInt("startX"); + this.startY = rs.getInt("startY"); + this.endX = rs.getInt("endX"); + this.endY = rs.getInt("endY"); + this.doorID = rs.getInt("doorID"); + this.link = rs.getBoolean("link"); + } + + public StaticColliders(int meshID, float startX, float startY, float endX, + float endY, int doorID,boolean link) { + super(); + this.meshID = meshID; + this.startX = startX; + this.startY = startY; + this.endX = endX; + this.endY = endY; + this.doorID = doorID; + this.link = link; + } + + public static void loadAllStaticColliders(){ + _staticColliders = DbManager.BuildingQueries.LOAD_ALL_STATIC_COLLIDERS(); + } + + public static ArrayList GetStaticCollidersForMeshID(int meshID) { + return _staticColliders.get(meshID); + } + + + + + public int getMeshID() { + return meshID; + } + + public float getStartX() { + return startX; + } + + public float getStartY() { + return startY; + } + + public float getEndX() { + return endX; + } + + public float getEndY() { + return endY; + } + + public int getDoorID() { + return doorID; + } + + public boolean isLink() { + return link; + } + + public void setLink(boolean link) { + this.link = link; + } +} diff --git a/src/engine/objects/Transaction.java b/src/engine/objects/Transaction.java new file mode 100644 index 00000000..c03b1cdf --- /dev/null +++ b/src/engine/objects/Transaction.java @@ -0,0 +1,111 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.objects; + +import engine.Enum.GameObjectType; +import engine.Enum.TransactionType; +import org.joda.time.DateTime; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Date; + + + +public class Transaction implements Comparable { + + private final int warehouseUUID; + private final int targetUUID; + private final Resource resource; + private final DateTime date; + private final int amount; + private GameObjectType targetType; + private final TransactionType transactionType; + + + + public Transaction(ResultSet rs) throws SQLException { + this.warehouseUUID = rs.getInt("warehouseUID"); + this.targetUUID = rs.getInt("targetUID"); + this.targetType = GameObjectType.valueOf(rs.getString("targetType")); + this.transactionType = TransactionType.valueOf(rs.getString("type").toUpperCase()); + this.resource = Resource.valueOf(rs.getString("resource").toUpperCase()); + this.amount = rs.getInt("amount"); + + Date sqlDateTime = rs.getTimestamp("date"); + + if (sqlDateTime != null) + this.date = new DateTime(sqlDateTime); + else + this.date = DateTime.now(); + + } + + + public Transaction(int warehouseUUID,GameObjectType targetType, int targetUUID, TransactionType transactionType, Resource resource, int amount, + DateTime date) { + this.warehouseUUID = warehouseUUID; + this.targetUUID = targetUUID; + this.resource = resource; + this.date = date; + this.amount = amount; + this.targetType = targetType; + this.transactionType = transactionType; + } + + + public int getWarehouseUUID() { + return warehouseUUID; + } + + + public int getTargetUUID() { + return targetUUID; + } + + + public Resource getResource() { + return resource; + } + + + public DateTime getDate() { + return date; + } + + + public int getAmount() { + return amount; + } + + + public TransactionType getTransactionType() { + return transactionType; + } + + + @Override + public int compareTo(Transaction arg0) { + // TODO Auto-generated method stub + return 0; + } + + + public GameObjectType getTargetType() { + return targetType; + } + + + public void setTargetType(GameObjectType targetType) { + this.targetType = targetType; + } + +} diff --git a/src/engine/objects/VendorDialog.java b/src/engine/objects/VendorDialog.java new file mode 100644 index 00000000..3bf2cdd1 --- /dev/null +++ b/src/engine/objects/VendorDialog.java @@ -0,0 +1,74 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.gameManager.DbManager; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; + + +public class VendorDialog extends AbstractGameObject { + + private final String dialogType; + private final String intro; + private ArrayList options = new ArrayList<>(); + + public VendorDialog(String dialogType, String intro, int UUID) { + super(UUID); + this.dialogType = dialogType; + this.intro = intro; + } + + /** + * ResultSet Constructor + */ + public VendorDialog(ResultSet rs) throws SQLException { + super(rs); + this.dialogType = rs.getString("dialogType"); + this.intro = rs.getString("intro"); + this.options = DbManager.MenuQueries.GET_MENU_OPTIONS(this.getObjectUUID()); + } + + /* + * Getters + */ + public String getDialogType() { + return this.dialogType; + } + + public String getIntro() { + return this.intro; + } + + public ArrayList getOptions() { + return this.options; + } + + private static VendorDialog vd; + public static VendorDialog getHostileVendorDialog() { + if (VendorDialog.vd == null) + VendorDialog.vd = new VendorDialog("TrainerDialog", "HostileIntro", 0); + return VendorDialog.vd; + } + + + /* + * Database + */ + @Override + public void updateDatabase() {} + + public static VendorDialog getVendorDialog(int id) { + + return DbManager.VendorDialogQueries.GET_VENDORDIALOG(id); + } +} diff --git a/src/engine/objects/Warehouse.java b/src/engine/objects/Warehouse.java new file mode 100644 index 00000000..a044810e --- /dev/null +++ b/src/engine/objects/Warehouse.java @@ -0,0 +1,1340 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import ch.claude_martin.enumbitset.EnumBitSet; +import engine.Enum; +import engine.Enum.*; +import engine.gameManager.BuildingManager; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.*; +import engine.server.MBServerStatics; +import org.joda.time.DateTime; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; + +public class Warehouse extends AbstractWorldObject { + + + private int UID; + + public EnumBitSet lockedResourceTypes; + + private int buildingUID; + private ArrayList transactions = new ArrayList<>(); + + private ConcurrentHashMap resources = new ConcurrentHashMap<>(); + + public static ItemBase goldIB = ItemBase.getItemBase(7); + public static ItemBase stoneIB = ItemBase.getItemBase(1580000); + public static ItemBase truesteelIB = ItemBase.getItemBase(1580001); + public static ItemBase ironIB = ItemBase.getItemBase(1580002); + public static ItemBase adamantIB = ItemBase.getItemBase(1580003); + public static ItemBase lumberIB = ItemBase.getItemBase(1580004); + public static ItemBase oakIB = ItemBase.getItemBase(1580005); + public static ItemBase bronzewoodIB = ItemBase.getItemBase(1580006); + public static ItemBase mandrakeIB = ItemBase.getItemBase(1580007); + public static ItemBase coalIB = ItemBase.getItemBase(1580008); + public static ItemBase agateIB = ItemBase.getItemBase(1580009); + public static ItemBase diamondIB = ItemBase.getItemBase(1580010); + public static ItemBase onyxIB = ItemBase.getItemBase(1580011); + public static ItemBase azothIB = ItemBase.getItemBase(1580012); + public static ItemBase orichalkIB = ItemBase.getItemBase(1580013); + public static ItemBase antimonyIB = ItemBase.getItemBase(1580014); + public static ItemBase sulferIB = ItemBase.getItemBase(1580015); + public static ItemBase quicksilverIB = ItemBase.getItemBase(1580016); + public static ItemBase galvorIB = ItemBase.getItemBase(1580017); + public static ItemBase wormwoodIB = ItemBase.getItemBase(1580018); + public static ItemBase obsidianIB = ItemBase.getItemBase(1580019); + public static ItemBase bloodstoneIB = ItemBase.getItemBase(1580020); + public static ItemBase mithrilIB = ItemBase.getItemBase(1580021); + public static ConcurrentHashMap maxResources = new ConcurrentHashMap<>(); + public static ConcurrentHashMap warehouseByBuildingUUID = new ConcurrentHashMap<>(); + + + + public static ConcurrentHashMap getMaxResources() { + if(maxResources.size() != 23){ + maxResources.put(7, 100000000); + maxResources.put(1580000, 10000); + maxResources.put(1580001, 2000); + maxResources.put(1580002, 2000); + maxResources.put(1580003, 1000); + maxResources.put(1580004, 10000); + maxResources.put(1580005, 3000); + maxResources.put(1580006, 3000); + maxResources.put(1580007, 1000); + maxResources.put(1580008, 3000); + maxResources.put(1580009, 2000); + maxResources.put(1580010, 2000); + maxResources.put(1580011, 1000); + maxResources.put(1580012, 2000); + maxResources.put(1580013, 3000); + maxResources.put(1580014, 1000); + maxResources.put(1580015, 1000); + maxResources.put(1580016, 1000); + maxResources.put(1580017, 500); + maxResources.put(1580018, 500); + maxResources.put(1580019, 500); + maxResources.put(1580020, 500); + maxResources.put(1580021, 500); + } + + return maxResources; + } + + /** + * ResultSet Constructor + */ + public Warehouse(ResultSet rs) throws SQLException { + super(rs); + this.UID = rs.getInt("UID"); + this.resources.put(stoneIB, rs.getInt("warehouse_stone")); + this.resources.put(truesteelIB,rs.getInt("warehouse_truesteel")); + this.resources.put(ironIB,rs.getInt("warehouse_iron")); + this.resources.put(adamantIB,rs.getInt("warehouse_adamant")); + this.resources.put(lumberIB,rs.getInt("warehouse_lumber")); + this.resources.put(oakIB,rs.getInt("warehouse_oak")); + this.resources.put(bronzewoodIB,rs.getInt("warehouse_bronzewood")); + this.resources.put(mandrakeIB,rs.getInt("warehouse_mandrake")); + this.resources.put(coalIB,rs.getInt("warehouse_coal")); + this.resources.put(agateIB,rs.getInt("warehouse_agate")); + this.resources.put(diamondIB,rs.getInt("warehouse_diamond")); + this.resources.put(onyxIB,rs.getInt("warehouse_onyx")); + this.resources.put(azothIB,rs.getInt("warehouse_azoth")); + this.resources.put(orichalkIB,rs.getInt("warehouse_orichalk")); + this.resources.put(antimonyIB,rs.getInt("warehouse_antimony")); + this.resources.put(sulferIB,rs.getInt("warehouse_sulfur")); + this.resources.put(quicksilverIB,rs.getInt("warehouse_quicksilver")); + this.resources.put(galvorIB,rs.getInt("warehouse_galvor")); + this.resources.put(wormwoodIB,rs.getInt("warehouse_wormwood")); + this.resources.put(obsidianIB,rs.getInt("warehouse_obsidian")); + this.resources.put(bloodstoneIB,rs.getInt("warehouse_bloodstone")); + this.resources.put(mithrilIB,rs.getInt("warehouse_mithril")); + this.resources.put(goldIB, rs.getInt("warehouse_gold")); + this.lockedResourceTypes = EnumBitSet.asEnumBitSet(rs.getLong("warehouse_locks"), Enum.ResourceType.class); + this.buildingUID = rs.getInt("parent"); + Warehouse.warehouseByBuildingUUID.put(this.buildingUID, this); + } + + public static void warehouseDeposit(MerchantMsg msg, PlayerCharacter player, NPC npc, ClientConnection origin) { + + Building warehouseBuilding; + Warehouse warehouse; + int depositAmount; + Dispatch dispatch; + + Item resource = Item.getFromCache(msg.getItemID()); + + if (resource == null) + return; + + depositAmount = msg.getAmount(); + CharacterItemManager itemMan = player.getCharItemManager(); + + if (itemMan.doesCharOwnThisItem(resource.getObjectUUID()) == false) + return; + + warehouseBuilding = npc.getBuilding(); + + if (warehouseBuilding == null) + return; + + warehouse = warehouseByBuildingUUID.get(warehouseBuilding.getObjectUUID()); + + if (warehouse == null) + return; + + ItemBase ib = resource.getItemBase(); + + if (!warehouse.deposit(player, resource, depositAmount, true,true)) { + // ChatManager.chatGuildError(player, "Failed to deposit " + ib.getName() +"."); + // Logger.debug("OpenWindow", player.getName() + " Failed to deposit Item with ID " + resource.getObjectUUID() + " from Warehouse With ID = " + warehouseBuilding.getObjectUUID()); + return; + } + + ViewResourcesMessage vrm = new ViewResourcesMessage(player); + vrm.setGuild(player.getGuild()); + vrm.setWarehouseBuilding(warehouseBuilding); + vrm.configure(); + dispatch = Dispatch.borrow(player, vrm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + public static void warehouseWithdraw(MerchantMsg msg, PlayerCharacter player, NPC npc, ClientConnection origin) { + + int withdrawAmount; + Building warehouseBuilding; + Warehouse warehouse; + Dispatch dispatch; + + withdrawAmount = msg.getAmount(); + warehouseBuilding = npc.getBuilding(); + + if (warehouseBuilding == null) + return; + + if (player.getGuild() != warehouseBuilding.getGuild() || GuildStatusController.isInnerCouncil(player.getGuildStatus()) == false) + return; + + warehouse = warehouseByBuildingUUID.get(warehouseBuilding.getObjectUUID()); + + if (warehouse == null) + return; + + int hashID = msg.getHashID(); + int itemBaseID = ItemBase.getItemHashIDMap().get(hashID); + ItemBase ib = ItemBase.getItemBase(itemBaseID); + + if (ib == null) { + Logger.debug("Failed to find Resource ItemBaseID with Hash ID = " + hashID); + return; + } + + if (warehouse.isResourceLocked(ib) == true) { + ChatManager.chatSystemInfo(player, "You cannot withdrawl a locked resource."); + return; + } + if (!warehouse.withdraw(player, ib, withdrawAmount, true,true)) { + ChatManager.chatGuildError(player, "Failed to withdrawl " + ib.getName() + '.'); + Logger.debug(player.getName() + " Failed to withdrawl =" + ib.getName() + " from Warehouse With ID = " + warehouseBuilding.getObjectUUID()); + return; + } + + ViewResourcesMessage vrm = new ViewResourcesMessage(player); + vrm.setGuild(player.getGuild()); + vrm.setWarehouseBuilding(warehouseBuilding); + vrm.configure(); + dispatch = Dispatch.borrow(player, vrm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + public static void warehouseLock(MerchantMsg msg, PlayerCharacter player, NPC npc, ClientConnection origin) { + Building warehouse; + int hashID; + Dispatch dispatch; + + hashID = msg.getHashID(); + warehouse = npc.getBuilding(); + + if (warehouse == null) + return; + + if (player.getGuild() != warehouse.getGuild() || GuildStatusController.isInnerCouncil(player.getGuildStatus()) == false) + return; + + Warehouse wh = warehouseByBuildingUUID.get(warehouse.getObjectUUID()); + + if (wh == null) + return; + + int itemBaseID = ItemBase.getItemHashIDMap().get(hashID); + ItemBase ib = ItemBase.getItemBase(itemBaseID); + + if (ib == null) + return; + + if (wh.isResourceLocked(ib) == true) { + boolean worked = false; + EnumBitSet bitSet = EnumBitSet.asEnumBitSet(wh.lockedResourceTypes.toLong(), ResourceType.class); + + bitSet.remove(ResourceType.resourceLookup.get(itemBaseID)); + + worked = DbManager.WarehouseQueries.updateLocks(wh, bitSet.toLong()); + + if (worked) { + wh.lockedResourceTypes.remove(Enum.ResourceType.resourceLookup.get(itemBaseID)); + ViewResourcesMessage vrm = new ViewResourcesMessage(player); + vrm.setGuild(player.getGuild()); + vrm.setWarehouseBuilding(warehouse); + vrm.configure(); + dispatch = Dispatch.borrow(player, vrm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + return; + } + + EnumBitSet bitSet = EnumBitSet.asEnumBitSet(wh.lockedResourceTypes.toLong(), ResourceType.class); + + bitSet.add(ResourceType.resourceLookup.get(itemBaseID)); + + if (DbManager.WarehouseQueries.updateLocks(wh,bitSet.toLong()) == false) + return; + + wh.lockedResourceTypes.add(Enum.ResourceType.resourceLookup.get(itemBaseID)); + ViewResourcesMessage vrm = new ViewResourcesMessage(player); + vrm.setGuild(player.getGuild()); + vrm.setWarehouseBuilding(warehouse); + vrm.configure(); + dispatch = Dispatch.borrow(player, vrm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + } + + public ConcurrentHashMap getResources() { + return resources; + } + + public int getUID() { + return UID; + } + + public void setUID(int uID) { + UID = uID; + } + + public synchronized boolean deposit(PlayerCharacter pc,Item resource, int amount,boolean removeFromInventory, boolean transaction){ + + ClientConnection origin = pc.getClientConnection(); + if (origin == null) + return false; + + if (amount < 0){ + Logger.info(pc.getFirstName() + " Attempting to Dupe!!!!!!"); + return false; + } + + ItemBase ib = resource.getItemBase(); + + if (ib == null) + return false; + + if (this.resources.get(ib) == null) + return false; + + CharacterItemManager itemMan = pc.getCharItemManager(); + + if (itemMan == null) + return false; + + + if (itemMan.getGoldTrading() > 0){ + ErrorPopupMsg.sendErrorPopup(pc, 195); + return false; + } + + + if (!itemMan.doesCharOwnThisItem(resource.getObjectUUID())) + return false; + + if (!resource.validForInventory(origin, pc, itemMan)) + return false; + + if (resource.getNumOfItems() < amount) + return false; + + int oldAmount = resources.get(ib); + + int newAmount = oldAmount + amount; + + if (newAmount > Warehouse.getMaxResources().get(ib.getUUID())){ + //ChatManager.chatSystemInfo(pc, "The Warehouse is at it's maximum for this type of resource."); + return false; + } + + + if (removeFromInventory){ + if (ib.getUUID() == 7){ + + if (itemMan.getGoldInventory().getNumOfItems() -amount < 0) + return false; + + if (itemMan.getGoldInventory().getNumOfItems() - amount > MBServerStatics.PLAYER_GOLD_LIMIT) + return false; + + if (!itemMan.modifyInventoryGold(-amount)){ + //ChatManager.chatSystemError(pc, "You do not have this Gold."); + return false; + } + + UpdateGoldMsg ugm = new UpdateGoldMsg(pc); + ugm.configure(); + Dispatch dispatch = Dispatch.borrow(pc, ugm); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + + itemMan.updateInventory(); + + }else{ + itemMan.delete(resource); + itemMan.updateInventory(); + } + } + itemMan.updateInventory(); + int itemID = ib.getUUID(); + boolean worked = false; + switch(itemID){ + case 7: + worked = DbManager.WarehouseQueries.updateGold(this, newAmount); + break; + case 1580000: + worked = DbManager.WarehouseQueries.updateStone(this, newAmount); + break; + case 1580001: + worked = DbManager.WarehouseQueries.updateTruesteel(this, newAmount); + break; + case 1580002: + worked = DbManager.WarehouseQueries.updateIron(this, newAmount); + break; + case 1580003: + worked = DbManager.WarehouseQueries.updateAdamant(this, newAmount); + break; + case 1580004: + worked = DbManager.WarehouseQueries.updateLumber(this, newAmount); + break; + case 1580005: + worked = DbManager.WarehouseQueries.updateOak(this, newAmount); + break; + case 1580006: + worked = DbManager.WarehouseQueries.updateBronzewood(this, newAmount); + break; + case 1580007: + worked = DbManager.WarehouseQueries.updateMandrake(this, newAmount); + break; + case 1580008: + worked = DbManager.WarehouseQueries.updateCoal(this, newAmount); + break; + case 1580009: + worked = DbManager.WarehouseQueries.updateAgate(this, newAmount); + break; + case 1580010: + worked = DbManager.WarehouseQueries.updateDiamond(this, newAmount); + break; + case 1580011: + worked = DbManager.WarehouseQueries.updateOnyx(this, newAmount); + break; + case 1580012: + worked = DbManager.WarehouseQueries.updateAzoth(this, newAmount); + break; + case 1580013: + worked = DbManager.WarehouseQueries.updateOrichalk(this, newAmount); + break; + case 1580014: + worked = DbManager.WarehouseQueries.updateAntimony(this, newAmount); + break; + case 1580015: + worked = DbManager.WarehouseQueries.updateSulfur(this, newAmount); + break; + case 1580016: + worked = DbManager.WarehouseQueries.updateQuicksilver(this, newAmount); + break; + case 1580017: + worked = DbManager.WarehouseQueries.updateGalvor(this, newAmount); + break; + case 1580018: + worked = DbManager.WarehouseQueries.updateWormwood(this, newAmount); + break; + case 1580019: + worked = DbManager.WarehouseQueries.updateObsidian(this, newAmount); + break; + case 1580020: + worked = DbManager.WarehouseQueries.updateBloodstone(this, newAmount); + break; + case 1580021: + worked = DbManager.WarehouseQueries.updateMithril(this, newAmount); + break; + } + + if (!worked) + return false; + + resources.put(ib,newAmount); + + Resource resourceType; + + if (resource.getItemBase().getType().equals(engine.Enum.ItemType.GOLD)) + resourceType = Resource.GOLD; + else + resourceType = Resource.valueOf(resource.getItemBase().getName().toUpperCase()); + + if (transaction) + this.AddTransactionToWarehouse(pc.getObjectType(), pc.getObjectUUID(), TransactionType.DEPOSIT,resourceType, amount); + + return true; + } + + //for mine deposit + public synchronized boolean depositFromMine(Mine mine,ItemBase resource, int amount){ + + if (resource == null) + return false; + + if (this.resources.get(resource) == null) + return false; + + int oldAmount = resources.get(resource); + int newAmount = oldAmount + amount; + + if (newAmount > Warehouse.getMaxResources().get(resource.getUUID())) + return false; + + int itemID = resource.getUUID(); + boolean worked = false; + + switch(itemID){ + case 7: + worked = DbManager.WarehouseQueries.updateGold(this, newAmount); + break; + case 1580000: + worked = DbManager.WarehouseQueries.updateStone(this, newAmount); + break; + case 1580001: + worked = DbManager.WarehouseQueries.updateTruesteel(this, newAmount); + break; + case 1580002: + worked = DbManager.WarehouseQueries.updateIron(this, newAmount); + break; + case 1580003: + worked = DbManager.WarehouseQueries.updateAdamant(this, newAmount); + break; + case 1580004: + worked = DbManager.WarehouseQueries.updateLumber(this, newAmount); + break; + case 1580005: + worked = DbManager.WarehouseQueries.updateOak(this, newAmount); + break; + case 1580006: + worked = DbManager.WarehouseQueries.updateBronzewood(this, newAmount); + break; + case 1580007: + worked = DbManager.WarehouseQueries.updateMandrake(this, newAmount); + break; + case 1580008: + worked = DbManager.WarehouseQueries.updateCoal(this, newAmount); + break; + case 1580009: + worked = DbManager.WarehouseQueries.updateAgate(this, newAmount); + break; + case 1580010: + worked = DbManager.WarehouseQueries.updateDiamond(this, newAmount); + break; + case 1580011: + worked = DbManager.WarehouseQueries.updateOnyx(this, newAmount); + break; + case 1580012: + worked = DbManager.WarehouseQueries.updateAzoth(this, newAmount); + break; + case 1580013: + worked = DbManager.WarehouseQueries.updateOrichalk(this, newAmount); + break; + case 1580014: + worked = DbManager.WarehouseQueries.updateAntimony(this, newAmount); + break; + case 1580015: + worked = DbManager.WarehouseQueries.updateSulfur(this, newAmount); + break; + case 1580016: + worked = DbManager.WarehouseQueries.updateQuicksilver(this, newAmount); + break; + case 1580017: + worked = DbManager.WarehouseQueries.updateGalvor(this, newAmount); + break; + case 1580018: + worked = DbManager.WarehouseQueries.updateWormwood(this, newAmount); + break; + case 1580019: + worked = DbManager.WarehouseQueries.updateObsidian(this, newAmount); + break; + case 1580020: + worked = DbManager.WarehouseQueries.updateBloodstone(this, newAmount); + break; + case 1580021: + worked = DbManager.WarehouseQueries.updateMithril(this, newAmount); + break; + } + if (!worked) + return false; + + this.resources.put(resource, newAmount); + Resource resourceType; + + if (resource.getUUID() == 7) + resourceType = Resource.GOLD; + else + resourceType = Resource.valueOf(resource.getName().toUpperCase()); + + if (mine != null) + this.AddTransactionToWarehouse(GameObjectType.Building, mine.getBuildingID(), TransactionType.MINE, resourceType, amount); + + return true; + } + + public synchronized boolean depositRealmTaxes(PlayerCharacter taxer, ItemBase ib,int amount){ + + if (ib == null) + return false; + + if (this.resources.get(ib) == null) + return false; + + int oldAmount = resources.get(ib); + int newAmount = oldAmount + amount; + + if (newAmount > Warehouse.getMaxResources().get(ib.getUUID())) + return false; + + int itemID = ib.getUUID(); + boolean worked = false; + + switch(itemID){ + case 7: + worked = DbManager.WarehouseQueries.updateGold(this, newAmount); + break; + case 1580000: + worked = DbManager.WarehouseQueries.updateStone(this, newAmount); + break; + case 1580001: + worked = DbManager.WarehouseQueries.updateTruesteel(this, newAmount); + break; + case 1580002: + worked = DbManager.WarehouseQueries.updateIron(this, newAmount); + break; + case 1580003: + worked = DbManager.WarehouseQueries.updateAdamant(this, newAmount); + break; + case 1580004: + worked = DbManager.WarehouseQueries.updateLumber(this, newAmount); + break; + case 1580005: + worked = DbManager.WarehouseQueries.updateOak(this, newAmount); + break; + case 1580006: + worked = DbManager.WarehouseQueries.updateBronzewood(this, newAmount); + break; + case 1580007: + worked = DbManager.WarehouseQueries.updateMandrake(this, newAmount); + break; + case 1580008: + worked = DbManager.WarehouseQueries.updateCoal(this, newAmount); + break; + case 1580009: + worked = DbManager.WarehouseQueries.updateAgate(this, newAmount); + break; + case 1580010: + worked = DbManager.WarehouseQueries.updateDiamond(this, newAmount); + break; + case 1580011: + worked = DbManager.WarehouseQueries.updateOnyx(this, newAmount); + break; + case 1580012: + worked = DbManager.WarehouseQueries.updateAzoth(this, newAmount); + break; + case 1580013: + worked = DbManager.WarehouseQueries.updateOrichalk(this, newAmount); + break; + case 1580014: + worked = DbManager.WarehouseQueries.updateAntimony(this, newAmount); + break; + case 1580015: + worked = DbManager.WarehouseQueries.updateSulfur(this, newAmount); + break; + case 1580016: + worked = DbManager.WarehouseQueries.updateQuicksilver(this, newAmount); + break; + case 1580017: + worked = DbManager.WarehouseQueries.updateGalvor(this, newAmount); + break; + case 1580018: + worked = DbManager.WarehouseQueries.updateWormwood(this, newAmount); + break; + case 1580019: + worked = DbManager.WarehouseQueries.updateObsidian(this, newAmount); + break; + case 1580020: + worked = DbManager.WarehouseQueries.updateBloodstone(this, newAmount); + break; + case 1580021: + worked = DbManager.WarehouseQueries.updateMithril(this, newAmount); + break; + } + + if (!worked) + return false; + + this.resources.put(ib, newAmount); + Resource resourceType; + + if (ib.getUUID() == 7) + resourceType = Resource.GOLD; + else + resourceType = Resource.valueOf(ib.getName().toUpperCase()); + + this.AddTransactionToWarehouse(taxer.getObjectType(), taxer.getObjectUUID(), TransactionType.TAXRESOURCEDEPOSIT, resourceType, amount); + + return true; + } + + public synchronized boolean depositProfitTax(ItemBase ib,int amount,Building building){ + + if (ib == null) + return false; + + if (this.resources.get(ib) == null) + return false; + + int oldAmount = resources.get(ib); + int newAmount = oldAmount + amount; + + if (newAmount > Warehouse.getMaxResources().get(ib.getUUID())) + return false; + + int itemID = ib.getUUID(); + boolean worked = false; + + switch(itemID){ + case 7: + worked = DbManager.WarehouseQueries.updateGold(this, newAmount); + break; + case 1580000: + worked = DbManager.WarehouseQueries.updateStone(this, newAmount); + break; + case 1580001: + worked = DbManager.WarehouseQueries.updateTruesteel(this, newAmount); + break; + case 1580002: + worked = DbManager.WarehouseQueries.updateIron(this, newAmount); + break; + case 1580003: + worked = DbManager.WarehouseQueries.updateAdamant(this, newAmount); + break; + case 1580004: + worked = DbManager.WarehouseQueries.updateLumber(this, newAmount); + break; + case 1580005: + worked = DbManager.WarehouseQueries.updateOak(this, newAmount); + break; + case 1580006: + worked = DbManager.WarehouseQueries.updateBronzewood(this, newAmount); + break; + case 1580007: + worked = DbManager.WarehouseQueries.updateMandrake(this, newAmount); + break; + case 1580008: + worked = DbManager.WarehouseQueries.updateCoal(this, newAmount); + break; + case 1580009: + worked = DbManager.WarehouseQueries.updateAgate(this, newAmount); + break; + case 1580010: + worked = DbManager.WarehouseQueries.updateDiamond(this, newAmount); + break; + case 1580011: + worked = DbManager.WarehouseQueries.updateOnyx(this, newAmount); + break; + case 1580012: + worked = DbManager.WarehouseQueries.updateAzoth(this, newAmount); + break; + case 1580013: + worked = DbManager.WarehouseQueries.updateOrichalk(this, newAmount); + break; + case 1580014: + worked = DbManager.WarehouseQueries.updateAntimony(this, newAmount); + break; + case 1580015: + worked = DbManager.WarehouseQueries.updateSulfur(this, newAmount); + break; + case 1580016: + worked = DbManager.WarehouseQueries.updateQuicksilver(this, newAmount); + break; + case 1580017: + worked = DbManager.WarehouseQueries.updateGalvor(this, newAmount); + break; + case 1580018: + worked = DbManager.WarehouseQueries.updateWormwood(this, newAmount); + break; + case 1580019: + worked = DbManager.WarehouseQueries.updateObsidian(this, newAmount); + break; + case 1580020: + worked = DbManager.WarehouseQueries.updateBloodstone(this, newAmount); + break; + case 1580021: + worked = DbManager.WarehouseQueries.updateMithril(this, newAmount); + break; + } + + if (!worked) + return false; + + this.resources.put(ib, newAmount); + Resource resourceType; + + if (ib.getUUID() == 7) + resourceType = Resource.GOLD; + else + resourceType = Resource.valueOf(ib.getName().toUpperCase()); + + if (building != null) + this.AddTransactionToWarehouse(GameObjectType.Building, building.getObjectUUID(), TransactionType.DEPOSIT, resourceType, amount); + + return true; + } + public synchronized boolean withdraw(PlayerCharacter pc, ItemBase ib, int amount, boolean addToInventory, boolean transaction){ + + if (pc == null) + return false; + + if (ib == null) + return false; + + if (this.resources.get(ib) == null) + return false; + + if (amount <= 0) + return false; + + CharacterItemManager itemMan = pc.getCharItemManager(); + + if(itemMan == null) + return false; + + if (addToInventory) + if(!itemMan.hasRoomInventory(ib.getWeight())) { + ChatManager.chatSystemInfo(pc, "You can not carry any more of that item."); + return false; + } + + if (addToInventory && ib.getUUID() == ItemBase.GOLD_BASE_ID){ + if (pc.getCharItemManager().getGoldInventory().getNumOfItems() + amount > MBServerStatics.PLAYER_GOLD_LIMIT){ + return false; + } + + if (pc.getCharItemManager().getGoldInventory().getNumOfItems() + amount < 0) + return false; + } + int oldAmount = this.resources.get(ib); + + if (oldAmount < amount) + return false; + + int hashID = ib.getHashID(); + int newAmount = oldAmount - amount; + + boolean worked = false; + + switch(hashID){ + case 2308551: + worked = DbManager.WarehouseQueries.updateGold(this,newAmount); + break; + case 74856115: + worked = DbManager.WarehouseQueries.updateStone(this, newAmount); + break; + case -317484979: + worked = DbManager.WarehouseQueries.updateTruesteel(this, newAmount); + break; + case 2504297: + worked = DbManager.WarehouseQueries.updateIron(this, newAmount); + break; + case -1741189964: + worked = DbManager.WarehouseQueries.updateAdamant(this, newAmount); + break; + case -1603256692: + worked = DbManager.WarehouseQueries.updateLumber(this, newAmount); + break; + case 74767: + worked = DbManager.WarehouseQueries.updateOak(this, newAmount); + break; + case 1334770447: + worked = DbManager.WarehouseQueries.updateBronzewood(this, newAmount); + break; + case 1191391799: + worked = DbManager.WarehouseQueries.updateMandrake(this, newAmount); + break; + case 2559427: + worked = DbManager.WarehouseQueries.updateCoal(this, newAmount); + break; + case 75173057: + worked = DbManager.WarehouseQueries.updateAgate(this, newAmount); + break; + case -1730704107: + worked = DbManager.WarehouseQueries.updateDiamond(this, newAmount); + break; + case 2977263: + worked = DbManager.WarehouseQueries.updateOnyx(this, newAmount); + break; + case 78329697: + worked = DbManager.WarehouseQueries.updateAzoth(this, newAmount); + break; + case -2036290524: + worked = DbManager.WarehouseQueries.updateOrichalk(this, newAmount); + break; + case 452320058: + worked = DbManager.WarehouseQueries.updateAntimony(this, newAmount); + break; + case -1586349421: + worked = DbManager.WarehouseQueries.updateSulfur(this, newAmount); + break; + case -472884509: + worked = DbManager.WarehouseQueries.updateQuicksilver(this, newAmount); + break; + case -1596311545: + worked = DbManager.WarehouseQueries.updateGalvor(this, newAmount); + break; + case 1532478436: + worked = DbManager.WarehouseQueries.updateWormwood(this, newAmount); + break; + case -697973233: + worked = DbManager.WarehouseQueries.updateObsidian(this, newAmount); + break; + case -1569826353: + worked = DbManager.WarehouseQueries.updateBloodstone(this, newAmount); + break; + case -1761257186: + worked = DbManager.WarehouseQueries.updateMithril(this, newAmount); + break; + } + if (!worked) + return false; + + this.resources.put(ib, newAmount); + + if (addToInventory){ + if (ib.getUUID() == 7){ + + itemMan.addGoldToInventory(amount, false); + UpdateGoldMsg ugm = new UpdateGoldMsg(pc); + ugm.configure(); + Dispatch dispatch = Dispatch.borrow(pc, ugm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + itemMan.updateInventory(); + }else{ + boolean itemWorked = false; + Item item = new Item( ib, pc.getObjectUUID(), OwnerType.PlayerCharacter, (byte) 0, (byte) 0, + (short) 1, (short) 1, true, false,ItemContainerType.INVENTORY, (byte) 0, + new ArrayList<>(),""); + item.setNumOfItems(amount); + item.containerType = Enum.ItemContainerType.INVENTORY; + + try { + item = DbManager.ItemQueries.ADD_ITEM(item); + itemWorked = true; + } catch (Exception e) { + Logger.error(e); + } + if (itemWorked) { + itemMan.addItemToInventory(item); + itemMan.updateInventory(); + } + } + } + Resource resourceType; + + if (ib.getUUID() == 7) + resourceType = Resource.GOLD; + else + resourceType = Resource.valueOf(ib.getName().toUpperCase()); + + if (transaction) + this.AddTransactionToWarehouse(pc.getObjectType(), pc.getObjectUUID(), TransactionType.WITHDRAWL, resourceType, amount); + + return true; + } + + public synchronized boolean withdraw(NPC npc, ItemBase ib, int amount, boolean addToInventory, boolean transaction){ + + if (npc == null) + return false; + + if (ib == null) + return false; + + if (this.resources.get(ib) == null) + return false; + + if (amount <= 0) + return false; + + int oldAmount = this.resources.get(ib); + + if (oldAmount < amount) + return false; + + int hashID = ib.getHashID(); + int newAmount = oldAmount - amount; + boolean worked = false; + + switch(hashID){ + case 2308551: + worked = DbManager.WarehouseQueries.updateGold(this,newAmount); + break; + case 74856115: + worked = DbManager.WarehouseQueries.updateStone(this, newAmount); + break; + case -317484979: + worked = DbManager.WarehouseQueries.updateTruesteel(this, newAmount); + break; + case 2504297: + worked = DbManager.WarehouseQueries.updateIron(this, newAmount); + break; + case -1741189964: + worked = DbManager.WarehouseQueries.updateAdamant(this, newAmount); + break; + case -1603256692: + worked = DbManager.WarehouseQueries.updateLumber(this, newAmount); + break; + case 74767: + worked = DbManager.WarehouseQueries.updateOak(this, newAmount); + break; + case 1334770447: + worked = DbManager.WarehouseQueries.updateBronzewood(this, newAmount); + break; + case 1191391799: + worked = DbManager.WarehouseQueries.updateMandrake(this, newAmount); + break; + case 2559427: + worked = DbManager.WarehouseQueries.updateCoal(this, newAmount); + break; + case 75173057: + worked = DbManager.WarehouseQueries.updateAgate(this, newAmount); + break; + case -1730704107: + worked = DbManager.WarehouseQueries.updateDiamond(this, newAmount); + break; + case 2977263: + worked = DbManager.WarehouseQueries.updateOnyx(this, newAmount); + break; + case 78329697: + worked = DbManager.WarehouseQueries.updateAzoth(this, newAmount); + break; + case -2036290524: + worked = DbManager.WarehouseQueries.updateOrichalk(this, newAmount); + break; + case 452320058: + worked = DbManager.WarehouseQueries.updateAntimony(this, newAmount); + break; + case -1586349421: + worked = DbManager.WarehouseQueries.updateSulfur(this, newAmount); + break; + case -472884509: + worked = DbManager.WarehouseQueries.updateQuicksilver(this, newAmount); + break; + case -1596311545: + worked = DbManager.WarehouseQueries.updateGalvor(this, newAmount); + break; + case 1532478436: + worked = DbManager.WarehouseQueries.updateWormwood(this, newAmount); + break; + case -697973233: + worked = DbManager.WarehouseQueries.updateObsidian(this, newAmount); + break; + case -1569826353: + worked = DbManager.WarehouseQueries.updateBloodstone(this, newAmount); + break; + case -1761257186: + worked = DbManager.WarehouseQueries.updateMithril(this, newAmount); + break; + } + + if (!worked) + return false; + + this.resources.put(ib, newAmount); + Resource resourceType; + + if (ib.getUUID() == 7) + resourceType = Resource.GOLD; + else + resourceType = Resource.valueOf(ib.getName().toUpperCase()); + + if (transaction) + this.AddTransactionToWarehouse(npc.getObjectType(), npc.getObjectUUID(), TransactionType.WITHDRAWL, resourceType, amount); + + return true; + } + + public synchronized boolean transferResources(PlayerCharacter taxer, TaxResourcesMsg msg, ArrayList realmResources, float taxPercent, Warehouse toWarehouse){ + + for (int ibID: realmResources){ + + ItemBase ib = ItemBase.getItemBase(ibID); + + if (ib == null) + return false; + + if (this.resources.get(ib) == null) + return false; + + int amount = (int) (this.resources.get(ib) * taxPercent); + + if (amount <= 0){ + msg.getResources().put(ib.getHashID(), 0); + continue; + } + + int oldAmount = this.resources.get(ib); + + if (oldAmount < amount) + amount = oldAmount; + + int hashID = ib.getHashID(); + int newAmount = oldAmount - amount; + + if (newAmount < amount) + continue; + + boolean worked = false; + + switch(hashID){ + case 2308551: + worked = DbManager.WarehouseQueries.updateGold(this,newAmount); + break; + case 74856115: + worked = DbManager.WarehouseQueries.updateStone(this, newAmount); + break; + case -317484979: + worked = DbManager.WarehouseQueries.updateTruesteel(this, newAmount); + break; + case 2504297: + worked = DbManager.WarehouseQueries.updateIron(this, newAmount); + break; + case -1741189964: + worked = DbManager.WarehouseQueries.updateAdamant(this, newAmount); + break; + case -1603256692: + worked = DbManager.WarehouseQueries.updateLumber(this, newAmount); + break; + case 74767: + worked = DbManager.WarehouseQueries.updateOak(this, newAmount); + break; + case 1334770447: + worked = DbManager.WarehouseQueries.updateBronzewood(this, newAmount); + break; + case 1191391799: + worked = DbManager.WarehouseQueries.updateMandrake(this, newAmount); + break; + case 2559427: + worked = DbManager.WarehouseQueries.updateCoal(this, newAmount); + break; + case 75173057: + worked = DbManager.WarehouseQueries.updateAgate(this, newAmount); + break; + case -1730704107: + worked = DbManager.WarehouseQueries.updateDiamond(this, newAmount); + break; + case 2977263: + worked = DbManager.WarehouseQueries.updateOnyx(this, newAmount); + break; + case 78329697: + worked = DbManager.WarehouseQueries.updateAzoth(this, newAmount); + break; + case -2036290524: + worked = DbManager.WarehouseQueries.updateOrichalk(this, newAmount); + break; + case 452320058: + worked = DbManager.WarehouseQueries.updateAntimony(this, newAmount); + break; + case -1586349421: + worked = DbManager.WarehouseQueries.updateSulfur(this, newAmount); + break; + case -472884509: + worked = DbManager.WarehouseQueries.updateQuicksilver(this, newAmount); + break; + case -1596311545: + worked = DbManager.WarehouseQueries.updateGalvor(this, newAmount); + break; + case 1532478436: + worked = DbManager.WarehouseQueries.updateWormwood(this, newAmount); + break; + case -697973233: + worked = DbManager.WarehouseQueries.updateObsidian(this, newAmount); + break; + case -1569826353: + worked = DbManager.WarehouseQueries.updateBloodstone(this, newAmount); + break; + case -1761257186: + worked = DbManager.WarehouseQueries.updateMithril(this, newAmount); + break; + } + + if (!worked){ + msg.getResources().put(ib.getHashID(), 0); + continue; + } + + msg.getResources().put(ib.getHashID(), amount); + + this.resources.put(ib, newAmount); + toWarehouse.depositRealmTaxes(taxer,ib, amount); + Resource resourceType; + + if (ib.getUUID() == 7) + resourceType = Resource.GOLD; + else + resourceType = Resource.valueOf(ib.getName().toUpperCase()); + + this.AddTransactionToWarehouse(taxer.getObjectType(), taxer.getObjectUUID(), TransactionType.TAXRESOURCE, resourceType, amount); + + } + return true; + } + + public synchronized boolean loot(PlayerCharacter pc, ItemBase ib, int amount, boolean addToInventory){ + + if (pc == null) + return false; + + if (ib == null) + return false; + + if (this.resources.get(ib) == null) + return false; + + if (amount <= 0) + return false; + + CharacterItemManager itemMan = pc.getCharItemManager(); + + if(itemMan == null) + return false; + + if(!itemMan.hasRoomInventory(ib.getWeight())) { + ChatManager.chatSystemInfo(pc, "You can not carry any more of that item."); + return false; + } + + int oldAmount = this.resources.get(ib); + + if (oldAmount < amount) + return false; + + int newAmount = oldAmount - amount; + + this.resources.put(ib, newAmount); + + if (addToInventory){ + if (ib.getUUID() == 7){ + + itemMan.addGoldToInventory(amount, false); + UpdateGoldMsg ugm = new UpdateGoldMsg(pc); + ugm.configure(); + Dispatch dispatch = Dispatch.borrow(pc, ugm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + itemMan.updateInventory(); + }else{ + boolean itemWorked = false; + Item item = new Item(ib, pc.getObjectUUID(), OwnerType.PlayerCharacter, (byte) 0, (byte) 0, + (short) 1, (short) 1, true, false,ItemContainerType.INVENTORY, (byte) 0, + new ArrayList<>(),""); + item.setNumOfItems(amount); + item.containerType = Enum.ItemContainerType.INVENTORY; + + try { + item = DbManager.ItemQueries.ADD_ITEM(item); + itemWorked = true; + } catch (Exception e) { + Logger.error(e); + } + if (itemWorked) { + itemMan.addItemToInventory(item); + itemMan.updateInventory(); + } + } + } + + return true; + } + + @Override + public void updateDatabase() { + // TODO Auto-generated method stub + + } + @Override + public void runAfterLoad() { + + try{ + Building warehouseBuilding = BuildingManager.getBuilding(this.buildingUID); + Logger.info("configuring warehouse " + UID + " for city " + warehouseBuilding.getCity().getCityName() + " structure UUID " + this.buildingUID); + + //Building is gone, but Warehouse still in DB?? Should never happen, sanity check anyway. + if (warehouseBuilding == null){ + Logger.error( "Failed to load Building for Warehouse"); + return; + } + + Zone cityZone = warehouseBuilding.getParentZone(); + + if (cityZone == null){ + Logger.error( "Failed to load Zone for Warehouse with UUID " + this.getObjectUUID()); + return; + } + + City city = City.getCity(cityZone.getPlayerCityUUID()); + + if (city == null){ + Logger.error( "Failed to load City for Warehouse with UUID " + this.getObjectUUID()); + return; + } + + warehouseByBuildingUUID.put(this.buildingUID, this); + city.setWarehouseBuildingID(this.buildingUID); + }catch(Exception E){ + Logger.info(this.getObjectUUID() + " failed"); + + } + } + + public boolean isEmpty(){ + int amount = 0; + for(ItemBase ib: ItemBase.getResourceList()){ + if (amount > 0) + return false; + amount += resources.get(ib); + } + return true; + } + + public int getBuildingUID() { + return buildingUID; + } + + public void loadAllTransactions(){ + this.transactions = DbManager.WarehouseQueries.GET_TRANSACTIONS_FOR_WAREHOUSE(this.buildingUID); + } + + public boolean AddTransactionToWarehouse(GameObjectType targetType, int targetUUID, TransactionType transactionType,Resource resource, int amount){ + + + if (!DbManager.WarehouseQueries.CREATE_TRANSACTION(this.buildingUID, targetType, targetUUID, transactionType, resource, amount, DateTime.now())) + return false; + + Transaction transaction = new Transaction(this.buildingUID,targetType,targetUUID,transactionType,resource,amount, DateTime.now()); + this.transactions.add(transaction); + return true; + } + + public ArrayList getTransactions() { + return transactions; + } + + public boolean isAboveCap(ItemBase ib, int deposit){ + int newAmount = this.resources.get(ib) + deposit; + return newAmount > Warehouse.getMaxResources().get(ib.getUUID()); + + } + + public boolean isResourceLocked(ItemBase itemBase) { + + Enum.ResourceType resourceType; + + resourceType = Enum.ResourceType.resourceLookup.get(itemBase.getUUID()); + + return resourceType.elementOf(this.lockedResourceTypes); + } +} diff --git a/src/engine/objects/Zone.java b/src/engine/objects/Zone.java new file mode 100644 index 00000000..eb9e5792 --- /dev/null +++ b/src/engine/objects/Zone.java @@ -0,0 +1,512 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.objects; + +import engine.Enum; +import engine.InterestManagement.HeightMap; +import engine.db.archive.DataWarehouse; +import engine.gameManager.DbManager; +import engine.gameManager.ZoneManager; +import engine.math.Bounds; +import engine.math.Vector2f; +import engine.math.Vector3fImmutable; +import engine.net.ByteBufferWriter; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public class Zone extends AbstractGameObject { + + private final int playerCityID; + private final String zoneName; + private final float xCoord; + private final float zCoord; + private final float yCoord; + public float absX = 0.0f; + public float absY = 0.0f; + public float absZ = 0.0f; + private final int loadNum; + private final byte safeZone; + private final String Icon1; + private final String Icon2; + private final String Icon3; + private ArrayList nodes = null; + private int parentZoneID; + private Zone parent = null; + private Bounds bounds; + private boolean isNPCCity = false; + private boolean isPlayerCity = false; + private String hash; + private int minLvl; + private int maxLvl; + + private float worldAltitude = 0; + + private float seaLevel = 0; + public final Set zoneBuildingSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); + public final Set zoneNPCSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); + public final Set zoneMobSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); + + /** + * ResultSet Constructor + */ + public Zone(ResultSet rs) throws SQLException { + super(rs); + this.parentZoneID = rs.getInt("parent"); + this.playerCityID = rs.getInt("isPlayerCity"); + this.isPlayerCity = this.playerCityID != 0; + this.zoneName = rs.getString("Name"); + this.xCoord = rs.getFloat("XCoord"); + this.zCoord = rs.getFloat("ZCoord"); + this.yCoord = rs.getFloat("YOffset"); + this.loadNum = rs.getInt("LoadNum"); + this.safeZone = rs.getByte("SafeZone"); + this.Icon1 = rs.getString("Icon1"); + this.Icon2 = rs.getString("Icon2"); + this.Icon3 = rs.getString("Icon3"); + this.hash = rs.getString("hash"); + + this.minLvl = rs.getInt("minLvl"); + this.maxLvl = rs.getInt("maxLvl"); + + //this needs to be here specifically for new zones created after server boot (e.g. player city zones) + Zone parentZone = ZoneManager.getZoneByUUID(parentZoneID); + + this.setParent(parentZone); + + if (this.minLvl == 0 && parentZone != null){ + this.minLvl = parentZone.minLvl; + this.maxLvl = parentZone.maxLvl; + } + + if (parentZone != null) + parentZone.addNode(this); + + // If zone doesn't yet hava a hash then write it back to the zone table + + if (hash == null) + setHash(); + + + } + + /* Method sets a default value for player cities + * otherwise using values derived from the loadnum + * field in the obj_zone database table. + */ + public void setBounds() { + + float halfExtentX; + float halfExtentY; + + // Set initial bounds object + + this.bounds = Bounds.borrow(); + + // Player cities are assigned default value + + if (this.loadNum == 0) { + bounds.setBounds(new Vector2f(this.absX, this.absZ), new Vector2f(Enum.CityBoundsType.ZONE.extents, Enum.CityBoundsType.ZONE.extents), 0.0f); + return; + } + + // All other zones have bounding boxes loaded from database + ResultSet rs = DbManager.ZoneQueries.GET_ZONE_EXTENTS(this.loadNum); + boolean loaded = false; + + if (rs != null) + try { + if (rs.next()) { + halfExtentX = rs.getFloat("xRadius"); + halfExtentY = rs.getFloat("zRadius"); + this.bounds.setBounds(new Vector2f(this.absX, this.absZ), new Vector2f(halfExtentX, halfExtentY), 0.0f); + loaded = true; + } + + } catch (SQLException e) { + Logger.error("SQLException: " + e.getMessage()); + } + + if (!loaded) { + + // Default to Citygrid size on error + + bounds.setBounds(new Vector2f(this.absX, this.absZ), new Vector2f(Enum.CityBoundsType.ZONE.extents, Enum.CityBoundsType.ZONE.extents), 0.0f); + } + + } + + /* + * Getters + */ + public int getPlayerCityUUID() { + if (this.playerCityID == 0) + return 0; + return this.playerCityID; + } + + public String getName() { + return zoneName; + } + + public float getXCoord() { + return xCoord; + } + + public float getYCoord() { + return yCoord; + } + + public float getZCoord() { + return zCoord; + } + + public int getLoadNum() { + return loadNum; + } + + public int getLoadNumClient() { + return loadNum; + } + + public byte getSafeZone() { + return safeZone; + } + + public String getIcon1() { + return Icon1; + } + + public String getIcon2() { + return Icon2; + } + + public String getIcon3() { + return Icon3; + } + + public void setParent(final Zone value) { + + this.parent = value; + this.parentZoneID = (this.parent != null) ? this.parent.getObjectUUID() : 0; + + if (this.parent != null) { + this.absX = this.xCoord + parent.absX; + this.absY = this.yCoord + parent.absY; + this.absZ = this.zCoord + parent.absZ; + + if (this.minLvl == 0 || this.maxLvl == 0){ + this.minLvl = this.parent.minLvl; + this.maxLvl = this.parent.maxLvl; + } + } else { //only the Sea Floor zone does not have a parent + this.absX = this.xCoord; + this.absY = MBServerStatics.SEA_FLOOR_ALTITUDE; + this.absZ = this.zCoord; + } + + // Zone AABB is set here as it's coordinate space is world requiring a parent. + this.setBounds(); + + if (this.getHeightMap() != null && this.getHeightMap().getSeaLevel() != 0) + this.seaLevel = this.getHeightMap().getSeaLevel(); + + } + + public void generateWorldAltitude(){ + + if (ZoneManager.getSeaFloor().getObjectUUID() == this.getObjectUUID()){ + this.worldAltitude = MBServerStatics.SEA_FLOOR_ALTITUDE; + return; + } + + Zone parentZone = this.parent; + + Zone currentZone = this; + float altitude = this.absY; + + //seafloor only zone with null parent; + + while(parentZone != ZoneManager.getSeaFloor()){ + + if(parentZone.getHeightMap() != null){ + + Vector2f zoneLoc = ZoneManager.worldToZoneSpace(currentZone.getLoc(), parentZone); + altitude += parentZone.getHeightMap().getInterpolatedTerrainHeight(zoneLoc); + + } + currentZone = parentZone; + parentZone = parentZone.parent; + + } + + this.worldAltitude = altitude; + + if (ZoneManager.getSeaFloor().equals(this)) + this.seaLevel = 0; + else if + (this.getHeightMap() != null && this.getHeightMap().getSeaLevel() == 0){ + this.seaLevel = this.parent.seaLevel; + + }else if (this.getHeightMap() != null){ + this.seaLevel = this.worldAltitude + this.getHeightMap().getSeaLevel(); + }else { + this.seaLevel = this.parent.seaLevel; + } + + } + + public Zone getParent() { + return this.parent; + } + + public float getAbsX() { + return this.absX; + } + + public float getAbsY() { + return this.absY; + } + + public float getAbsZ() { + return this.absZ; + } + + public boolean isMacroZone() { + + // Player cities are not considered a macrozone + // although their parent is always a continent. + + if (this.isPlayerCity == true) + return false; + + if (this.parent == null) + return false; + + return (this.parent.isContininent() == true); + } + + public boolean isNPCCity() { + return this.isNPCCity; + } + + public boolean isPlayerCity() { + return this.isPlayerCity; + } + + public void setNPCCity(boolean value) { + this.isNPCCity = value; + } + + public void setPlayerCity(boolean value) { + this.isPlayerCity = value; + } + + public Vector3fImmutable getLoc() { + return new Vector3fImmutable(this.absX, this.absY, this.absZ); + } + + public int getParentZoneID() { + return this.parentZoneID; + } + + public ArrayList getNodes() { + if (this.nodes == null) { + this.nodes = DbManager.ZoneQueries.GET_MAP_NODES(super.getObjectUUID()); + + //Add reverse lookup for child->parent + if (this.nodes != null) + for (Zone zone : this.nodes) { + zone.setParent(this); + } + } + + return nodes; + } + + public void addNode(Zone child) { + this.nodes.add(child); + } + + public void removeNode(Zone child) { + this.nodes.remove(child); + } + + /* + * Serializing + */ + + + public static void serializeForClientMsg(Zone zone,ByteBufferWriter writer) { + + if (zone.loadNum == 0 && zone.playerCityID == 0) + Logger.warn( "Warning! WorldServerMap with ID " + zone.getObjectUUID() + " has a loadnum of 0 (player city) and no city linked. This will probably crash the client!"); + + // Player City Terraform values serialized here. + + if (zone.playerCityID > 0) { + writer.put((byte) 1); // Player City - True + writer.putFloat(Enum.CityBoundsType.TERRAFORM.extents); + writer.putFloat(Enum.CityBoundsType.TERRAFORM.extents); + } else + writer.put((byte) 0); // Player City - False + + writer.putFloat(zone.xCoord); + writer.putFloat(zone.zCoord); + writer.putFloat(zone.yCoord); + + writer.putInt(0); + writer.putInt(0); + writer.putInt(zone.loadNum); + + if (zone.playerCityID > 0) { + City k = City.getCity(zone.playerCityID); + + if (k != null) { + writer.putInt(k.getObjectType().ordinal()); + writer.putInt(k.getObjectUUID()); + } + else + writer.putLong(0x0); + } else { + writer.putInt(zone.getObjectType().ordinal()); + writer.putInt(zone.getObjectUUID()); + } + writer.putInt(zone.nodes.size()); + + City city = City.getCity(zone.playerCityID); + + if (city != null) + writer.putString(city.getCityName()); + else + writer.putString(zone.zoneName); + writer.put(zone.safeZone); + writer.putString(zone.Icon1); + writer.putString(zone.Icon2); + writer.putString(zone.Icon3); + writer.put((byte) 0); // Pad + + for (Zone child : zone.nodes) { + Zone.serializeForClientMsg(child,writer); + } + } + + @Override + public void updateDatabase() { + // TODO Auto-generated method stub + } + + public Zone findRuinedCityZone(float centerX, float centerY, float centerZ){ + Bounds cityBounds; + cityBounds = Bounds.borrow(); + Zone RuinedZone = null; + cityBounds.setBounds(new Vector2f(centerX, centerZ), new Vector2f(Enum.CityBoundsType.ZONE.extents, Enum.CityBoundsType.ZONE.extents), 0.0f); + Zone currentZone = ZoneManager.findSmallestZone(new Vector3fImmutable(centerX, centerY, centerZ)); + if (currentZone != null) + if (this.getObjectUUID() == currentZone.getObjectUUID()){ + + if (currentZone.getPlayerCityUUID() != 0){ + //null player city? skip.. + if (City.GetCityFromCache(currentZone.getPlayerCityUUID()) == null) + RuinedZone = null; + else //no tol? skip... + if (City.GetCityFromCache(currentZone.getPlayerCityUUID()).getTOL() == null) + RuinedZone = null; + else + if (City.GetCityFromCache(currentZone.getPlayerCityUUID()).getTOL().getRank() == -1) + RuinedZone = currentZone; + //Dead tree? skip. + cityBounds.release(); + return RuinedZone; + } + } + + for (Zone zone : this.getNodes()) { + + if (zone == this) + continue; + + if (zone.isContininent() && zone.getPlayerCityUUID() == 0) + continue; + + if (zone.getPlayerCityUUID() != 0){ + //null player city? skip.. + if (City.GetCityFromCache(zone.getPlayerCityUUID()) == null) + continue; + //no tol? skip... + if (City.GetCityFromCache(zone.getPlayerCityUUID()).getTOL() == null) + continue; + + //Dead tree? skip. + if (Bounds.collide(zone.bounds, cityBounds, 0.0f)){ + if (City.GetCityFromCache(zone.getPlayerCityUUID()).getTOL().getRank() == -1){ + RuinedZone = zone; + break; + } + } + } + } + cityBounds.release(); + return RuinedZone; + } + + public boolean isContininent() { + + if (this.parent == null) + return false; + + return this.parent.equals(ZoneManager.getSeaFloor()); + } + + /** + * @return the bounds + */ + public Bounds getBounds() { + return bounds; + } + + public String getHash() { + return hash; + } + + public void setHash() { + + this.hash = DataWarehouse.hasher.encrypt(this.getObjectUUID()); + + // Write hash to player character table + + DataWarehouse.writeHash(Enum.DataRecordType.ZONE, this.getObjectUUID()); + } + + // Return heightmap for this Zone. + + public HeightMap getHeightMap() { + + if (this.isPlayerCity) + return HeightMap.PlayerCityHeightMap; + + return HeightMap.heightmapByLoadNum.get(this.loadNum); + } + + public float getSeaLevel() { + return seaLevel; + } + + public float getWorldAltitude() { + return worldAltitude; + } + +} diff --git a/src/engine/pooling/ByteBufferPool.java b/src/engine/pooling/ByteBufferPool.java new file mode 100644 index 00000000..ef6459cf --- /dev/null +++ b/src/engine/pooling/ByteBufferPool.java @@ -0,0 +1,69 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.pooling; + +import org.pmw.tinylog.Logger; + +import java.nio.ByteBuffer; + +public class ByteBufferPool extends LinkedObjectPool { + + private final int defaultBufferSize; + + public ByteBufferPool(int defaultBufferSize) { + super(ObjectPool.DEFAULT_SIZE); + this.defaultBufferSize = defaultBufferSize; + } + + public ByteBufferPool(int size, int defaultBufferSize) { + super(size); + this.defaultBufferSize = defaultBufferSize; + } + + @Override + protected ByteBuffer makeNewObject() { + return ByteBuffer.allocate(defaultBufferSize); + } + + @Override + protected void resetObject(ByteBuffer obj) { + obj.clear(); + } + + @Override + public ByteBuffer get() { + // Logger.debug("ByteBufferPool.get() BB.capacity(): " + bb.capacity() + // + ", bb.pos(): " + bb.position() + ". Pool.size() is now: " + // + this.getPoolSize()); + return super.get(); + } + + @Override + public void put(ByteBuffer bb) { + if(bb.isDirect()) + super.put(bb); + // Logger.debug("ByteBufferPool.put() BB.capacity(): " + bb.capacity() + // + ", bb.pos(): " + bb.position() + ". Pool.size() is now: " + // + this.getPoolSize()); + } + + @Override + protected void handlePoolExhaustion() { + Logger.debug("ByteBufferPool(" + defaultBufferSize + + ") exhausted, making more objects."); + + // If none exist, make (and pool) a few + // Dont sync the loop, let the makeNewObject() + // call return before locking the pool object + for (int i = 0; i < 5; ++i) + this.makeAndAdd(); + } + +} diff --git a/src/engine/pooling/ConnectionPool.java b/src/engine/pooling/ConnectionPool.java new file mode 100644 index 00000000..cb8e240e --- /dev/null +++ b/src/engine/pooling/ConnectionPool.java @@ -0,0 +1,55 @@ +/* + * Copyright 2013 MagicBane Emulator Project + * All Rights Reserved + */ +package engine.pooling; + +import engine.gameManager.ConfigManager; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public class ConnectionPool extends LinkedObjectPool { + + static { + //Register the Driver + try { + Class.forName("com.mysql.cj.jdbc.Driver").newInstance(); + } catch (InstantiationException | ClassNotFoundException | IllegalAccessException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + public ConnectionPool() { + super(10); + } + + @Override + protected Connection makeNewObject() { + // Protocol + String sqlURI = "jdbc:mysql://"; + sqlURI += ConfigManager.MB_DATABASE_ADDRESS.getValue() + ':' + ConfigManager.MB_DATABASE_PORT.getValue(); + sqlURI += '/' + ConfigManager.MB_DATABASE_NAME.getValue() + '?'; + sqlURI += "useServerPrepStmts=true"; + sqlURI += "&cachePrepStmts=false"; + sqlURI += "&cacheCallableStmts=true"; + sqlURI += "&characterEncoding=utf8"; + + Connection out = null; + try { + out = DriverManager.getConnection(sqlURI, ConfigManager.MB_DATABASE_USER.getValue(), + ConfigManager.MB_DATABASE_PASS.getValue()); + } catch (SQLException e) { + e.printStackTrace(); + } + + return out; + } + + @Override + protected void resetObject(Connection obj) { + + } +} diff --git a/src/engine/pooling/LinkedObjectPool.java b/src/engine/pooling/LinkedObjectPool.java new file mode 100644 index 00000000..34828c5c --- /dev/null +++ b/src/engine/pooling/LinkedObjectPool.java @@ -0,0 +1,142 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.pooling; + + +import org.pmw.tinylog.Logger; + +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicInteger; + +public abstract class LinkedObjectPool { + private final LinkedBlockingQueue pool; + private final AtomicInteger poolSize; + + /** + * Constructor that allows the caller to specify initial array size and + * percent of prefill. + * + * @param size + * - initial size for the containing array (pool) + */ + public LinkedObjectPool(int size) { + this.pool = new LinkedBlockingQueue<>(); + this.poolSize = new AtomicInteger(); + } + + /** + * Default Constructor that uses default initial Array size and percent + * prefill values + */ + public LinkedObjectPool() { + this(0); + } + + /** + * Forces pool to add numberOfObjects to the pool. This may cause + * internal ArrayList to resize. + * + * @param numberOfObjects + */ + public void fill(int numberOfObjects) { + for (int i = 0; i < numberOfObjects; ++i) + this.makeAndAdd(); + } + + /** + * Forces subclasses to implement factory routine for object creation. + */ + protected abstract T makeNewObject(); + + /** + * Forces subclasses to implement a way to reset object to a reusable state. + */ + protected abstract void resetObject(T obj); + + /** + * Generic Get routine. If the pool is empty, then one (or more) of T type + * objects will be created. If more than one T is created, then they will be + * put into the pool. One T will always be created and returned. + */ + public T get() { + T obj = pool.poll(); + + if(obj == null) { + //Oops pool is empty.. make a new obj + obj = this.makeNewObject(); + } else { + poolSize.decrementAndGet(); + } + + return obj; + } + + /** + * Generic put routine. If the current pool size is below threshold, this + * object will be pooled, otherwise it will be NULL'ed and scheduled for + * Garbage Collection + * + * @param obj + */ + public void put(T obj) { + //Logger.debug("Objectpool.put(). Pool size: " + pool.size()); + this.resetObject(obj); + this.poolSize.incrementAndGet(); + this.pool.add(obj); + } + + /** + * Helper method. Attempts to create and add a new T object. If this + * fails, an error is logged. + */ + protected final void makeAndAdd() { + T obj = this.makeNewObject(); + if (obj == null) { + Logger.error("Pooling failure: Object creation failed."); + } else { + this.put(obj); + } + } + + /** + * + * @return the current size of the pool, not the maximum capacity of the + * Array holding the pool. + */ + public final int getPoolSize() { + return this.poolSize.get(); + } + + /** + * Culls the pool and removes half of the stored objects. Removed objects + * are NULL'ed and scheduled form Garbage Collection. + * + * @return the number of Objects removed from the pool. + */ + public int cullHalf() { + int full, half; + full = this.getPoolSize(); + if (full < 1) { + return full; + } + + half = (full / 2); + + for (int i = 0; i < (full / 2); ++i) { + T obj = this.pool.poll(); + obj = null; // Null out for GC + } + return half; + } + + protected void handlePoolExhaustion() { + //Not needed in this implementation + } +} diff --git a/src/engine/pooling/MultisizeByteBufferPool.java b/src/engine/pooling/MultisizeByteBufferPool.java new file mode 100644 index 00000000..3196c517 --- /dev/null +++ b/src/engine/pooling/MultisizeByteBufferPool.java @@ -0,0 +1,190 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.pooling; + +import org.pmw.tinylog.Logger; + +import java.nio.ByteBuffer; +import java.util.HashMap; + +public class MultisizeByteBufferPool { + + /** + * Maps a power of two (0-30) to a BB Pool + */ + private final HashMap powerToPoolMap = new HashMap<>(); + + public MultisizeByteBufferPool() { + super(); + } + + /** + * Gets a ByteBuffer that is of the size 2^powerOfTwo from the + * appropriate pool. + * + * @param powerOfTwo + * int range of 0-30 + * @return + */ + public ByteBuffer getBuffer(int powerOfTwo) { + // Validate input + if (powerOfTwo > 30 || powerOfTwo < 0) { + Logger.error("powerOfTwo out of range (0-30) in getBuffer(). Got: " + powerOfTwo); + return null; + } + + // Check to see if there is a pool for this size + ByteBufferPool bbp = this.getByteBufferPool(powerOfTwo); + return bbp.get(); + } + + /** + * Internal getter to provide synchronization. Adds ByteBufferPool if not mapped. + * + * @param powerOfTwo + * @return + */ + private ByteBufferPool getByteBufferPool(Integer powerOfTwo) { + synchronized (this.powerToPoolMap) { + // Check to see if there is a pool for this size + ByteBufferPool bbp = powerToPoolMap.get(powerOfTwo); + + if (bbp == null) { + bbp = MultisizeByteBufferPool.makeByteBufferPool(powersOfTwo[powerOfTwo]); + this.putByteBufferPool(powerOfTwo, bbp); + } + return bbp; + } + } + + /** + * Internal setter to provide synchronization + * + * @param powerOfTwo + * @param bbp + * @return + */ + private ByteBufferPool putByteBufferPool(Integer powerOfTwo, + ByteBufferPool bbp) { + synchronized (this.powerToPoolMap) { + return powerToPoolMap.put(powerOfTwo, bbp); + } + } + + public ByteBuffer getBufferToFit(int numOfBytes) { + int pow = MultisizeByteBufferPool.getPowerThatWillFit(numOfBytes); + return this.getBuffer(pow); + } + + /** + * Puts a ByteBuffer that is of the size 2^powerOfTwo back into the + * appropriate pool. + * + * @param bb + * - Bytebuffer to put into a pool + * + */ + public void putBuffer(ByteBuffer bb) { + + if (bb == null) + return; + + // determine size: + int pow = MultisizeByteBufferPool.getPowerThatWillFit(bb.capacity()); + + // if we get here and pow == -1 then we have a bytebuffer > 2^30 !!!! + // so just file it under power of 30; + if (pow == -1) { + pow = 30; + } + + // get pool + ByteBufferPool bbp = this.getByteBufferPool(pow); + + // put buffer (back) into pool + bbp.put(bb); + } + + /** + * Returns the next power of two that is larger than or equal too the input + * value + * + * @param value + * @return the power of two that is larger than or equal too the input + * value. A return of -1 indicates out of range. + */ + public static int getPowerThatWillFit(final int value) { + return (value == 0 ? 0 : 32 - Integer.numberOfLeadingZeros(value - 1)); + } + + private static ByteBufferPool makeByteBufferPool(int bbInitialSize) { + return new ByteBufferPool(bbInitialSize); + } + + /** + * Returns the size of the ByteBufferPool mapped to the given powerOfTwo. + * + * @param powerOfTwo + * int range of 0-30 + * @return size of pool mapped to provided powerOfTwo. Returns -1 on + * error and lastError will be set. + */ + public int getSizeOfPool(int powerOfTwo) { + if (powerOfTwo > 30 || powerOfTwo < 0) { + Logger.error("powerOfTwo out of range (0-30) in getSizeOfPool(). Got: " + + powerOfTwo); + return -1; + } + ByteBufferPool bbp = this.getByteBufferPool(powerOfTwo); + + return bbp.getPoolSize(); + } + + + /** + * List of the powers of two from 2^0 to 2^30. The index of the array + * corresponds to the power of two. Example: If you'd like to quickly lookup + * 2^19, then reference powersOfTwo[19] + */ + public static final int[] powersOfTwo = { + 1, // 2^0 + 2, // 2^1 + 4, // 2^2 + 8, // 2^3 + 16, // 2^4 + 32, // 2^5 + 64, // 2^6 + 128, // 2^7 + 256, // 2^8 + 512, // 2^9 + 1024, // 2^10 + 2048, // 2^11 + 4096, // 2^12 + 8192, // 2^13 + 16384, // 2^14 + 32768, // 2^15 + 65536, // 2^16 + 131072, // 2^17 + 262144, // 2^18 + 524288, // 2^19 + 1048576, // 2^20 + 2097152, // 2^21 + 4194304, // 2^22 + 8388608, // 2^23 + 16777216, // 2^24 + 33554432, // 2^25 + 67108864, // 2^26 + 134217728, // 2^27 + 268435456, // 2^28 + 536870912, // 2^29 + 1073741824, // 2^30 + }; + +} diff --git a/src/engine/pooling/ObjectPool.java b/src/engine/pooling/ObjectPool.java new file mode 100644 index 00000000..5ded922f --- /dev/null +++ b/src/engine/pooling/ObjectPool.java @@ -0,0 +1,195 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.pooling; + +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; + +public abstract class ObjectPool { + protected final static int DEFAULT_SIZE = 1000; + + // Simple + quick list + private final ArrayList pool; + + /** + * Once the ArrayList fills to threshold, subsequent .put() calls + * result in the object being thrown away. Default is 75% of supplied + * size. + */ + private int threshold; + + /** + * Constructor that allows the caller to specify initial array size and + * percent of prefill. + * + * @param size + * - initial size for the containing array (pool) + */ + public ObjectPool(int size) { + if (size == 0) { + size = DEFAULT_SIZE; + } + + threshold = (int) (0.75 * size); + + this.pool = new ArrayList<>(size); + + } + + /** + * Default Constructor that uses default initial Array size and percent + * prefill values + */ + public ObjectPool() { + this(DEFAULT_SIZE); + } + + /** + * Forces pool to add numberOfObjects to the pool. This may cause + * internal ArrayList to resize. + * + * @param numberOfObjects + */ + public void fill(int numberOfObjects) { + for (int i = 0; i < numberOfObjects; ++i) + this.makeAndAdd(); + } + + /** + * Forces subclasses to implement factory routine for object creation. + */ + protected abstract T makeNewObject(); + + /** + * Forces subclasses to implement a way to reset object to a reusable state. + */ + protected abstract void resetObject(T obj); + + /** + * Generic Get routine. If the pool is empty, then one (or more) of T type + * objects will be created. If more than one T is created, then they will be + * put into the pool. One T will always be created and returned. + */ + public T get() { + synchronized (pool) { + //Logger.debug("Objectpool.get(). Pool size before get: " + pool.size()); + if (pool.size() > 0) { + return pool.remove(0); + } + } + + this.handlePoolExhaustion(); + + T obj = this.makeNewObject(); + + if (obj == null) { + Logger.error("Pooling failure: Object creation failed."); + } + return obj; + } + + protected void handlePoolExhaustion(){ + Logger.debug("Pool exhausted, making more objects."); + + // If none exist, make (and pool) a few + // Dont sync the loop, let the makeNewObject() + // call return before locking the pool object + for (int i = 0; i < 5; ++i) + this.makeAndAdd(); + + } + + /** + * Generic put routine. If the current pool size is below threshold, this + * object will be pooled, otherwise it will be NULL'ed and scheduled for + * Garbage Collection + * + * @param obj + */ + public void put(T obj) { + synchronized (pool) { + if (pool.size() >= this.threshold) { + //Logger.debug("Objectpool.put() rejected. Pool size: " + pool.size()); + return; + } + //Logger.debug("Objectpool.put(). Pool size: " + pool.size()); + this.resetObject(obj); + this.pool.add(obj); + } + } + + /** + * Helper method. Attempts to create and add a new T object. If this + * fails, an error is logged. + */ + protected final void makeAndAdd() { + T obj = this.makeNewObject(); + if (obj == null) { + Logger.error("Pooling failure: Object creation failed."); + } else { + this.put(obj); + } + } + + /** + * + * @return the current size of the pool, not the maximum capacity of the + * Array holding the pool. + */ + public final int getPoolSize() { + synchronized (this.pool) { + return this.pool.size(); + } + } + + /** + * Culls the pool and removes half of the stored objects. Removed objects + * are NULL'ed and scheduled form Garbage Collection. + * + * @return the number of Objects removed from the pool. + */ + public int cullHalf() { + int full, half; + synchronized (this.pool) { + full = this.pool.size(); + if (full < 1) { + return full; + } + + half = (full / 2); + + for (int i = 0; i < (full / 2); ++i) { + T obj = this.get(); + obj = null; // Null out for GC + } + } + return half; + } + + /** + * @return the threshold + */ + public final int getThreshold() { + return threshold; + } + + /** + * @param threshold + * the threshold to set + */ + public void setThreshold(int threshold) { + if (threshold < 0) + return; + + this.threshold = threshold; + } + +} diff --git a/src/engine/pooling/Poolable.java b/src/engine/pooling/Poolable.java new file mode 100644 index 00000000..4155cbc5 --- /dev/null +++ b/src/engine/pooling/Poolable.java @@ -0,0 +1,14 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.pooling; + +public interface Poolable { + public void clear(); +} diff --git a/src/engine/powers/ActionsBase.java b/src/engine/powers/ActionsBase.java new file mode 100644 index 00000000..0f157354 --- /dev/null +++ b/src/engine/powers/ActionsBase.java @@ -0,0 +1,266 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers; + +import engine.Enum.ModType; +import engine.Enum.SourceType; +import engine.Enum.StackType; +import engine.gameManager.PowersManager; +import engine.objects.*; +import engine.powers.poweractions.AbstractPowerAction; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; + + +public class ActionsBase { + + public int UUID; + public String IDString; + public String effectID; + public int minTrains; + public int maxTrains; + public float duration; + public float ramp; + public boolean addFormula; + public String stackType; + public StackType stackTypeType; + public int stackOrder; + + public boolean greaterThanEqual = false; + public boolean always = false; + public boolean greaterThan = false; + public String stackPriority; + + private AbstractPowerAction powerAction; + + /** + * No Table ID Constructor + */ + public ActionsBase() { + + } + + /** + * ResultSet Constructor + */ + public ActionsBase(ResultSet rs, HashMap apa) throws SQLException { + + this.UUID = rs.getInt("ID"); + this.IDString = rs.getString("powerID"); + this.effectID = rs.getString("effectID"); + this.minTrains = rs.getInt("minTrains"); + this.maxTrains = rs.getInt("maxTrains"); + this.duration = rs.getFloat("duration"); + this.ramp = rs.getFloat("ramp"); + this.addFormula = (rs.getInt("useAddFormula") == 1) ? true : false; + this.stackType = rs.getString("stackType"); + this.stackTypeType = StackType.GetStackType(this.stackType); + this.stackOrder = rs.getInt("stackOrder"); + this.stackPriority = rs.getString("stackPriority"); + + switch (stackPriority) { + case "GreaterThanOrEqualTo": + this.greaterThanEqual = true; + break; + case "Always": + this.always = true; + break; + case "GreaterThan": + this.greaterThan = true; + break; + } + this.powerAction = apa.get(this.effectID); + } + + protected ActionsBase(int uUID, String effectID, int minTrains, int maxTrains, float duration, float ramp, + boolean addFormula, String stackType, int stackOrder, boolean greaterThanEqual, boolean always, + boolean greaterThan, AbstractPowerAction powerAction) { + super(); + UUID = uUID; + this.effectID = effectID; + this.minTrains = minTrains; + this.maxTrains = maxTrains; + this.duration = duration; + this.ramp = ramp; + this.addFormula = addFormula; + this.stackType = stackType; + this.stackTypeType = StackType.GetStackType(this.stackType); + if (this.stackTypeType == null) + Logger.info("Invalid Stack Type " + this.stackTypeType + " for " + this.effectID); + this.stackOrder = stackOrder; + this.greaterThanEqual = greaterThanEqual; + this.always = always; + this.greaterThan = greaterThan; + this.powerAction = powerAction; + + if (this.greaterThanEqual) + this.stackPriority = "GreaterThanOrEqualTo"; + else if(this.always) + this.stackPriority = "Always"; + else if (this.greaterThan) + this.stackPriority = "GreaterThan"; + + } + + // public static ArrayList getActionsBase(String ID) { + // PreparedStatementShared ps = null; + // ArrayList out = new ArrayList(); + // try { + // ps = new PreparedStatementShared("SELECT * FROM actions where powerID = ?"); + // ps.setString(1, ID); + // ResultSet rs = ps.executeQuery(); + // while (rs.next()) { + // ActionsBase toAdd = new ActionsBase(rs); + // out.add(toAdd); + // } + // rs.close(); + // } catch (Exception e) { + // Logger.error("ActionsBase", e); + // } finally { + // ps.release(); + // } + // return out; + // } + + public static void getActionsBase(HashMap powers, HashMap apa) { + PreparedStatementShared ps = null; + try { + ps = new PreparedStatementShared("SELECT * FROM static_power_action"); + ResultSet rs = ps.executeQuery(); + String IDString; ActionsBase toAdd; PowersBase pb; + while (rs.next()) { + IDString = rs.getString("powerID"); + pb = powers.get(IDString); + if (pb != null) { + toAdd = new ActionsBase(rs, apa); + pb.getActions().add(toAdd); + } + } + rs.close(); + } catch (Exception e) { + Logger.error( e.toString()); + } finally { + ps.release(); + } + + int gateID = 5000; + for (String IDString : Runegate.GetAllOpenGateIDStrings()){ + gateID++; + ActionsBase openGateActionBase = new ActionsBase(gateID, "OPENGATE", 5, 9999, 0, 0, true, "IgnoreStack", 0, true, false, false, PowersManager.getPowerActionByIDString("OPENGATE")); + + PowersBase openGatePower = powers.get(IDString); + + if (openGatePower == null){ + Logger.error( "no powerbase for action " + IDString); + break; + } + openGatePower.getActions().add(openGateActionBase); + } + } + + + public int getUUID() { + return this.UUID; + } + + public String getEffectID() { + return this.effectID; + } + + public int getMinTrains() { + return this.minTrains; + } + + public int getMaxTrains() { + return this.maxTrains; + } + + public float getDuration() { + return this.duration; + } + + public AbstractPowerAction getPowerAction() { + return this.powerAction; + } + + public int getDuration(int trains) { + if (this.addFormula) + return (int)((this.duration + (this.ramp * trains)) * 1000); + else + return (int)((this.duration * (1 + (this.ramp * trains))) * 1000); + } + + public float getDurationAsFloat(int trains) { + if (this.addFormula) + return ((this.duration + (this.ramp * trains)) * 1000); + else + return ((this.duration * (1 + (this.ramp * trains))) * 1000); + } + + public int getDurationInSeconds(int trains) { + if (this.addFormula) + return (int)(this.duration + (this.ramp * trains)); + else + return (int)(this.duration * (1 + (this.ramp * trains))); + } + + public String getStackType() { + return this.stackType; + } + + public int getStackOrder() { + return this.stackOrder; + } + + public boolean greaterThanEqual() { + return this.greaterThanEqual; + } + + public boolean greaterThan() { + return this.greaterThan; + } + + public boolean always() { + return this.always; + } + + //Add blocked types here + public boolean blocked(AbstractWorldObject awo, PowersBase pb, int trains) { + if (AbstractWorldObject.IsAbstractCharacter(awo)) { + AbstractCharacter ac = (AbstractCharacter) awo; + PlayerBonuses bonus = ac.getBonuses(); + if (bonus == null) + return false; + + //TODO make this more efficient then testing strings + if (this.stackType.equals("Stun") && bonus.getBool(ModType.ImmuneTo, SourceType.Stun)) + return true; //Currently stun immune. Skip stun + else if(this.stackType.equals("Snare") && bonus.getBool(ModType.ImmuneTo, SourceType.Snare)) + return true; //Currently snare immune. Skip snare + else if(this.stackType.equals("Blindness") && bonus.getBool(ModType.ImmuneTo, SourceType.Blind)) + return true; //Currently blind immune. Skip blind + else if(this.stackType.equals("PowerInhibitor") && bonus.getBool(ModType.ImmuneTo, SourceType.Powerblock)) + return true; //Currently power block immune. Skip power block + else if (this.stackType.equals("Root") && bonus.getBool(ModType.ImmuneTo, SourceType.Root)) + return true; + // else if (pb.isHeal() && (bonus.getByte("immuneTo.Heal")) >= trains) + // return true; //Currently shadowmantled. Skip heals + else if (this.stackType.equals("Flight") && bonus.getBool(ModType.NoMod, SourceType.Fly)) + return true; + else if (this.stackType.equals("Track") && bonus.getBool(ModType.CannotTrack, SourceType.None)) + return true; + else return pb.vampDrain() && bonus.getBool(ModType.BlockedPowerType, SourceType.VAMPDRAIN); + } + return false; + } +} diff --git a/src/engine/powers/DamageShield.java b/src/engine/powers/DamageShield.java new file mode 100644 index 00000000..58b4af0b --- /dev/null +++ b/src/engine/powers/DamageShield.java @@ -0,0 +1,43 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + package engine.powers; + +import engine.Enum.DamageType; + +public class DamageShield { + + private final DamageType damageType; + private final float amount; + private final boolean usePercent; + + public DamageShield(DamageType damageType, float amount, boolean usePercent) { + super(); + this.damageType = damageType; + this.amount = amount; + this.usePercent = usePercent; + } + + public DamageType getDamageType() { + return this.damageType; + } + + public float getAmount() { + return this.amount; + } + + public boolean usePercent() { + return this.usePercent; + } + + @Override + public String toString() { + return "ds.DamageType: " + this.damageType.name() + ", Amount: " + this.amount + ", UsePercent: " + this.usePercent; + } +} diff --git a/src/engine/powers/EffectsBase.java b/src/engine/powers/EffectsBase.java new file mode 100644 index 00000000..e8cfca49 --- /dev/null +++ b/src/engine/powers/EffectsBase.java @@ -0,0 +1,892 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers; + +import engine.Enum; +import engine.Enum.DamageType; +import engine.Enum.EffectSourceType; +import engine.Enum.GameObjectType; +import engine.Enum.PowerFailCondition; +import engine.gameManager.DbManager; +import engine.gameManager.PowersManager; +import engine.job.JobContainer; +import engine.jobs.AbstractEffectJob; +import engine.jobs.DamageOverTimeJob; +import engine.jobs.FinishSpireEffectJob; +import engine.jobs.NoTimeJob; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ApplyEffectMsg; +import engine.objects.*; +import engine.powers.effectmodifiers.AbstractEffectModifier; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.concurrent.ConcurrentHashMap; + +public class EffectsBase { + + private int UUID; + private String IDString; + // private String name; + private int token; + private float amount; + private float amountRamp; + + // flags + private boolean isItemEffect; + private boolean isSpireEffect; + private boolean ignoreMod; + private boolean dontSave; + + private boolean cancelOnAttack = false; + private boolean cancelOnAttackSwing = false; + private boolean cancelOnCast = false; + private boolean cancelOnCastSpell = false; + private boolean cancelOnEquipChange = false; + private boolean cancelOnLogout = false; + private boolean cancelOnMove = false; + private boolean cancelOnNewCharm = false; + private boolean cancelOnSit = false; + private boolean cancelOnTakeDamage = false; + private boolean cancelOnTerritoryClaim = false; + private boolean cancelOnUnEquip = false; + private boolean useRampAdd; + private boolean isPrefix = false; //used by items + private boolean isSuffix = false; //used by items + private String name = ""; + private float value = 0; + private ConcurrentHashMap resourceCosts = new ConcurrentHashMap<>(); + private ConcurrentHashMap sourceTypes = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + public static HashMap> effectSourceTypeMap = new HashMap<>(); + public static HashMap> modifiersMap = new HashMap<>(); + private static ConcurrentHashMap itemEffectsByName = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); + private static int NewID = 3000; + public static HashMap>> OldEffectsMap = new HashMap<>(); + public static HashMap>> NewEffectsMap = new HashMap<>(); + public static HashMap>> ChangedEffectsMap = new HashMap<>(); + public static HashMap> EffectFailConditions = new HashMap<>(); + public static HashMap> EffectDamageTypes = new HashMap<>(); + + public static HashSet DefaultModifiers = new HashSet<>(); + /** + * No Table ID Constructor + */ + public EffectsBase() { + + } + + public EffectsBase(EffectsBase copyEffect, int newToken, String IDString) { + + UUID = NewID++; + this.IDString = IDString; + this.token = newToken; + + //filll + if (copyEffect == null){ + int flags = 0; + this.isItemEffect = ((flags & 1) != 0) ? true : false; + this.isSpireEffect = ((flags & 2) != 0) ? true : false; + this.ignoreMod = ((flags & 4) != 0) ? true : false; + this.dontSave = ((flags & 8) != 0) ? true : false; + + if (this.IDString.startsWith("PRE-")) + this.isPrefix = true; + else if (this.IDString.startsWith("SUF-")) + this.isSuffix = true; + + } + + + this.amount = copyEffect.amount; + this.amountRamp = copyEffect.amountRamp; + this.isItemEffect = copyEffect.isItemEffect; + this.isSpireEffect = copyEffect.isSpireEffect; + this.ignoreMod = copyEffect.ignoreMod; + this.dontSave = copyEffect.dontSave; + this.cancelOnAttack = copyEffect.cancelOnAttack; + this.cancelOnAttackSwing = copyEffect.cancelOnAttackSwing; + this.cancelOnCast = copyEffect.cancelOnCast; + this.cancelOnCastSpell = copyEffect.cancelOnCastSpell; + this.cancelOnEquipChange = copyEffect.cancelOnEquipChange; + this.cancelOnLogout = copyEffect.cancelOnLogout; + this.cancelOnMove = copyEffect.cancelOnMove; + this.cancelOnNewCharm = copyEffect.cancelOnNewCharm; + this.cancelOnSit = copyEffect.cancelOnSit; + this.cancelOnTakeDamage = copyEffect.cancelOnTakeDamage; + this.cancelOnTerritoryClaim = copyEffect.cancelOnTerritoryClaim; + this.cancelOnUnEquip = copyEffect.cancelOnUnEquip; + this.useRampAdd = copyEffect.useRampAdd; + this.isPrefix = copyEffect.isPrefix; + this.isSuffix = copyEffect.isSuffix; + this.name = copyEffect.name; + this.value = copyEffect.value; + this.resourceCosts = copyEffect.resourceCosts; + + } + + /** + * ResultSet Constructor + */ + public EffectsBase(ResultSet rs) throws SQLException { + + this.UUID = rs.getInt("ID"); + this.IDString = rs.getString("IDString"); + this.name = rs.getString("name"); + this.token = rs.getInt("Token"); + + //override tokens for some effects like Safemode that use the Action Token instead of the effect Token, + switch (this.IDString){ + case "INVIS-D": + this.token = -1661751254; + break; + case "SafeMode": + this.token = -1661750486; + break; + + } + int flags = rs.getInt("flags"); + this.isItemEffect = ((flags & 1) != 0) ? true : false; + this.isSpireEffect = ((flags & 2) != 0) ? true : false; + this.ignoreMod = ((flags & 4) != 0) ? true : false; + this.dontSave = ((flags & 8) != 0) ? true : false; + + if (this.IDString.startsWith("PRE-")) + this.isPrefix = true; + else if (this.IDString.startsWith("SUF-")) + this.isSuffix = true; + // getFailConditions(); + } + + + public static EffectsBase createNoDbEffectsBase(EffectsBase copyEffect, int newToken, String IDString){ + EffectsBase cachedEffectsBase = new EffectsBase(copyEffect,newToken,IDString); + + if (cachedEffectsBase == null) + return null; + + //add to Lists. + PowersManager.effectsBaseByIDString.put(cachedEffectsBase.IDString, cachedEffectsBase); + PowersManager.effectsBaseByToken.put(cachedEffectsBase.token, cachedEffectsBase); + + return cachedEffectsBase; + } + + + + public static ArrayList getAllEffectsBase() { + PreparedStatementShared ps = null; + ArrayList out = new ArrayList<>(); + try { + ps = new PreparedStatementShared("SELECT * FROM static_power_effectbase ORDER BY `IDString` DESC"); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + EffectsBase toAdd = new EffectsBase(rs); + out.add(toAdd); + } + rs.close(); + } catch (Exception e) { + Logger.error(e); + } finally { + ps.release(); + } + //testHash(out); + return out; + } + + public static ArrayList getAllLiveEffectsBase() { + PreparedStatementShared ps = null; + ArrayList out = new ArrayList<>(); + try { + ps = new PreparedStatementShared("SELECT * FROM static_power_effectbase_24 ORDER BY `IDString` DESC"); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + EffectsBase toAdd = new EffectsBase(rs); + out.add(toAdd); + } + rs.close(); + } catch (Exception e) { + Logger.error(e); + } finally { + ps.release(); + } + //testHash(out); + return out; + } + + //private static void testHash(ArrayList effs) { + // int valid = 0, invalid = 0; + // for (EffectsBase eff : effs) { + // String ids = eff.getIDString(); + // int tok = eff.getToken(); + // if (ids.length() != 8 || ids.startsWith("PRE-") || ids.startsWith("SUF-") || ids.endsWith("X") || !ids.substring(3,4).equals("-")) + // continue; + // + //// if ((tok > 1 || tok < 0) && ids.length() == 8) { + // int out = Hash(ids); + // if (out != tok) { + // System.out.println(ids + ": " + Integer.toHexString(out) + "(" + out + ")"); + // invalid++; + // } else + // valid++; + //// } + // } + // System.out.println("valid: " + valid + ", invalid: " + invalid); + //} + + //private static int Hash(String IDString) { + // char[] val = IDString.toCharArray(); + // int out = 360; + // out ^= val[0]; + // out ^= (val[1] << 5); + // out ^= (val[2] << 10); + // out ^= (val[4] << 23); + // out ^= (val[5] << 19); + // out ^= (val[6] << 15); + // out ^= (val[7] << 26); + // out ^= (val[7] >> 6); + // out ^= 17; + // return out; + //} + + + public static void getFailConditions(HashMap effects) { + PreparedStatementShared ps = null; + try { + ps = new PreparedStatementShared("SELECT * FROM static_power_failcondition WHERE powerOrEffect = 'Effect';"); + + ResultSet rs = ps.executeQuery(); + PowerFailCondition failCondition = null; + + Object value; + while (rs.next()) { + String fail = rs.getString("type"); + + + + String IDString = rs.getString("IDString"); + int token = DbManager.hasher.SBStringHash(IDString); + failCondition = PowerFailCondition.valueOf(fail); + if (failCondition == null){ + Logger.error( "Couldn't Find FailCondition " + fail + " for " + IDString); + continue; + } + + if (EffectsBase.EffectFailConditions.get(IDString) == null){ + EffectsBase.EffectFailConditions.put(IDString, new HashSet<>()); + } + + EffectsBase.EffectFailConditions.get(IDString).add(failCondition); + EffectsBase eb = effects.get(IDString); + + switch (failCondition) { + + case TakeDamage: + + + + // dont go any further. + if (eb == null){ + break; + } + + eb.cancelOnTakeDamage = true; + + + + + eb.amount = rs.getFloat("amount"); + eb.amountRamp = rs.getFloat("ramp"); + eb.useRampAdd = rs.getBoolean("UseAddFormula"); + + String damageType1 = rs.getString("damageType1"); + String damageType2 = rs.getString("damageType1"); + String damageType3 = rs.getString("damageType1"); + + + if (damageType1.isEmpty() && damageType2.isEmpty() && damageType3.isEmpty()) + break; + + if (!EffectsBase.EffectDamageTypes.containsKey(eb.getToken())){ + EffectsBase.EffectDamageTypes.put(eb.getToken(), new HashSet<>()); + } + if (damageType1.equalsIgnoreCase("Crushing")) + damageType1 = "Crush"; + if (damageType1.equalsIgnoreCase("Piercing")) + damageType1 = "Pierce"; + if (damageType1.equalsIgnoreCase("Slashing")) + damageType1 = "Slash"; + + if (damageType2.equalsIgnoreCase("Crushing")) + damageType2 = "Crush"; + if (damageType2.equalsIgnoreCase("Piercing")) + damageType2 = "Pierce"; + if (damageType2.equalsIgnoreCase("Slashing")) + damageType2 = "Slash"; + + if (damageType3.equalsIgnoreCase("Crushing")) + damageType3 = "Crush"; + if (damageType3.equalsIgnoreCase("Piercing")) + damageType3 = "Pierce"; + if (damageType3.equalsIgnoreCase("Slashing")) + damageType3 = "Slash"; + DamageType dt = getDamageType(damageType1); + if (dt != null) + EffectsBase.EffectDamageTypes.get(eb.token).add(dt); + + dt = getDamageType(damageType2); + if (dt != null) + EffectsBase.EffectDamageTypes.get(eb.token).add(dt); + dt = getDamageType(damageType3); + if (dt != null) + EffectsBase.EffectDamageTypes.get(eb.token).add(dt); + break; + case Attack: + eb.cancelOnAttack = true; + break; + case AttackSwing: + eb.cancelOnAttackSwing = true; + break; + case Cast: + eb.cancelOnCast = true; + break; + case CastSpell: + eb.cancelOnCastSpell = true; + break; + case EquipChange: + eb.cancelOnEquipChange = true; + break; + case Logout: + eb.cancelOnLogout = true; + break; + case Move: + eb.cancelOnMove = true; + break; + case NewCharm: + eb.cancelOnNewCharm = true; + break; + case Sit: + eb.cancelOnSit = true; + break; + case TerritoryClaim: + eb.cancelOnTerritoryClaim = true; + break; + case UnEquip: + eb.cancelOnUnEquip = true; + break; + } + } + + rs.close(); + } catch (Exception e) { + Logger.error( e); + } finally { + ps.release(); + } + + } + + public float getDamageAmount(int trains) { + if (useRampAdd) + return (amount + (amountRamp * trains)); + else + return (amount * (1 + (amountRamp * trains))); + } + + public boolean damageTypeSpecific() { + + return EffectsBase.EffectDamageTypes.containsKey(this.token); + + } + + public boolean containsDamageType(DamageType dt) { + if (!EffectsBase.EffectDamageTypes.containsKey(this.token)) + return false; + return EffectsBase.EffectDamageTypes.get(this.token).contains(dt); + } + + private static DamageType getDamageType(String name) { + try { + switch (name) { + case "Crushing": + name = "Crush"; + break; + case "Slashing": + name = "Slash"; + break; + case "Piercing": + name = "Pierce"; + break; + } + if (name.isEmpty()) + return null; + else + return DamageType.valueOf(name); + } catch (Exception e) { + Logger.error(name); + return null; + } + } + + // public String getName() { + // return this.name; + // } + + public int getUUID() { + return this.UUID; + } + + public String getIDString() { + return this.IDString; + } + + public int getToken() { + return this.token; + } + + public ConcurrentHashMap getSourceTypes() { + return this.sourceTypes; + } + + public HashSet getModifiers() { + + if (EffectsBase.modifiersMap.containsKey(this.IDString) == false) + return EffectsBase.DefaultModifiers; + + return EffectsBase.modifiersMap.get(this.IDString); + } + + public boolean isItemEffect() { + return this.isItemEffect; + } + + public boolean isSpireEffect() { + return this.isSpireEffect; + } + + public boolean ignoreMod() { + return this.ignoreMod; + } + + public boolean dontSave() { + return this.dontSave; + } + + public boolean isPrefix() { + return this.isPrefix; + } + + public boolean isSuffix() { + return this.isSuffix; + } + + public void startEffect(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + + + // Add SourceTypes for dispel + + if (this.token != 0) { + if (effect == null) { + Logger.error("AbstractEffectModifier.applyEffectModifier: missing FinishEffectTimeJob"); + return; + } + // AbstractWorldObject source = effect.getSource(); + if (source == null) { + Logger.error( "AbstractEffectModifier.applyEffectModifier: missing source"); + return; + } + PowersBase pb = effect.getPower(); + if (pb == null) { + Logger.error( "AbstractEffectModifier.applyEffectModifier: missing power"); + return; + } + ActionsBase ab = effect.getAction(); + if (ab == null) { + Logger.error( "AbstractEffectModifier.applyEffectModifier: missing action"); + return; + } + + //don't send effect if dead, except for death shroud + if (!awo.isAlive()) { + if (pb.getToken() != 1672601862) + return; + } + + + if (!effect.skipSendEffect()) { + // float duration = (pb.isChant()) ? pb.getChantDuration() * 1000 : ab.getDuration(trains); + float duration = ab.getDurationInSeconds(trains); + if (pb.getToken() == 1672601862){ + + Effect eff = awo.getEffects().get("DeathShroud"); + + + + + if (eff != null) { + JobContainer jc = eff.getJobContainer(); + + + if (jc != null){ + duration = jc.timeOfExection() - System.currentTimeMillis(); + duration *= .001f; + } + } + } + + + + if (duration > 0f) { + int removeToken = this.token; + ApplyEffectMsg pum = new ApplyEffectMsg(); + if (effect.getAction() != null) + if ( effect.getAction().getPowerAction() != null + && PowersManager.ActionTokenByIDString.containsKey(effect.getAction().getPowerAction().getIDString())) + try{ + removeToken = PowersManager.ActionTokenByIDString.get(effect.getAction().getPowerAction().getIDString()); + }catch(Exception e){ + removeToken = this.token; + } + + pum.setEffectID(removeToken); + pum.setSourceType(source.getObjectType().ordinal()); + pum.setSourceID(source.getObjectUUID()); + pum.setTargetType(awo.getObjectType().ordinal()); + pum.setTargetID(awo.getObjectUUID()); + pum.setNumTrains(trains); + pum.setDuration((int) duration); + // pum.setDuration((pb.isChant()) ? (int)pb.getChantDuration() : ab.getDurationInSeconds(trains)); + pum.setPowerUsedID(pb.getToken()); + pum.setPowerUsedName(pb.getName()); + DispatchMessage.sendToAllInRange(awo, pum); + } + + if (awo.getObjectType().equals(GameObjectType.Item)) { + if (source.getCharItemManager() != null) { + source.getCharItemManager().updateInventory(); + } + } + } + + // call modifiers to do their job + if (!effect.skipApplyEffect()) { + for (AbstractEffectModifier em : this.getModifiers()) + em.applyEffectModifier(source, awo, trains, effect); + } + } + } + + // Send end effect message to client + public void endEffect(AbstractWorldObject source, AbstractWorldObject awo, int trains, PowersBase pb, AbstractEffectJob effect) { + if (awo == null) { + Logger.error("endEffect(): Null AWO object passed in."); + return; + } + if (pb == null) { + Logger.error("endEffect(): Null PowerBase object passed in."); + return; + } + if (!effect.skipCancelEffect() && !effect.isNoOverwrite()) { + + int sendToken = this.token; + + if (effect.getAction() != null) + if ( effect.getAction().getPowerAction() != null + && PowersManager.ActionTokenByIDString.containsKey(effect.getAction().getPowerAction().getIDString())) + try{ + sendToken = PowersManager.ActionTokenByIDString.get(effect.getAction().getPowerAction().getIDString()); + }catch(Exception e){ + sendToken = this.token; + } + ApplyEffectMsg pum = new ApplyEffectMsg(); + pum.setEffectID(sendToken); + if (source != null) { + pum.setSourceType(source.getObjectType().ordinal()); + pum.setSourceID(source.getObjectUUID()); + } else { + pum.setSourceType(0); + pum.setSourceID(0); + } + pum.setTargetType(awo.getObjectType().ordinal()); + pum.setTargetID(awo.getObjectUUID()); + pum.setUnknown02(2); + pum.setNumTrains(0); + pum.setDuration(-1); + pum.setPowerUsedID(pb.getToken()); + pum.setPowerUsedName(pb.getName()); + DispatchMessage.sendToAllInRange(awo, pum); + + } + } + + public void endEffectNoPower(int trains, AbstractEffectJob effect) { + + AbstractWorldObject source = effect.getSource(); + + if (source == null) + return; + + if (!effect.skipCancelEffect() && !effect.isNoOverwrite()) { + ApplyEffectMsg pum = new ApplyEffectMsg(); + pum.setEffectID(this.token); + + pum.setSourceType(source.getObjectType().ordinal()); + pum.setSourceID(source.getObjectUUID()); + pum.setTargetType(source.getObjectType().ordinal()); + pum.setTargetID(source.getObjectUUID()); + pum.setUnknown02(2); + pum.setNumTrains(0); + pum.setDuration(-1); + pum.setUnknown06((byte)1); + pum.setEffectSourceType(effect.getEffectSourceType()); + pum.setEffectSourceID(effect.getEffectSourceID()); + pum.setPowerUsedID(0); + pum.setPowerUsedName(this.name); + + if (source.getObjectType() == GameObjectType.PlayerCharacter){ + Dispatch dispatch = Dispatch.borrow((PlayerCharacter)source, pum); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); + } + } + } + + public void sendEffect(AbstractEffectJob effect, int duration, ClientConnection conn) { + if (effect == null && conn != null) + return; + + if (conn == null) + return; + AbstractWorldObject source = effect.getSource(); + AbstractWorldObject awo = effect.getTarget(); + int trains = effect.getTrains(); + if (source == null || awo == null) + return; + + if (this.token != 0) { + PowersBase pb = effect.getPower(); + if (pb == null) { + Logger.error( "AbstractEffectModifier.applyEffectModifier: missing power"); + return; + } + ActionsBase ab = effect.getAction(); + if (ab == null) { + Logger.error("AbstractEffectModifier.applyEffectModifier: missing action"); + return; + } + + //don't send effect if dead, except for death shroud + if (!awo.isAlive()) { + if (pb.getToken() != 1672601862) + return; + } + + //duration for damage over times is (total time - (number of ticks x 5 seconds per tick)) + if (effect instanceof DamageOverTimeJob) + duration = ((DamageOverTimeJob)effect).getTickLength(); + + // float dur = (pb.isChant()) ? pb.getChantDuration() * 1000 : ab.getDuration(trains); + float dur = ab.getDuration(trains); + if (dur > 0f) { + ApplyEffectMsg pum = new ApplyEffectMsg(); + pum.setEffectID(this.token); + pum.setSourceType(source.getObjectType().ordinal()); + pum.setSourceID(source.getObjectUUID()); + pum.setTargetType(awo.getObjectType().ordinal()); + pum.setTargetID(awo.getObjectUUID()); + pum.setNumTrains(trains); + pum.setDuration(duration); + pum.setPowerUsedID(pb.getToken()); + pum.setPowerUsedName(pb.getName()); + + Dispatch dispatch = Dispatch.borrow(conn.getPlayerCharacter(), pum); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); + + } + } + } + + public void sendEffectNoPower(AbstractEffectJob effect, int duration, ClientConnection conn) { + + if (effect == null && conn != null) + return; + + if (conn == null) + return; + + AbstractWorldObject source = effect.getSource(); + AbstractWorldObject awo = effect.getTarget(); + int trains = effect.getTrains(); + + if (source == null || awo == null) + return; + + if (this.token != 0) { + //don't send effect if dead, except for death shroud + if (!awo.isAlive()) { + return; + } + + //duration for damage over times is (total time - (number of ticks x 5 seconds per tick)) + if (effect instanceof DamageOverTimeJob) + duration = ((DamageOverTimeJob)effect).getTickLength(); + else if (effect instanceof FinishSpireEffectJob) + duration = 45; + else if (effect instanceof NoTimeJob) + duration = -1; + + // float dur = (pb.isChant()) ? pb.getChantDuration() * 1000 : ab.getDuration(trains); + + ApplyEffectMsg pum = new ApplyEffectMsg(); + pum.setEffectID(this.token); + pum.setSourceType(source.getObjectType().ordinal()); + pum.setSourceID(source.getObjectUUID()); + pum.setTargetType(source.getObjectType().ordinal()); + pum.setTargetID(source.getObjectUUID()); + pum.setUnknown06((byte)1); + pum.setEffectSourceType(effect.getEffectSourceType()); + pum.setEffectSourceID(effect.getEffectSourceID()); + pum.setNumTrains(trains); + pum.setDuration(duration); + pum.setPowerUsedID(0); + pum.setPowerUsedName(this.name); + + Dispatch dispatch = Dispatch.borrow(conn.getPlayerCharacter(), pum); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY); + + } + } + + public boolean containsSource(EffectSourceType sourceType) { + if (EffectsBase.effectSourceTypeMap.containsKey(this.token) == false) + return false; + return EffectsBase.effectSourceTypeMap.get(this.token).contains(sourceType); + + } + + public boolean cancelOnAttack() { + return this.cancelOnAttack; + } + + public boolean cancelOnAttackSwing() { + return this.cancelOnAttackSwing; + } + + public boolean cancelOnCast() { + return this.cancelOnCast; + } + + public boolean cancelOnCastSpell() { + return this.cancelOnCastSpell; + } + + public boolean cancelOnEquipChange() { + return this.cancelOnEquipChange; + } + + public boolean cancelOnLogout() { + return this.cancelOnLogout; + } + + public boolean cancelOnMove() { + return this.cancelOnMove; + } + + public boolean cancelOnNewCharm() { + return this.cancelOnNewCharm; + } + + public boolean cancelOnSit() { + return this.cancelOnSit; + } + + public boolean cancelOnTakeDamage() { + return this.cancelOnTakeDamage; + } + + public boolean cancelOnTerritoryClaim() { + return this.cancelOnTerritoryClaim; + } + + public boolean cancelOnUnEquip() { + return this.cancelOnUnEquip; + } + + //For Debugging purposes. + public void setToken(int token) { + this.token = token; + } + + public static String getItemEffectsByName(String string) { + if (EffectsBase.itemEffectsByName.containsKey(string)) + return EffectsBase.itemEffectsByName.get(string); + return ""; + } + + public static void addItemEffectsByName(String name, String ID) { + EffectsBase.itemEffectsByName.put(name, ID); + } + + public String getDamageTypes() { + String text = ""; + if (!EffectsBase.EffectDamageTypes.containsKey(this.token)) + return text; + for (DamageType type: EffectsBase.EffectDamageTypes.get(this.token)) { + text += type.name() + ' '; + } + return text; + } + + public String getName() { + + return name; + } + + public void setName(String name){ + this.name = name; + } + + public float getValue() { + return value; + } + + public void setValue(float Value){ + this.value = Value; + } + public ConcurrentHashMap getResourcesForEffect() { + if (this.resourceCosts.isEmpty()){ + ArrayList effectsCostList = DbManager.EffectsResourceCostsQueries.GET_ALL_EFFECT_RESOURCES(this.IDString); + for (EffectsResourceCosts erc : effectsCostList){ + this.resourceCosts.put(ItemBase.getItemBase(erc.getResourceID()), erc.getAmount()); + } + } + return this.resourceCosts; + } + + +} diff --git a/src/engine/powers/FailCondition.java b/src/engine/powers/FailCondition.java new file mode 100644 index 00000000..5ee49f51 --- /dev/null +++ b/src/engine/powers/FailCondition.java @@ -0,0 +1,118 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers; + +import engine.objects.PreparedStatementShared; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; + + +public class FailCondition { + + private String IDString; + private Boolean forPower; + private String type; + private float amount; + private float ramp; + private boolean rampAdd; + + // private String damageType1; + // private String damageType2; + // private String damageType3; + + /** + * No Table ID Constructor + */ + public FailCondition() { + + } + + /** + * ResultSet Constructor + */ + public FailCondition(ResultSet rs) throws SQLException { + + this.IDString = rs.getString("IDString"); + this.forPower = (rs.getString("powerOrEffect").equals("Power")) ? true : false; + this.type = rs.getString("type"); + this.amount = rs.getFloat("amount"); + this.ramp = rs.getFloat("ramp"); + this.rampAdd = (rs.getInt("useAddFormula") == 1) ? true : false; + // this.damageType1 = rs.getString("damageType1"); + // this.damageType2 = rs.getString("damageType2"); + // this.damageType3 = rs.getString("damageType3"); + } + + public static ArrayList getAllFailConditions() { + PreparedStatementShared ps = null; + ArrayList out = new ArrayList<>(); + try { + ps = new PreparedStatementShared("SELECT * FROM failconditions"); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + FailCondition toAdd = new FailCondition(rs); + out.add(toAdd); + } + rs.close(); + } catch (Exception e) { + Logger.error( e); + + } finally { + ps.release(); + } + return out; + } + + public String getIDString() { + return this.IDString; + } + + public String getType() { + return this.type; + } + + public boolean forPower() { + return this.forPower; + } + + public float getAmount() { + return this.amount; + } + + public float getRamp() { + return this.ramp; + } + + public float getAmountForTrains(float trains) { + if (this.rampAdd) + return this.amount + (this.ramp * trains); + else + return this.amount * (1 + (this.ramp * trains)); + } + + public boolean useRampAdd() { + return this.rampAdd; + } + + // public String getDamageType1() { + // return this.damageType1; + // } + + // public String getDamageType2() { + // return this.damageType2; + // } + + // public String getDamageType3() { + // return this.damageType3; + // } +} diff --git a/src/engine/powers/PowerPrereq.java b/src/engine/powers/PowerPrereq.java new file mode 100644 index 00000000..b280e878 --- /dev/null +++ b/src/engine/powers/PowerPrereq.java @@ -0,0 +1,105 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers; + +import engine.objects.PreparedStatementShared; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; + + +public class PowerPrereq { + + private String effect; + private String message; + private boolean mainHand; + private boolean required; + + /** + * No Table ID Constructor + */ + public PowerPrereq() { + + } + + /** + * ResultSet Constructor + */ + public PowerPrereq(ResultSet rs, int type) throws SQLException { + + // this.IDString = rs.getString("IDString"); + if (type == 1) { + this.effect = rs.getString("messageone"); + this.message = rs.getString("messagetwo"); + this.mainHand = false; + this.required = false; + } else if (type == 2) { + String sl = rs.getString("messageone"); + if (sl.equals("RHELD")) + this.mainHand = true; + else if (sl.equals("LHELD")) + this.mainHand = false; + this.effect = ""; + this.message = rs.getString("messagetwo"); + this.required = (rs.getInt("required") == 1) ? true : false; + } else { //targetEffectPrereq + this.effect = rs.getString("messageone"); + this.message = ""; + this.mainHand = false; + this.required = (rs.getInt("required") == 1) ? true : false; + } + } + + public static void getAllPowerPrereqs(HashMap powers) { + PreparedStatementShared ps = null; + try { + ps = new PreparedStatementShared("SELECT * FROM static_power_powercastprereq"); + ResultSet rs = ps.executeQuery(); + int type; String IDString; PowerPrereq toAdd; PowersBase pb; + while (rs.next()) { + IDString = rs.getString("IDString"); + pb = powers.get(IDString); + if (pb != null) { + type = rs.getInt("Type"); + toAdd = new PowerPrereq(rs, type); + if (type == 1) + pb.getEffectPrereqs().add(toAdd); + else if (type == 2) + pb.getEquipPrereqs().add(toAdd); + else + pb.getTargetEffectPrereqs().add(toAdd); + } + } + rs.close(); + } catch (Exception e) { + Logger.error( e.toString()); + } finally { + ps.release(); + } + } + + public String getEffect() { + return this.effect; + } + + public String getMessage() { + return this.message; + } + + public boolean mainHand() { + return this.mainHand; + } + + public boolean isRequired() { + return this.required; + } +} diff --git a/src/engine/powers/PowersBase.java b/src/engine/powers/PowersBase.java new file mode 100644 index 00000000..89e4aa37 --- /dev/null +++ b/src/engine/powers/PowersBase.java @@ -0,0 +1,700 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers; + +import engine.Enum.PowerCategoryType; +import engine.Enum.PowerTargetType; +import engine.objects.PreparedStatementShared; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; + +public class PowersBase { + + public int UUID; + public String name; + public int token; + public String IDString; + public String category; + public int skillID; + public float range; + public float cost; + public float costRamp; + public float castTime; + public float castTimeRamp; + public float cooldown; + public float recycleTime; + public float recycleRamp; + public int maxTrains; + public float hateValue; + public float hateRamp; + public String monsterTypePrereq; // target limited to these types + public String skillName; + public float weaponRange = 15f; + + // aoe related + public boolean isAOE = true; + public boolean useCone = false; + public boolean usePointBlank = false; + public boolean useSphere = false; + public float radius; + public byte groupReq; // who the spell won't hit + public int maxNumMobTargets; + public int maxNumPlayerTargets; + + // chant related + public float chantDuration; + public int chantIterations; + + // valid target types from targetType field + public boolean targetPlayer = false; + public boolean targetMob = false; + public boolean targetPet = false; + public boolean targetNecroPet = false; + public boolean targetSelf = false; + public boolean targetWeapon = false; + public boolean targetCorpse = false; + public boolean targetBuilding = false; + public boolean targetGroup = false; + public boolean targetGuildLeader = false; + public boolean targetJewelry = false; + public boolean targetArmor = false; + public boolean targetItem = false; + + + // flags + public boolean isCasterFriendly = false; // from groupReq + public boolean isGroupFriendly = false; // from groupReq + public boolean isGroupOnly = false; // from groupReq + public boolean mustHitPets = false; // from groupReq + public boolean isNationFriendly = false; // from groupReq + public boolean targetFromLastTarget = false; // from unknown06 + public boolean targetFromSelf = false; // from unknown06 + public boolean targetFromName = false; // from unknown06 + public boolean targetFromNearbyMobs = false; // from unknown06 + public boolean useHealth = false; // from costType + public boolean useMana = false; // from costType + public boolean useStamina = false; // from costType + public boolean isSpell = true; // from skillOrSpell field + public boolean allowedInCombat = false; // from combat field + public boolean allowedOutOfCombat = false; // from combat field + public boolean regularPlayerCanCast = false; // from grantOverrideVar + public boolean hateRampAdd = true; // 1 bit flag + public boolean costRampAdd = true; // 2 bit flag + public boolean recycleRampAdd = true; // 4 bit flag + public boolean initRampAdd = true; // 8 bit flag + public boolean canCastWhileMoving = false; // 16 bit flag + public boolean canCastWhileFlying = false; // 32 bit flag + public boolean isChant = false; // 64 bit flag + public boolean losCheck = false; // 128 bit flag + public boolean sticky = false; // 256 bit flag + public boolean isAdminPower = false; // 512 bit flag + public boolean requiresHitRoll = false; // 1024 bit flag + public boolean isWeaponPower = false; // from category + public boolean isHeal = false; //from category + public boolean isTrack = false; //from category + public boolean isHarmful = true; + public boolean vampDrain = false; + + public boolean cancelOnCastSpell = false; + public boolean cancelOnTakeDamage = false; + + public final ArrayList monsterTypeRestrictions = new ArrayList<>(); + public final ArrayList actions = new ArrayList<>(); + public final ArrayList effectPrereqs = new ArrayList<>(); + public final ArrayList targetEffectPrereqs = new ArrayList<>(); + public final ArrayList equipPrereqs = new ArrayList<>(); + + public PowerTargetType targetType; + public PowerCategoryType powerCategory; + public String description; + + /** + * No Table ID Constructor + */ + public PowersBase() { + + } + + /** + * ResultSet Constructor + */ + public PowersBase(ResultSet rs) throws SQLException { + + this.UUID = rs.getInt("ID"); + this.name = rs.getString("name").trim(); + this.token = rs.getInt("token"); + this.skillName = rs.getString("skillName").trim(); + this.IDString = rs.getString("IDString").trim(); + this.isSpell = (rs.getString("skillOrSpell").equals("SPELL")) ? true : false; + this.skillID = rs.getInt("skillID"); + this.range = rs.getFloat("range"); + this.description = (rs.getString("description")).trim().replace("\r\n ", ""); + String ct = rs.getString("costType").trim(); + if (ct.equals("HEALTH")) + this.useHealth = true; + else if (ct.equals("MANA")) + this.useMana = true; + else if (ct.equals("STAMINA")) + this.useStamina = true; + ct = rs.getString("targetType").trim(); + if (ct.equals("BUILDING")) + this.targetBuilding = true; + else if (ct.equals("CORPSE")) + this.targetCorpse = true; + else if (ct.equals("GROUP")) + this.targetGroup = true; + else if (ct.equals("GUILDLEADER")) + this.targetGuildLeader = true; + else if (ct.equals("MOBILE")) { + this.targetMob = true; + this.targetPet = true; // sure on this one? + } else if (ct.equals("PC")) + this.targetPlayer = true; + else if (ct.equals("SELF")) + this.targetSelf = true; + else if (ct.equals("PET")) + this.targetPet = true; + else if (ct.equals("NECROPET")) + this.targetNecroPet = true; + else if (ct.equals("ARMOR")) + this.targetArmor = true; + else if (ct.equals("WEAPON")) + this.targetWeapon = true; + else if (ct.equals("JEWELRY")) + this.targetJewelry = true; + else if (ct.equals("ITEM")) { + this.targetItem = true; + this.targetJewelry = true; + this.targetArmor = true; + this.targetWeapon = true; + } else if (ct.equals("ARMORWEAPONJEWELRY")) { + this.targetArmor = true; + this.targetWeapon = true; + this.targetJewelry = true; + } else if (ct.equals("PCMOBILE")) { + this.targetPlayer = true; + this.targetMob = true; + this.targetPet = true; // sure on this one? + } else if (ct.equals("WEAPONARMOR")) { + this.targetWeapon = true; + this.targetArmor = true; + } else { + Logger.info("Missed " + ct + " targetType"); + } + String cat = rs.getString("category").trim(); + this.category = cat; + + + if (cat.isEmpty()) + this.powerCategory = PowerCategoryType.NONE; + else + this.powerCategory = PowerCategoryType.valueOf(cat.replace("-", "")); + + + + + if (cat.equals("WEAPON")) { + this.isWeaponPower = true; + this.isHarmful = false; + if (this.skillName.equals("Bow") || this.skillName.equals("Crossbow") || this.skillName.equals("Archery")) + this.weaponRange = 1000f; + else if (this.skillName.equals("Throwing")) + this.weaponRange = 60f; + else + this.weaponRange = 15f; + } else if (cat.equals("HEAL") || cat.equals("GROUPHEAL")) { + this.isHeal = true; + this.isHarmful = false; + } else if (cat.equals("TRACK")) { + this.isTrack = true; + this.isHarmful = false; + } else if (cat.equals("AE") || cat.equals("AEDAMAGE") || + cat.equals("BREAKFLY") || + cat.equals("DAMAGE") || cat.equals("DEBUFF") || + cat.equals("MOVE") || cat.equals("SPECIAL") || + cat.equals("SPIREDISABLE")) + this.isHarmful = true; + else if (cat.equals("CHANT")) { + this.isHarmful = ct.equals("MOBILE") || ct.equals("PC") || ct.equals("PCMOBILE"); + } else if (cat.equals("DISPEL")) { + //TODO this needs broken down better later + this.isHarmful = false; + } else if (cat.isEmpty()) { + if (ct.equals("MOBILE") || ct.equals("PCMOBILE")) + this.isHarmful = true; + else if (ct.equals("PC")) { + this.isHarmful = this.token != 429607195 && this.token != 429425915; + } else + this.isHarmful = false; + } else + this.isHarmful = false; + + if (cat.equals("VAMPDRAIN")) { + this.vampDrain = true; + this.isHarmful = true; + } + + this.cost = rs.getFloat("cost"); + this.costRamp = rs.getFloat("costRamp"); + this.castTime = rs.getFloat("castTime"); + this.castTimeRamp = rs.getFloat("initRamp"); + this.cooldown = rs.getFloat("cooldown"); + this.recycleTime = rs.getFloat("recycleTime"); + this.recycleRamp = rs.getFloat("recycleRamp"); + this.maxTrains = rs.getInt("maxTrains"); + this.hateValue = rs.getFloat("hateValue"); + this.hateRamp = rs.getFloat("hateRamp"); + ct = rs.getString("unknown06").trim(); + if (this.targetSelf) { + } else if (ct.equals("CLICK")) + if (!this.targetGroup) + this.targetFromLastTarget = true; + else if (ct.equals("NAME")) + this.targetFromName = true; + else if (ct.equals("NEARBYMOBS")) + this.targetFromNearbyMobs = true; + this.monsterTypePrereq = rs.getString("monsterTypePrereqs").trim(); + ct = rs.getString("radiusType").trim(); + if (ct.equals("CONE")) + this.useCone = true; + else if (ct.equals("POINTBLANK")) + this.usePointBlank = true; + else if (ct.equals("SPHERE")) + this.useSphere = true; + else + this.isAOE = false; + this.radius = rs.getFloat("radius"); + ct = rs.getString("groupReq").trim(); + if (ct.equals("CASTER")) + this.isCasterFriendly = true; + else if (ct.equals("GROUP")) { + this.isGroupFriendly = true; + this.isCasterFriendly = true; + } + else if (ct.equals("ALLBUTGROUP")) + this.isGroupOnly = true; + else if (ct.equals("ALLBUTPETS")) + this.mustHitPets = true; + else if (ct.equals("NATION")) { + this.isNationFriendly = true; + this.isCasterFriendly = true; + } + this.maxNumMobTargets = rs.getInt("maxNumMobTargets"); + this.maxNumPlayerTargets = rs.getInt("maxNumPlayerTargets"); + this.chantDuration = rs.getFloat("chantDuration"); + this.chantIterations = rs.getInt("chantIterations"); + ct = rs.getString("combat").trim(); + if (ct.equals("COMBAT")) + this.allowedInCombat = true; + else if (ct.equals("NONCOMBAT")) + this.allowedOutOfCombat = true; + else if (ct.equals("BOTH")) { + this.allowedInCombat = true; + this.allowedOutOfCombat = true; + } + ct = rs.getString("grantOverideVar").trim(); + if (ct.equals("PGOV_PLAYER")) + this.regularPlayerCanCast = true; + int flags = rs.getInt("flags"); + if ((flags & 1) == 0) + this.hateRampAdd = false; + if ((flags & 2) == 0) + this.costRampAdd = false; + if ((flags & 4) == 0) + this.recycleRampAdd = false; + if ((flags & 8) == 0) + this.initRampAdd = false; + if ((flags & 16) != 0) + this.canCastWhileMoving = true; + if ((flags & 32) != 0) + this.canCastWhileFlying = true; + if ((flags & 64) != 0) + this.isChant = true; + if ((flags & 128) != 0) + this.losCheck = true; + if ((flags & 256) != 0) + this.sticky = true; + if ((flags & 512) != 0) + this.isAdminPower = true; + if ((flags & 1024) != 0) + this.requiresHitRoll = true; + ct = rs.getString("monsterTypeRestrict1").trim(); + if (!ct.isEmpty()) + this.monsterTypeRestrictions.add(ct); + ct = rs.getString("monsterTypeRestrict2").trim(); + if (!ct.isEmpty()) + this.monsterTypeRestrictions.add(ct); + ct = rs.getString("monsterTypeRestrict3").trim(); + if (!ct.isEmpty()) + this.monsterTypeRestrictions.add(ct); + } + + public static ArrayList getAllPowersBase() { + PreparedStatementShared ps = null; + ArrayList out = new ArrayList<>(); + try { + ps = new PreparedStatementShared("SELECT * FROM static_power_powerbase"); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + PowersBase toAdd = new PowersBase(rs); + out.add(toAdd); + } + rs.close(); + } catch (Exception e) { + Logger.error( e.toString()); + } finally { + ps.release(); + } + return out; + } + + + public static void getFailConditions(HashMap powers) { + PreparedStatementShared ps = null; + try { + ps = new PreparedStatementShared("SELECT IDString, type FROM static_power_failcondition where powerOrEffect = 'Power'"); + ResultSet rs = ps.executeQuery(); + String type, IDString; PowersBase pb; + while (rs.next()) { + type = rs.getString("type"); + IDString = rs.getString("IDString"); + pb = powers.get(IDString); + if (pb != null) { + switch (type) { + case "CastSpell": + pb.cancelOnCastSpell = true; + break; + case "TakeDamage": + pb.cancelOnTakeDamage = true; + break; + } + }else{ + Logger.error("null power for Grief " + IDString); + } + } + rs.close(); + } catch (Exception e) { + Logger.error( e.toString()); + } finally { + ps.release(); + } + } + + + public String getName() { + return this.name; + } + + public int getMaxTrains() { + return this.maxTrains; + } + + public int getUUID() { + return this.UUID; + } + + public String getIDString() { + return this.IDString; + } + + public int getToken() { + if (this.IDString.equals("BLEED-DOT-10.5-RANGE")) + return -369682965; + return this.token; + } + + public int getCastTime(int trains) { // returns cast time in ms + if (this.initRampAdd) + return (int) ((this.castTime + (this.castTimeRamp * trains)) * 1000); + else + return (int) ((this.castTime * (1 + (this.castTimeRamp * trains))) * 1000); + } + + public int getRecycleTime(int trains) { // returns cast time in ms + if (this.recycleRampAdd) + return (int) (((this.recycleTime + (this.recycleRamp * trains)) * 1000) + getCastTime(trains)); + else + return (int) (((this.recycleTime * (1 + (this.recycleRamp * trains))) * 1000) + getCastTime(trains)); + } + + // public ArrayList getConditions() { + // return this.conditions; + // } + + public ArrayList getEffectPrereqs() { + return this.effectPrereqs; + } + + public ArrayList getTargetEffectPrereqs() { + return this.targetEffectPrereqs; + } + + public ArrayList getEquipPrereqs() { + return this.equipPrereqs; + } + + public ArrayList getActions() { + return this.actions; + } + + public boolean usePointBlank() { + return this.usePointBlank; + } + + public float getRadius() { + return this.radius; + } + + public int getMaxNumMobTargets() { + return this.maxNumMobTargets; + } + + public int getMaxNumPlayerTargets() { + return this.maxNumPlayerTargets; + } + + public boolean cancelOnCastSpell() { + return this.cancelOnCastSpell; + } + + public boolean cancelOnTakeDamage() { + return this.cancelOnTakeDamage; + } + + public boolean allowedInCombat() { + return this.allowedInCombat; + } + + public boolean allowedOutOfCombat() { + return this.allowedOutOfCombat; + } + + public boolean isCasterFriendly() { + return this.isCasterFriendly; + } + + public boolean isGroupFriendly() { + return this.isGroupFriendly; + } + + public boolean isNationFriendly() { + return this.isNationFriendly; + } + + public boolean isGroupOnly() { + return this.isGroupOnly; + } + + public boolean mustHitPets() { + return this.mustHitPets; + } + + public boolean targetFromLastTarget() { + return this.targetFromLastTarget; + } + + public boolean targetFromSelf() { + return this.targetFromSelf; + } + + public boolean targetFromName() { + return this.targetFromName; + } + + public boolean targetFromNearbyMobs() { + return this.targetFromNearbyMobs; + } + + public float getRange() { + return this.range; + } + + public boolean requiresHitRoll() { + return this.requiresHitRoll; + } + + public boolean regularPlayerCanCast() { + return this.regularPlayerCanCast; + } + + public boolean isSpell() { + return this.isSpell; + } + + public boolean isHarmful() { + return this.isHarmful; + } + + public boolean targetPlayer() { + return this.targetPlayer; + } + + public boolean targetMob() { + return this.targetMob; + } + + public boolean targetPet() { + return this.targetPet; + } + + public boolean targetNecroPet() { + return this.targetNecroPet; + } + + public boolean targetSelf() { + return this.targetSelf; + } + + public boolean targetCorpse() { + return this.targetCorpse; + } + + public boolean targetBuilding() { + return this.targetBuilding; + } + + public boolean targetGroup() { + return this.targetGroup; + } + + public boolean targetGuildLeader() { + return this.targetGuildLeader; + } + + public boolean targetJewelry() { + return this.targetJewelry; + } + + public boolean targetArmor() { + return this.targetArmor; + } + + public boolean targetWeapon() { + return this.targetWeapon; + } + + public boolean targetItem() { + return this.targetItem; + } + + public long getCooldown() { + return (long) (this.cooldown * 1000); // return + // in ms + } + + public boolean useHealth() { + return this.useHealth; + } + + public boolean useMana() { + return this.useMana; + } + + public boolean useStamina() { + return this.useStamina; + } + + public float getCost(int trains) { + if (this.costRampAdd) + return this.cost + (this.costRamp * trains); + else + return this.cost * (1 + (this.costRamp * trains)); + + } + + public float getHateValue() { + return this.hateValue; + } + + public float getHateRamp() { + return this.hateRamp; + } + + public float getHateValue(int trains) { + return this.hateValue + (this.hateRamp * trains); + } + + public boolean canCastWhileMoving() { + return this.canCastWhileMoving; + } + + public boolean canCastWhileFlying() { + return this.canCastWhileFlying; + } + + public boolean isAOE() { + return isAOE; + } + + public boolean isChant() { + return isChant; + } + + public int getChantIterations() { + return chantIterations; + } + + public float getChantDuration() { + return chantDuration; + } + + public boolean isWeaponPower() { + return isWeaponPower; + } + + public boolean isHeal() { + return isHeal; + } + + public boolean isTrack() { + return isTrack; + } + + public boolean vampDrain() { + return vampDrain; + } + + public void setCancelOnCastSpell(boolean value) { + this.cancelOnCastSpell = value; + } + + public void setCancelOnTakeDamage(boolean value) { + this.cancelOnTakeDamage = value; + } + + public String getSkillName() { + return this.skillName; + } + + public String getMonsterTypePrereq() { + return this.monsterTypePrereq; + } + + public String getCategory() { + return this.category; + } + + public float getWeaponRange() { + return this.weaponRange; + } + + public PowerCategoryType getPowerCategoryType(){ + return this.powerCategory; + } + + public String getDescription() { + return description; + } + +} diff --git a/src/engine/powers/RangeBasedAwo.java b/src/engine/powers/RangeBasedAwo.java new file mode 100644 index 00000000..b80c654e --- /dev/null +++ b/src/engine/powers/RangeBasedAwo.java @@ -0,0 +1,104 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers; + +import engine.Enum; +import engine.Enum.GameObjectType; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.PlayerCharacter; + +import java.util.HashSet; + + +//This package creates a list of AbstractWorldObjects +//sorted by range from a specified point. +public class RangeBasedAwo implements Comparable { + + private float range; + private AbstractWorldObject awo; + + public RangeBasedAwo(Float range, AbstractWorldObject awo) { + super(); + this.range = range; + this.awo = awo; + } + + public static HashSet createList(HashSet awolist, Vector3fImmutable searchLoc) { + HashSet rbal = new HashSet<>(); + for (AbstractWorldObject awo: awolist) { + RangeBasedAwo rba = new RangeBasedAwo(searchLoc.distance(awo.getLoc()), awo); + rbal.add(rba); + } + return rbal; + } + + @Override + public int compareTo(RangeBasedAwo obj) throws ClassCastException { + return (int)(this.range - obj.range); + } + + public static HashSet getSortedList(HashSet awolist, Vector3fImmutable searchLoc, int maxPlayers, int maxMobs) { + int playerCnt = 0; + int mobCnt = 0; + int maxCnt = (maxPlayers > maxMobs) ? maxPlayers : maxMobs; + HashSet rbal = RangeBasedAwo.createList(awolist, searchLoc); + awolist = new HashSet<>(); + for (RangeBasedAwo rba : rbal) { + if (awolist.size() >= maxCnt) + return awolist; + AbstractWorldObject awo = rba.awo; + + if (awo.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) { + if (playerCnt < maxPlayers) { + awolist.add(awo); + playerCnt++; + } + } else if (awo.getObjectType().equals(Enum.GameObjectType.Mob)) { + if (mobCnt < maxMobs) { + awolist.add(awo); + mobCnt++; + } + } + } + return awolist; + } + + public static HashSet getTrackList(HashSet awolist, PlayerCharacter pc, int max) { + Vector3fImmutable searchLoc = pc.getLoc(); + int cnt = 0; + HashSet rbal = RangeBasedAwo.createList(awolist, searchLoc); + HashSet aclist = new HashSet<>(); + for (RangeBasedAwo rba : rbal) { + if (aclist.size() >= max) + return aclist; + AbstractWorldObject awo = rba.awo; + + if (awo.getObjectType().equals(GameObjectType.PlayerCharacter)) + if (((PlayerCharacter)awo).isCSR()) + continue; + + if (AbstractWorldObject.IsAbstractCharacter(awo) && !(pc.equals(awo))) { + aclist.add((AbstractCharacter)awo); + cnt++; + } + } + return aclist; + } + + public float getRange() { + return this.range; + } + + public AbstractWorldObject getAwo() { + return this.awo; + } +} diff --git a/src/engine/powers/effectmodifiers/AbstractEffectModifier.java b/src/engine/powers/effectmodifiers/AbstractEffectModifier.java new file mode 100644 index 00000000..6a8f385d --- /dev/null +++ b/src/engine/powers/effectmodifiers/AbstractEffectModifier.java @@ -0,0 +1,373 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.Enum.ModType; +import engine.Enum.SourceType; +import engine.gameManager.DbManager; +import engine.gameManager.PowersManager; +import engine.jobs.AbstractEffectJob; +import engine.objects.*; +import engine.powers.EffectsBase; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashSet; + + +public abstract class AbstractEffectModifier { + + protected EffectsBase parent; + protected int UUID; + protected String IDString; + protected String effectType; + protected float minMod; + protected float maxMod; + protected float percentMod; + protected float ramp; + protected boolean useRampAdd; + protected String type; + public SourceType sourceType; + + protected String string1; + protected String string2; + public ModType modType; + + public AbstractEffectModifier(ResultSet rs) throws SQLException { + + this.UUID = rs.getInt("ID"); + this.IDString = rs.getString("IDString"); + this.effectType = rs.getString("modType"); + this.modType = ModType.GetModType(this.effectType); + this.type = rs.getString("type").replace("\"", ""); + this.sourceType = SourceType.GetSourceType(this.type.replace(" ", "").replace("-", "")); + this.minMod = rs.getFloat("minMod"); + this.maxMod = rs.getFloat("maxMod"); + this.percentMod = rs.getFloat("percentMod"); + this.ramp = rs.getFloat("ramp"); + this.useRampAdd = (rs.getInt("useRampAdd") == 1) ? true : false; + + this.string1 = rs.getString("string1"); + this.string2 = rs.getString("string2"); + } + + public static ArrayList getAllEffectModifiers() { + PreparedStatementShared ps = null; + ArrayList out = new ArrayList<>(); + try { + ps = new PreparedStatementShared("SELECT * FROM static_power_effectmod"); + ResultSet rs = ps.executeQuery(); + String IDString; + AbstractEffectModifier aem = null; + while (rs.next()) { + IDString = rs.getString("IDString"); + int token = DbManager.hasher.SBStringHash(IDString); + + EffectsBase eb = PowersManager.getEffectByIDString(IDString); + + ModType modifier = ModType.GetModType(rs.getString("modType")); + + //combine item prefix and suffix effect modifiers + switch (modifier){ + case AdjustAboveDmgCap: + aem = new AdjustAboveDmgCapEffectModifier(rs); + break; + case Ambidexterity: + aem = new AmbidexterityEffectModifier(rs); + break; + case AnimOverride: + break; + case ArmorPiercing: + aem = new ArmorPiercingEffectModifier(rs); + break; + case AttackDelay: + aem = new AttackDelayEffectModifier(rs); + break; + case Attr: + aem = new AttributeEffectModifier(rs); + break; + case BlackMantle: + aem = new BlackMantleEffectModifier(rs); + break; + case BladeTrails: + aem = new BladeTrailsEffectModifier(rs); + break; + case Block: + aem = new BlockEffectModifier(rs); + break; + case BlockedPowerType: + aem = new BlockedPowerTypeEffectModifier(rs); + break; + case CannotAttack: + aem = new CannotAttackEffectModifier(rs); + break; + case CannotCast: + aem = new CannotCastEffectModifier(rs); + break; + case CannotMove: + aem = new CannotMoveEffectModifier(rs); + break; + case CannotTrack: + aem = new CannotTrackEffectModifier(rs); + break; + case Charmed: + aem = new CharmedEffectModifier(rs); + break; + case ConstrainedAmbidexterity: + aem = new ConstrainedAmbidexterityEffectModifier(rs); + break; + case DamageCap: + aem = new DamageCapEffectModifier(rs); + break; + case DamageShield: + aem = new DamageShieldEffectModifier(rs); + break; + case DCV: + aem = new DCVEffectModifier(rs); + break; + case Dodge: + aem = new DodgeEffectModifier(rs); + break; + case DR: + aem = new DREffectModifier(rs); + break; + case Durability: + aem = new DurabilityEffectModifier(rs); + break; + case ExclusiveDamageCap: + aem = new ExclusiveDamageCapEffectModifier(rs); + break; + case Fade: + aem = new FadeEffectModifier(rs); + break; + case Fly: + aem = new FlyEffectModifier(rs); + break; + case Health: + aem = new HealthEffectModifier(rs); + break; + case HealthFull: + aem = new HealthFullEffectModifier(rs); + break; + case HealthRecoverRate: + aem = new HealthRecoverRateEffectModifier(rs); + break; + case IgnoreDamageCap: + aem = new IgnoreDamageCapEffectModifier(rs); + break; + case IgnorePassiveDefense: + aem = new IgnorePassiveDefenseEffectModifier(rs); + break; + case ImmuneTo: + aem = new ImmuneToEffectModifier(rs); + break; + case ImmuneToAttack: + aem = new ImmuneToAttackEffectModifier(rs); + break; + case ImmuneToPowers: + aem = new ImmuneToPowersEffectModifier(rs); + break; + case Invisible: + aem = new InvisibleEffectModifier(rs); + break; + case ItemName: + aem = new ItemNameEffectModifier(rs); + if ((((ItemNameEffectModifier)aem).name.isEmpty())) + break; + if (eb != null) + eb.setName((((ItemNameEffectModifier)aem).name)); + break; + case Mana: + aem = new ManaEffectModifier(rs); + break; + case ManaFull: + aem = new ManaFullEffectModifier(rs); + break; + case ManaRecoverRate: + aem = new ManaRecoverRateEffectModifier(rs); + break; + case MaxDamage: + aem = new MaxDamageEffectModifier(rs); + break; + case MeleeDamageModifier: + aem = new MeleeDamageEffectModifier(rs); + break; + case MinDamage: + aem = new MinDamageEffectModifier(rs); + break; + case NoMod: + aem = new NoModEffectModifier(rs); + break; + case OCV: + aem = new OCVEffectModifier(rs); + break; + case Parry: + aem = new ParryEffectModifier(rs); + break; + case PassiveDefense: + aem = new PassiveDefenseEffectModifier(rs); + case PowerCost: + aem = new PowerCostEffectModifier(rs); + break; + case PowerCostHealth: + aem = new PowerCostHealthEffectModifier(rs); + break; + case PowerDamageModifier: + aem = new PowerDamageEffectModifier(rs); + break; + case ProtectionFrom: + aem = new ProtectionFromEffectModifier(rs); + break; + case Resistance: + aem = new ResistanceEffectModifier(rs); + break; + case ScaleHeight: + aem = new ScaleHeightEffectModifier(rs); + break; + case ScaleWidth: + aem = new ScaleWidthEffectModifier(rs); + break; + case ScanRange: + aem = new ScanRangeEffectModifier(rs); + break; + case SeeInvisible: + aem = new SeeInvisibleEffectModifier(rs); + break; + case Silenced: + aem = new SilencedEffectModifier(rs); + break; + case Skill: + aem = new SkillEffectModifier(rs); + break; + case Slay: + aem = new SlayEffectModifier(rs); + break; + case Speed: + aem = new SpeedEffectModifier(rs); + break; + case SpireBlock: + aem = new SpireBlockEffectModifier(rs); + break; + case Stamina: + aem = new StaminaEffectModifier(rs); + break; + case StaminaFull: + aem = new StaminaFullEffectModifier(rs); + break; + case StaminaRecoverRate: + aem = new StaminaRecoverRateEffectModifier(rs); + break; + case Stunned: + aem = new StunnedEffectModifier(rs); + break; + case Value: + aem = new ValueEffectModifier(rs); + if (eb != null){ + ValueEffectModifier valueEffect = (ValueEffectModifier)aem; + eb.setValue(valueEffect.minMod); + } + break; + case WeaponProc: + aem = new WeaponProcEffectModifier(rs); + break; + case WeaponRange: + aem = new WeaponRangeEffectModifier(rs); + break; + case WeaponSpeed: + aem = new WeaponSpeedEffectModifier(rs); + break; + + } + + if (aem != null){ + + + if (EffectsBase.modifiersMap.containsKey(eb.getIDString()) == false) + EffectsBase.modifiersMap.put(eb.getIDString(), new HashSet<>()); + EffectsBase.modifiersMap.get(eb.getIDString()).add(aem); + + } + } + rs.close(); + } catch (Exception e) { + Logger.error( e); + } finally { + ps.release(); + } + return out; + } + + + + + + + + public int getUUID() { + return this.UUID; + } + + // public String getIDString() { + // return this.IDString; + // } + + public String getmodType() { + return this.effectType; + } + + public float getMinMod() { + return this.minMod; + } + + public float getMaxMod() { + return this.maxMod; + } + + public float getPercentMod() { + return this.percentMod; + } + + public float getRamp() { + return this.ramp; + } + + public String getType() { + return this.type; + } + + public String getString1() { + return this.string1; + } + + public String getString2() { + return this.string2; + } + + public EffectsBase getParent() { + return this.parent; + } + + public void setParent(EffectsBase value) { + this.parent = value; + } + + public void applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + _applyEffectModifier(source, awo, trains, effect); + } + + protected abstract void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect); + + public abstract void applyBonus(AbstractCharacter ac, int trains); + public abstract void applyBonus(Item item, int trains); + public abstract void applyBonus(Building building, int trains); +} diff --git a/src/engine/powers/effectmodifiers/AdjustAboveDmgCapEffectModifier.java b/src/engine/powers/effectmodifiers/AdjustAboveDmgCapEffectModifier.java new file mode 100644 index 00000000..bf64a6c0 --- /dev/null +++ b/src/engine/powers/effectmodifiers/AdjustAboveDmgCapEffectModifier.java @@ -0,0 +1,49 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class AdjustAboveDmgCapEffectModifier extends AbstractEffectModifier { + + public AdjustAboveDmgCapEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount / 100; + PlayerBonuses bonus = ac.getBonuses(); + bonus.setFloat(this, amount); + + + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/AmbidexterityEffectModifier.java b/src/engine/powers/effectmodifiers/AmbidexterityEffectModifier.java new file mode 100644 index 00000000..44f72554 --- /dev/null +++ b/src/engine/powers/effectmodifiers/AmbidexterityEffectModifier.java @@ -0,0 +1,42 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.Enum.ModType; +import engine.Enum.SourceType; +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class AmbidexterityEffectModifier extends AbstractEffectModifier { + + public AmbidexterityEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + PlayerBonuses bonus = ac.getBonuses(); + bonus.setBool(ModType.Ambidexterity, SourceType.None, true); + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/ArmorPiercingEffectModifier.java b/src/engine/powers/effectmodifiers/ArmorPiercingEffectModifier.java new file mode 100644 index 00000000..29ee21fe --- /dev/null +++ b/src/engine/powers/effectmodifiers/ArmorPiercingEffectModifier.java @@ -0,0 +1,42 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Building; +import engine.objects.Item; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class ArmorPiercingEffectModifier extends AbstractEffectModifier { + + public ArmorPiercingEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/AttackDelayEffectModifier.java b/src/engine/powers/effectmodifiers/AttackDelayEffectModifier.java new file mode 100644 index 00000000..2ce2f6a3 --- /dev/null +++ b/src/engine/powers/effectmodifiers/AttackDelayEffectModifier.java @@ -0,0 +1,46 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class AttackDelayEffectModifier extends AbstractEffectModifier { + + public AttackDelayEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + PlayerBonuses bonus = ac.getBonuses(); + bonus.addFloat(this, amount); + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/AttributeEffectModifier.java b/src/engine/powers/effectmodifiers/AttributeEffectModifier.java new file mode 100644 index 00000000..4aceb60c --- /dev/null +++ b/src/engine/powers/effectmodifiers/AttributeEffectModifier.java @@ -0,0 +1,56 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class AttributeEffectModifier extends AbstractEffectModifier { + + public AttributeEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + + ac.update(); + Float amount = 0f; + PlayerBonuses bonus = ac.getBonuses(); + if (this.percentMod != 0f) { //Stat Percent Modifiers + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + bonus.addFloat(this, amount); + } else { //Stat Modifiers + if (this.useRampAdd) + amount = this.minMod + (this.ramp * trains); + else + amount = this.minMod * (1 + (this.ramp * trains)); + bonus.addFloat(this, amount); + } + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/BlackMantleEffectModifier.java b/src/engine/powers/effectmodifiers/BlackMantleEffectModifier.java new file mode 100644 index 00000000..1a6d7402 --- /dev/null +++ b/src/engine/powers/effectmodifiers/BlackMantleEffectModifier.java @@ -0,0 +1,53 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.Enum.ModType; +import engine.Enum.SourceType; +import engine.jobs.AbstractEffectJob; +import engine.objects.*; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class BlackMantleEffectModifier extends AbstractEffectModifier { + + public BlackMantleEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + PlayerBonuses bonus = ac.getBonuses(); + SourceType sourceType = SourceType.valueOf(this.type); + + if (sourceType == null){ + Logger.error("Bad Source Type for " + this.type); + return; + } + + if (this.type.equals("Heal")) + bonus.setFloat(this, trains); + else + bonus.setBool(ModType.ImmuneTo, this.sourceType, true); + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/BladeTrailsEffectModifier.java b/src/engine/powers/effectmodifiers/BladeTrailsEffectModifier.java new file mode 100644 index 00000000..891e3781 --- /dev/null +++ b/src/engine/powers/effectmodifiers/BladeTrailsEffectModifier.java @@ -0,0 +1,41 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Building; +import engine.objects.Item; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class BladeTrailsEffectModifier extends AbstractEffectModifier { + + public BladeTrailsEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/BlockEffectModifier.java b/src/engine/powers/effectmodifiers/BlockEffectModifier.java new file mode 100644 index 00000000..b388dac1 --- /dev/null +++ b/src/engine/powers/effectmodifiers/BlockEffectModifier.java @@ -0,0 +1,46 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class BlockEffectModifier extends AbstractEffectModifier { + + public BlockEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + PlayerBonuses bonus = ac.getBonuses(); + bonus.setFloat(this, amount); + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/BlockedPowerTypeEffectModifier.java b/src/engine/powers/effectmodifiers/BlockedPowerTypeEffectModifier.java new file mode 100644 index 00000000..3e447d31 --- /dev/null +++ b/src/engine/powers/effectmodifiers/BlockedPowerTypeEffectModifier.java @@ -0,0 +1,62 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.Enum.ModType; +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashSet; + + +public class BlockedPowerTypeEffectModifier extends AbstractEffectModifier { + + public BlockedPowerTypeEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + PlayerBonuses bonus = ac.getBonuses(); + bonus.setBool(this.modType,this.sourceType, true); + + + for (String effect : ac.getEffects().keySet()){ + Effect eff = ac.getEffects().get(effect); + ModType toBlock = ModType.None; + + switch (this.sourceType){ + case Invisible: + toBlock = ModType.Invisible; + break; + } + + HashSet aemList = eff.getEffectModifiers(); + for (AbstractEffectModifier aem : aemList ){ + if (aem.modType.equals(toBlock)){ + ac.endEffect(effect); + } + } + + } + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/CannotAttackEffectModifier.java b/src/engine/powers/effectmodifiers/CannotAttackEffectModifier.java new file mode 100644 index 00000000..86435c20 --- /dev/null +++ b/src/engine/powers/effectmodifiers/CannotAttackEffectModifier.java @@ -0,0 +1,41 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class CannotAttackEffectModifier extends AbstractEffectModifier { + + public CannotAttackEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + PlayerBonuses bonus = ac.getBonuses(); + + bonus.setBool(this.modType,this.sourceType, true); + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/CannotCastEffectModifier.java b/src/engine/powers/effectmodifiers/CannotCastEffectModifier.java new file mode 100644 index 00000000..3dca97b7 --- /dev/null +++ b/src/engine/powers/effectmodifiers/CannotCastEffectModifier.java @@ -0,0 +1,46 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.Enum; +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class CannotCastEffectModifier extends AbstractEffectModifier { + + public CannotCastEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + + if (ac.getObjectType().equals(Enum.GameObjectType.Mob)) { + Mob mob = (Mob) ac; + } + + PlayerBonuses bonus = ac.getBonuses(); + bonus.setBool(this.modType,this.sourceType, true); + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/CannotMoveEffectModifier.java b/src/engine/powers/effectmodifiers/CannotMoveEffectModifier.java new file mode 100644 index 00000000..51fe46cb --- /dev/null +++ b/src/engine/powers/effectmodifiers/CannotMoveEffectModifier.java @@ -0,0 +1,41 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class CannotMoveEffectModifier extends AbstractEffectModifier { + + public CannotMoveEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + PlayerBonuses bonus = ac.getBonuses(); + bonus.setBool(this.modType,this.sourceType,true); + ac.stopMovement(ac.getMovementLoc()); + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/CannotTrackEffectModifier.java b/src/engine/powers/effectmodifiers/CannotTrackEffectModifier.java new file mode 100644 index 00000000..a4e6a4c0 --- /dev/null +++ b/src/engine/powers/effectmodifiers/CannotTrackEffectModifier.java @@ -0,0 +1,40 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class CannotTrackEffectModifier extends AbstractEffectModifier { + + public CannotTrackEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + PlayerBonuses bonus = ac.getBonuses(); + bonus.setBool(this.modType,this.sourceType,true); + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/CharmedEffectModifier.java b/src/engine/powers/effectmodifiers/CharmedEffectModifier.java new file mode 100644 index 00000000..324dde83 --- /dev/null +++ b/src/engine/powers/effectmodifiers/CharmedEffectModifier.java @@ -0,0 +1,41 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Building; +import engine.objects.Item; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class CharmedEffectModifier extends AbstractEffectModifier { + + public CharmedEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/ConstrainedAmbidexterityEffectModifier.java b/src/engine/powers/effectmodifiers/ConstrainedAmbidexterityEffectModifier.java new file mode 100644 index 00000000..22c7e497 --- /dev/null +++ b/src/engine/powers/effectmodifiers/ConstrainedAmbidexterityEffectModifier.java @@ -0,0 +1,40 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class ConstrainedAmbidexterityEffectModifier extends AbstractEffectModifier { + + public ConstrainedAmbidexterityEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + PlayerBonuses bonus = ac.getBonuses(); + bonus.setString(this,this.type); + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/DCVEffectModifier.java b/src/engine/powers/effectmodifiers/DCVEffectModifier.java new file mode 100644 index 00000000..29dcd20e --- /dev/null +++ b/src/engine/powers/effectmodifiers/DCVEffectModifier.java @@ -0,0 +1,54 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class DCVEffectModifier extends AbstractEffectModifier { + + public DCVEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + PlayerBonuses bonus = ac.getBonuses(); + if (this.percentMod != 0f) { //Stat Percent Modifiers + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = (amount) / 100; + bonus.addFloat(this, amount); + } else { //Stat Modifiers + if (this.useRampAdd) + amount = this.minMod + (this.ramp * trains); + else + amount = this.minMod * (1 + (this.ramp * trains)); + bonus.addFloat(this,amount); + } + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/DREffectModifier.java b/src/engine/powers/effectmodifiers/DREffectModifier.java new file mode 100644 index 00000000..54b606a5 --- /dev/null +++ b/src/engine/powers/effectmodifiers/DREffectModifier.java @@ -0,0 +1,61 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Building; +import engine.objects.Item; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class DREffectModifier extends AbstractEffectModifier { + + public DREffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + //Defense Rating (defense bonus for armor) + } + + @Override + public void applyBonus(Item item, int trains) { + if (item == null) + return; + String key; float amount = 0f; + if (this.percentMod != 0f) { + if (this.useRampAdd) + amount = (this.percentMod + (this.ramp * trains)) / 100f; + else + amount = (this.percentMod * (1 + (this.ramp * trains))) / 100f; + amount = amount/100; + key = "DR.percent"; + } else { + if (this.useRampAdd) + amount = this.minMod + (this.ramp * trains); + else + amount = this.minMod * (1 + (this.ramp * trains)); + key = "DR"; + } + item.addBonus(this, amount); + } + + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/DamageCapEffectModifier.java b/src/engine/powers/effectmodifiers/DamageCapEffectModifier.java new file mode 100644 index 00000000..905805f2 --- /dev/null +++ b/src/engine/powers/effectmodifiers/DamageCapEffectModifier.java @@ -0,0 +1,46 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class DamageCapEffectModifier extends AbstractEffectModifier { + + public DamageCapEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + PlayerBonuses bonus = ac.getBonuses(); + bonus.setFloat(this, amount); + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/DamageShieldEffectModifier.java b/src/engine/powers/effectmodifiers/DamageShieldEffectModifier.java new file mode 100644 index 00000000..1a4bf094 --- /dev/null +++ b/src/engine/powers/effectmodifiers/DamageShieldEffectModifier.java @@ -0,0 +1,64 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.Enum.DamageType; +import engine.jobs.AbstractEffectJob; +import engine.objects.*; +import engine.powers.DamageShield; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class DamageShieldEffectModifier extends AbstractEffectModifier { + + public DamageShieldEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + float amount; boolean usePercent; + if (this.percentMod != 0) { + amount = this.percentMod; + usePercent = true; + } else { + amount = this.minMod; + usePercent = false; + } + + if (this.ramp > 0f) { + float mod = this.ramp * trains; + if (this.useRampAdd) + amount += mod; + else + amount *= (1 + mod); + } + + DamageType dt = DamageType.valueOf(this.type); + if (dt != null) { + DamageShield ds = new DamageShield(dt, amount, usePercent); + PlayerBonuses bonus = ac.getBonuses(); + if (bonus != null) + bonus.addDamageShield(this, ds); + } + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/DodgeEffectModifier.java b/src/engine/powers/effectmodifiers/DodgeEffectModifier.java new file mode 100644 index 00000000..5580f7f0 --- /dev/null +++ b/src/engine/powers/effectmodifiers/DodgeEffectModifier.java @@ -0,0 +1,54 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class DodgeEffectModifier extends AbstractEffectModifier { + + public DodgeEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + PlayerBonuses bonus = ac.getBonuses(); + if (this.percentMod != 0f) { //Stat Percent Modifiers + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + bonus.addFloat(this, amount); + } else { //Stat Modifiers + if (this.useRampAdd) + amount = this.minMod + (this.ramp * trains); + else + amount = this.minMod * (1 + (this.ramp * trains)); + bonus.addFloat(this, amount); + } + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/DurabilityEffectModifier.java b/src/engine/powers/effectmodifiers/DurabilityEffectModifier.java new file mode 100644 index 00000000..2af78999 --- /dev/null +++ b/src/engine/powers/effectmodifiers/DurabilityEffectModifier.java @@ -0,0 +1,41 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Building; +import engine.objects.Item; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class DurabilityEffectModifier extends AbstractEffectModifier { + + public DurabilityEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/ExclusiveDamageCapEffectModifier.java b/src/engine/powers/effectmodifiers/ExclusiveDamageCapEffectModifier.java new file mode 100644 index 00000000..0a652cef --- /dev/null +++ b/src/engine/powers/effectmodifiers/ExclusiveDamageCapEffectModifier.java @@ -0,0 +1,45 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashSet; + +public class ExclusiveDamageCapEffectModifier extends AbstractEffectModifier { + + public ExclusiveDamageCapEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + PlayerBonuses bonus = ac.getBonuses(); + if (bonus == null) + return; + if (bonus.getList(this.modType) == null) + bonus.setList(this.modType, new HashSet<>()); + bonus.getList(this.modType).add(this.sourceType); + + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/FadeEffectModifier.java b/src/engine/powers/effectmodifiers/FadeEffectModifier.java new file mode 100644 index 00000000..2e12d645 --- /dev/null +++ b/src/engine/powers/effectmodifiers/FadeEffectModifier.java @@ -0,0 +1,41 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Building; +import engine.objects.Item; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class FadeEffectModifier extends AbstractEffectModifier { + + public FadeEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/FlyEffectModifier.java b/src/engine/powers/effectmodifiers/FlyEffectModifier.java new file mode 100644 index 00000000..3e16ca7c --- /dev/null +++ b/src/engine/powers/effectmodifiers/FlyEffectModifier.java @@ -0,0 +1,39 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class FlyEffectModifier extends AbstractEffectModifier { + + public FlyEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + PlayerBonuses bonus = ac.getBonuses(); + bonus.setBool(this.modType,this.sourceType,true); + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/HealthEffectModifier.java b/src/engine/powers/effectmodifiers/HealthEffectModifier.java new file mode 100644 index 00000000..97ef9462 --- /dev/null +++ b/src/engine/powers/effectmodifiers/HealthEffectModifier.java @@ -0,0 +1,331 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.Enum.DamageType; +import engine.Enum.GameObjectType; +import engine.Enum.ModType; +import engine.Enum.SourceType; +import engine.gameManager.ChatManager; +import engine.jobs.AbstractEffectJob; +import engine.jobs.DamageOverTimeJob; +import engine.net.AbstractNetMsg; +import engine.net.DispatchMessage; +import engine.net.client.msg.ModifyHealthKillMsg; +import engine.net.client.msg.ModifyHealthMsg; +import engine.objects.*; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.concurrent.ThreadLocalRandom; + +public class HealthEffectModifier extends AbstractEffectModifier { + + private DamageType damageType; + + public HealthEffectModifier(ResultSet rs) throws SQLException { + super(rs); + String damageTypeDB = rs.getString("type"); + try { + this.damageType = DamageType.valueOf(damageTypeDB); + } catch (IllegalArgumentException e) { + Logger.error("DamageType could not be loaded from database. " + "UUID = " + this.UUID + + " value received = '" + damageTypeDB + '\'', e); + } + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + if (awo == null) { + Logger.error("_applyEffectModifier(): NULL AWO passed in."); + return; + } + + if (effect == null) { + Logger.error( "_applyEffectModifier(): NULL AbstractEffectJob passed in."); + return; + } + + float modAmount = 0f; + + // Modify health by percent + if (this.percentMod != 0f) { + + //high level mobs/players should not be %damaged/healed. + if (awo.getHealthMax() > 25000f && (this.percentMod < 0f || this.percentMod > 5f)) + return; + + float mod = 1f; + if (this.useRampAdd) + mod = (this.percentMod + (this.ramp * trains)) / 100; + else + mod = (this.percentMod * (1 + (this.ramp * trains))) / 100; + modAmount = mod * awo.getHealthMax(); + if (AbstractWorldObject.IsAbstractCharacter(awo)) { + if (((AbstractCharacter)awo).isSit()) + modAmount *= 2.5f; + } + + //debug for spell damage and atr + if (source.getDebug(16) && source.getObjectType().equals(GameObjectType.PlayerCharacter)) { + PlayerCharacter pc = (PlayerCharacter) source; + String smsg = "Percent Damage: " + mod * 100 + '%'; + ChatManager.chatSystemInfo(pc, smsg); + } + } + + // Modify health by min/max amount + else if (this.minMod != 0f || this.maxMod != 0f) { + float min = this.minMod; + float max = this.maxMod; + if (this.ramp > 0f) { + float mod = this.ramp * trains; + if (this.useRampAdd) { + min += mod; + max += mod; + } else { + min *= (1 + mod); + max *= (1 + mod); + } + } + if (source.getObjectType().equals(GameObjectType.PlayerCharacter)) { + PlayerCharacter pc = (PlayerCharacter) source; + + float focus; + CharacterSkill skill = pc.getSkills().get(effect.getPower().getSkillName()); + if (skill == null) + focus = CharacterSkill.getQuickMastery(pc, effect.getPower().getSkillName()); + else + focus = skill.getModifiedAmount(); + //TODO clean up old formulas once new one is verified + // min *= (0.5 + 0.0075 * pc.getStatIntCurrent() + 0.011 * pc.getStatSpiCurrent() + 0.0196 * focus); + // max *= (0.62 + 0.0192 * pc.getStatIntCurrent() + 0.00415 * pc.getStatSpiCurrent() + 0.015 * focus); + float intt = (pc.getStatIntCurrent() >= 1) ? (float)pc.getStatIntCurrent() : 1f; + float spi = (pc.getStatSpiCurrent() >= 1) ? (float)pc.getStatSpiCurrent() : 1f; + // min *= (intt * 0.0045 + 0.055 * (float)Math.sqrt(intt - 0.5) + spi * 0.006 + 0.07 * (float)Math.sqrt(spi - 0.5) + 0.02 * (int)focus); + // max *= (intt * 0.0117 + 0.13 * (float)Math.sqrt(intt - 0.5) + spi * 0.0024 + (float)Math.sqrt(spi - 0.5) * 0.021 + 0.015 * (int)focus); + min = HealthEffectModifier.getMinDamage(min, intt, spi, focus); + max = HealthEffectModifier.getMaxDamage(max, intt, spi, focus); + + //debug for spell damage and atr + if (pc.getDebug(16)) { + String smsg = "Damage: " + (int)Math.abs(min) + " - " + (int)Math.abs(max); + ChatManager.chatSystemInfo(pc, smsg); + } + }else if (source.getObjectType() == GameObjectType.Mob){ + Mob pc = (Mob) source; + + float focus; + CharacterSkill skill = pc.getSkills().get(effect.getPower().getSkillName()); + if (skill == null) + focus = CharacterSkill.getQuickMastery(pc, effect.getPower().getSkillName()); + else + focus = skill.getModifiedAmount(); + //TODO clean up old formulas once new one is verified + // min *= (0.5 + 0.0075 * pc.getStatIntCurrent() + 0.011 * pc.getStatSpiCurrent() + 0.0196 * focus); + // max *= (0.62 + 0.0192 * pc.getStatIntCurrent() + 0.00415 * pc.getStatSpiCurrent() + 0.015 * focus); + float intt = (pc.getStatIntCurrent() >= 1) ? (float)pc.getStatIntCurrent() : 1f; + + if (pc.isPlayerGuard()) + intt = 200; + float spi = (pc.getStatSpiCurrent() >= 1) ? (float)pc.getStatSpiCurrent() : 1f; + + if (pc.isPlayerGuard()) + spi = 200; + // min *= (intt * 0.0045 + 0.055 * (float)Math.sqrt(intt - 0.5) + spi * 0.006 + 0.07 * (float)Math.sqrt(spi - 0.5) + 0.02 * (int)focus); + // max *= (intt * 0.0117 + 0.13 * (float)Math.sqrt(intt - 0.5) + spi * 0.0024 + (float)Math.sqrt(spi - 0.5) * 0.021 + 0.015 * (int)focus); + min = HealthEffectModifier.getMinDamage(min, intt, spi, focus); + max = HealthEffectModifier.getMaxDamage(max, intt, spi, focus); + + //debug for spell damage and atr + // if (pc.getDebug(16)) { + // String smsg = "Damage: " + (int)Math.abs(min) + " - " + (int)Math.abs(max); + // ChatManager.chatSystemInfo(pc, smsg); + // } + } + modAmount = calculateDamage(source, min, max, awo, trains); + PlayerBonuses bonus = source.getBonuses(); + + // Apply any power effect modifiers (such as stances) + if (bonus != null) + modAmount *= (1 + (bonus.getFloatPercentAll(ModType.PowerDamageModifier, SourceType.None))); + } + if (modAmount == 0f) + return; + if (AbstractWorldObject.IsAbstractCharacter(awo)) { + AbstractCharacter ac = (AbstractCharacter) awo; + + if (!ac.isAlive()) + return; + + int powerID = 0, effectID = 0; + String powerName = ""; + if (effect.getPower() != null) { + powerID = effect.getPower().getToken(); + powerName = effect.getPower().getName(); + } else { + Logger.error("Power has returned null! Damage will fail to register! (" + (ac.getCurrentHitpoints()>0?"Alive)":"Dead)")); + } + + if (effect.getEffect() != null) { + effectID = effect.getEffect().getToken(); + } else { + Logger.error("Effect has returned null! Damage will fail to register! (" + (ac.getCurrentHitpoints()>0?"Alive)":"Dead)")); + } + + //see if target is immune to heals + if (modAmount > 0f) { + boolean skipImmune = false; + // first tick of HoT going thru SM was removed in a later patch + /*if (effect.getAction().getPowerAction() instanceof DirectDamagePowerAction) { + ArrayList actions = effect.getPower().getActions(); + for (ActionsBase ab : actions) { + AbstractPowerAction apa = ab.getPowerAction(); + if (apa instanceof DamageOverTimePowerAction) + skipImmune = true; + } + }*/ + + PlayerBonuses bonus = ac.getBonuses(); + if (!skipImmune && bonus.getFloat(ModType.BlackMantle, SourceType.Heal) >= trains) { + ModifyHealthMsg mhm = new ModifyHealthMsg(source, ac, 0f, 0f, 0f, powerID, powerName, trains, effectID); + mhm.setUnknown03(5); //set target is immune + DispatchMessage.sendToAllInRange(ac, mhm); + return; + } + } + float mod = 0; + + //Modify health + + mod = ac.modifyHealth(modAmount, source, false); + + float cur = awo.getCurrentHitpoints(); + float maxAmount = awo.getHealthMax() - cur; + + AbstractNetMsg mhm = null; + if (modAmount < 0 && cur < 0 && mod != 0) + mhm = new ModifyHealthKillMsg(source, ac, modAmount, 0f, 0f, powerID, powerName, trains, effectID); + else + mhm = new ModifyHealthMsg(source, ac, modAmount, 0f, 0f, powerID, powerName, trains, effectID); + + if (effect instanceof DamageOverTimeJob) { + if (mhm instanceof ModifyHealthMsg) + ((ModifyHealthMsg)mhm).setOmitFromChat(1); + else if (mhm instanceof ModifyHealthKillMsg) + ((ModifyHealthKillMsg)mhm).setUnknown02(1); + } + + //send the damage + + DispatchMessage.sendToAllInRange(ac, mhm); + + // //send corpse if this kills a mob + // //TODO fix the someone misses blurb. + // if(awo instanceof Mob && awo.getHealth() <= 0) { + // CombatMessageMsg cmm = new CombatMessageMsg(null, 0, awo, 15); + // try { + // DispatchMessage.sendToAllInRange(ac, cmm); + // } catch (MsgSendException e) { + // Logger.error("MobCorpseSendError", e); + // } + // } + } else if (awo.getObjectType().equals(GameObjectType.Building)) { + + Building b = (Building) awo; + + if (modAmount < 0 && (!b.isVulnerable())) + return; //can't damage invul building + + int powerID = 0, effectID = 0; + String powerName = ""; + if (effect.getPower() != null) { + powerID = effect.getPower().getToken(); + powerName = effect.getPower().getName(); + } else + Logger.error("Power has returned null! Damage will fail to register! (" + (b.getRank() == -1 ? "Standing)" : "Destroyed)")); + + if (effect.getEffect() != null) { + effectID = effect.getEffect().getToken(); + } else + Logger.error("Effect has returned null! Damage will fail to register! (" + (b.getRank() == -1 ? "Standing)" : "Destroyed)")); + + float mod = b.modifyHealth(modAmount, source); + ModifyHealthMsg mhm = new ModifyHealthMsg(source, b, modAmount, 0f, 0f, powerID, powerName, trains, effectID); + + if (effect instanceof DamageOverTimeJob) + mhm.setOmitFromChat(1); + + //send the damage + + DispatchMessage.sendToAllInRange(b, mhm); + + } + } + + private float calculateDamage(AbstractCharacter source, float minDamage, float maxDamage, AbstractWorldObject awo, int trains) { + + // get range between min and max + float range = maxDamage - minDamage; + + // Damage is calculated twice to average a more central point + float damage = ThreadLocalRandom.current().nextFloat() * range; + damage = (damage + (ThreadLocalRandom.current().nextFloat() * range)) / 2; + + // put it back between min and max + damage += minDamage; + + Resists resists = null; + // get resists + if (AbstractWorldObject.IsAbstractCharacter(awo)) { + AbstractCharacter ac = (AbstractCharacter) awo; + resists = ac.getResists(); + } else if (awo.getObjectType().equals(GameObjectType.Building)) + resists = ((Building) awo).getResists(); + + // calculate resists in if any + if (resists != null) { + if (AbstractWorldObject.IsAbstractCharacter(awo)) + damage = resists.getResistedDamage(source, (AbstractCharacter) awo, damageType, damage * -1, trains) * -1; + else + damage = resists.getResistedDamage(source, null, damageType, damage * -1, trains) * -1; + } + + if (AbstractWorldObject.IsAbstractCharacter(awo)) { + AbstractCharacter ac = (AbstractCharacter) awo; + if (ac.isSit()) + damage *= 2.5f; // increase damage if sitting + } + + return damage; + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} + + public static float getMinDamage(float baseMin, float intelligence, float spirit, float focus) { + float min = baseMin * (((float)Math.pow(intelligence, 0.75f) * 0.0311f) + (0.02f * (int)focus) + ((float)Math.pow(spirit, 0.75f) * 0.0416f)); + return (float)((int)(min + 0.5f)); //round to nearest whole number + } + + public static float getMaxDamage(float baseMax, float intelligence, float spirit, float focus) { + float max = baseMax * (((float)Math.pow(intelligence, 0.75f) * 0.0785f) + (0.015f * (int)focus) + ((float)Math.pow(spirit, 0.75f) * 0.0157f)); + return (float)((int)(max + 0.5f)); //round to nearest whole number + } + +} diff --git a/src/engine/powers/effectmodifiers/HealthFullEffectModifier.java b/src/engine/powers/effectmodifiers/HealthFullEffectModifier.java new file mode 100644 index 00000000..6025f670 --- /dev/null +++ b/src/engine/powers/effectmodifiers/HealthFullEffectModifier.java @@ -0,0 +1,53 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class HealthFullEffectModifier extends AbstractEffectModifier { + + public HealthFullEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + PlayerBonuses bonus = ac.getBonuses(); + if (this.percentMod != 0f) { //Stat Percent Modifiers + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + bonus.addFloat(this, amount); + } else { //Stat Modifiers + if (this.useRampAdd) + amount = this.minMod + (this.ramp * trains); + else + amount = this.minMod * (1 + (this.ramp * trains)); + bonus.addFloat(this, amount); + } + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/HealthRecoverRateEffectModifier.java b/src/engine/powers/effectmodifiers/HealthRecoverRateEffectModifier.java new file mode 100644 index 00000000..2eb38cdf --- /dev/null +++ b/src/engine/powers/effectmodifiers/HealthRecoverRateEffectModifier.java @@ -0,0 +1,47 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class HealthRecoverRateEffectModifier extends AbstractEffectModifier { + + public HealthRecoverRateEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + + ac.update(); + Float amount = 0f; + PlayerBonuses bonus = ac.getBonuses(); + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + bonus.multRegen(this.modType, amount); //positive regen modifiers + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/IgnoreDamageCapEffectModifier.java b/src/engine/powers/effectmodifiers/IgnoreDamageCapEffectModifier.java new file mode 100644 index 00000000..6b2ffbaf --- /dev/null +++ b/src/engine/powers/effectmodifiers/IgnoreDamageCapEffectModifier.java @@ -0,0 +1,46 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashSet; + +public class IgnoreDamageCapEffectModifier extends AbstractEffectModifier { + + public IgnoreDamageCapEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + PlayerBonuses bonus = ac.getBonuses(); + if (bonus == null) + return; + + if (bonus.getList(this.modType) == null) + bonus.setList(this.modType, new HashSet<>()); + bonus.getList(this.modType).add(this.sourceType); + + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/IgnorePassiveDefenseEffectModifier.java b/src/engine/powers/effectmodifiers/IgnorePassiveDefenseEffectModifier.java new file mode 100644 index 00000000..3b02a302 --- /dev/null +++ b/src/engine/powers/effectmodifiers/IgnorePassiveDefenseEffectModifier.java @@ -0,0 +1,39 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class IgnorePassiveDefenseEffectModifier extends AbstractEffectModifier { + + public IgnorePassiveDefenseEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + PlayerBonuses bonus = ac.getBonuses(); + bonus.setBool(this.modType,this.sourceType,true); + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/ImmuneToAttackEffectModifier.java b/src/engine/powers/effectmodifiers/ImmuneToAttackEffectModifier.java new file mode 100644 index 00000000..35876f37 --- /dev/null +++ b/src/engine/powers/effectmodifiers/ImmuneToAttackEffectModifier.java @@ -0,0 +1,40 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class ImmuneToAttackEffectModifier extends AbstractEffectModifier { + + public ImmuneToAttackEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + + PlayerBonuses bonus = ac.getBonuses(); + bonus.setBool(this.modType,this.sourceType,true); + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/ImmuneToEffectModifier.java b/src/engine/powers/effectmodifiers/ImmuneToEffectModifier.java new file mode 100644 index 00000000..da21abec --- /dev/null +++ b/src/engine/powers/effectmodifiers/ImmuneToEffectModifier.java @@ -0,0 +1,39 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class ImmuneToEffectModifier extends AbstractEffectModifier { + + public ImmuneToEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + PlayerBonuses bonus = ac.getBonuses(); + bonus.setBool(this.modType,this.sourceType,true); + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/ImmuneToPowersEffectModifier.java b/src/engine/powers/effectmodifiers/ImmuneToPowersEffectModifier.java new file mode 100644 index 00000000..67b4225d --- /dev/null +++ b/src/engine/powers/effectmodifiers/ImmuneToPowersEffectModifier.java @@ -0,0 +1,40 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class ImmuneToPowersEffectModifier extends AbstractEffectModifier { + + public ImmuneToPowersEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + + PlayerBonuses bonus = ac.getBonuses(); + bonus.setBool(this.modType,this.sourceType,true); + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/InvisibleEffectModifier.java b/src/engine/powers/effectmodifiers/InvisibleEffectModifier.java new file mode 100644 index 00000000..c9aba42a --- /dev/null +++ b/src/engine/powers/effectmodifiers/InvisibleEffectModifier.java @@ -0,0 +1,82 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.Enum; +import engine.gameManager.SessionManager; +import engine.jobs.AbstractEffectJob; +import engine.net.client.ClientConnection; +import engine.objects.*; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class InvisibleEffectModifier extends AbstractEffectModifier { + + public InvisibleEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + if (awo.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) { + PlayerCharacter pc = (PlayerCharacter) awo; + + if (effect == null) + return; + + PowersBase pb = effect.getPower(); + if (pb == null) + return; + + ActionsBase ab = effect.getAction(); + + if (ab == null) + return; + + //send invis message to everyone around. + ClientConnection origin = SessionManager.getClientConnection(pc); + if (origin == null) + return; + + ab.getDurationInSeconds(trains); + + pc.setHidden(trains); + + pc.setTimeStampNow("Invis"); + + } + else { + Logger.error( "Cannot go invis on a non player."); + } + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + if (ac == null) + return; + PlayerBonuses bonus = ac.getBonuses(); + if (bonus != null) + bonus.updateIfHigher(this, (float)trains); + + //remove pets + if (ac.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) + ((PlayerCharacter)ac).dismissPet(); + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/ItemNameEffectModifier.java b/src/engine/powers/effectmodifiers/ItemNameEffectModifier.java new file mode 100644 index 00000000..16b91db3 --- /dev/null +++ b/src/engine/powers/effectmodifiers/ItemNameEffectModifier.java @@ -0,0 +1,103 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Building; +import engine.objects.Item; +import engine.powers.EffectsBase; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class ItemNameEffectModifier extends AbstractEffectModifier { + + String name = ""; + + public ItemNameEffectModifier(ResultSet rs) throws SQLException { + super(rs); + + //We're going to add effect names to a lookup map for ./makeitem + int ID = rs.getInt("ID"); + switch (ID) { //don't add these ID's to the name list. They're duplicates + case 4259: return; + case 4210: return; + case 4: return; + case 97: return; + case 610: return; + case 4442: return; + case 5106: return; + case 4637: return; + case 2271: return; + case 587: return; + case 600: return; + case 3191: return; + case 3589: return; + case 3950: return; + case 3499: return; + case 4925: return; + case 15: return; + case 5101: return; + case 2418: return; + case 183: return; + case 373: return; + case 1893: return; + case 3127: return; + case 1232: return; + case 4522: return; + case 4817: return; + case 2833: return; + case 4469: return; + case 2122: return; + case 3057: return; + case 3070: return; + case 191: return; + case 3117: return; + case 3702: return; + case 1619: return; + case 2584: return; + case 414: return; + case 2078: return; + case 4844: return; + case 2275: return; + } + + String namePre = rs.getString("string1"); + String nameSuf = rs.getString("string2"); + String n = (namePre.isEmpty()) ? nameSuf : namePre; + this.name = n; + n = n.toLowerCase(); + n = n.replace(" ", "_"); + String IDString = rs.getString("IDString"); + IDString = IDString.substring(0, IDString.length() - 1); + EffectsBase.addItemEffectsByName(n, IDString); + } + + public String getName() { + return this.name; + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/ManaEffectModifier.java b/src/engine/powers/effectmodifiers/ManaEffectModifier.java new file mode 100644 index 00000000..94e20793 --- /dev/null +++ b/src/engine/powers/effectmodifiers/ManaEffectModifier.java @@ -0,0 +1,232 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.Enum; +import engine.Enum.DamageType; +import engine.Enum.ModType; +import engine.Enum.SourceType; +import engine.gameManager.ChatManager; +import engine.jobs.AbstractEffectJob; +import engine.jobs.DamageOverTimeJob; +import engine.net.DispatchMessage; +import engine.net.client.msg.ModifyHealthMsg; +import engine.objects.*; +import engine.powers.ActionsBase; +import engine.powers.poweractions.AbstractPowerAction; +import engine.powers.poweractions.DamageOverTimePowerAction; +import engine.powers.poweractions.DirectDamagePowerAction; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.concurrent.ThreadLocalRandom; + +public class ManaEffectModifier extends AbstractEffectModifier { + + private DamageType damageType; + + public ManaEffectModifier(ResultSet rs) throws SQLException { + super(rs); + String damageTypeDB = rs.getString("type"); + try { + this.damageType = DamageType.valueOf(damageTypeDB); + } catch (IllegalArgumentException e) { + Logger.error("DamageType could not be loaded from database. " + "UUID = " + this.UUID + + " value received = '" + damageTypeDB + '\'', e); + } + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + if (awo == null) { + Logger.error( "_applyEffectModifier(): NULL AWO passed in."); + return; + } + + if (effect == null) { + Logger.error( "_applyEffectModifier(): NULL AbstractEffectJob passed in."); + return; + } + + if (!AbstractWorldObject.IsAbstractCharacter(awo)) + return; + AbstractCharacter awoac = (AbstractCharacter) awo; + + float modAmount = 0f; + + // Modify Mana by percent + if (this.percentMod != 0f) { + + float mod = 1f; + if (this.useRampAdd) + mod = (this.percentMod + (this.ramp * trains)) / 100; + else + mod = (this.percentMod * (1 + (this.ramp * trains))) / 100; + modAmount = mod * awoac.getManaMax(); + + if (awoac.isSit()) + modAmount *= 2.5f; + + //debug for spell damage and atr + if (source.getDebug(16) && source.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) { + + PlayerCharacter pc = (PlayerCharacter) source; + String smsg = "Percent Damage: " + mod * 100 + '%'; + + ChatManager.chatSystemInfo(pc, smsg); + } + } + + // Modify health by min/max amount + else if (this.minMod != 0f || this.maxMod != 0f) { + float min = this.minMod; + float max = this.maxMod; + if (this.ramp > 0f) { + float mod = this.ramp * trains; + if (this.useRampAdd) { + min += mod; + max += mod; + } else { + min *= (1 + mod); + max *= (1 + mod); + } + } + if (source.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) { + PlayerCharacter pc = (PlayerCharacter) source; + + float focus; + CharacterSkill skill = pc.getSkills().get(effect.getPower().getSkillName()); + if (skill == null) + focus = CharacterSkill.getQuickMastery(pc, effect.getPower().getSkillName()); + else + focus = skill.getModifiedAmount(); + //TODO clean up old formulas once new one is verified + // min *= (0.5 + 0.0075 * pc.getStatIntCurrent() + 0.011 * pc.getStatSpiCurrent() + 0.0196 * focus); + // max *= (0.62 + 0.0192 * pc.getStatIntCurrent() + 0.00415 * pc.getStatSpiCurrent() + 0.015 * focus); + float intt = (pc.getStatIntCurrent() >= 1) ? (float)pc.getStatIntCurrent() : 1f; + float spi = (pc.getStatSpiCurrent() >= 1) ? (float)pc.getStatSpiCurrent() : 1f; + // min *= (intt * 0.0045 + 0.055 * (float)Math.sqrt(intt - 0.5) + spi * 0.006 + 0.07 * (float)Math.sqrt(spi - 0.5) + 0.02 * (int)focus); + // max *= (intt * 0.0117 + 0.13 * (float)Math.sqrt(intt - 0.5) + spi * 0.0024 + (float)Math.sqrt(spi - 0.5) * 0.021 + 0.015 * (int)focus); + min = HealthEffectModifier.getMinDamage(min, intt, spi, focus); + max = HealthEffectModifier.getMaxDamage(max, intt, spi, focus); + + //debug for spell damage and atr + if (pc.getDebug(16)) { + String smsg = "Damage: " + (int)Math.abs(min) + " - " + (int)Math.abs(max); + ChatManager.chatSystemInfo(pc, smsg); + } + } + modAmount = calculateDamage(source, awoac, min, max, awo, trains); + PlayerBonuses bonus = source.getBonuses(); + + // Apply any power effect modifiers (such as stances) + if (bonus != null) + modAmount *= (1 + bonus.getFloatPercentAll(ModType.PowerDamageModifier, SourceType.None)); + } + if (modAmount == 0f) + return; + if (AbstractWorldObject.IsAbstractCharacter(awo)) { + AbstractCharacter ac = (AbstractCharacter) awo; + int powerID = 0, effectID = 0; + String powerName = ""; + if (effect.getPower() != null) { + powerID = effect.getPower().getToken(); + powerName = effect.getPower().getName(); + } + if (effect.getEffect() != null) { + effectID = effect.getEffect().getToken(); + } + + //see if target is immune to heals + if (modAmount > 0f) { + boolean skipImmune = false; + if (effect.getAction().getPowerAction() instanceof DirectDamagePowerAction) { + ArrayList actions = effect.getPower().getActions(); + for (ActionsBase ab : actions) { + AbstractPowerAction apa = ab.getPowerAction(); + if (apa instanceof DamageOverTimePowerAction) + skipImmune = true; + } + } + PlayerBonuses bonus = ac.getBonuses(); + if (!skipImmune && bonus.getFloat(ModType.BlackMantle, SourceType.Heal) >= trains) { + ModifyHealthMsg mhm = new ModifyHealthMsg(source, ac, 0f, 0f, 0f, powerID, powerName, trains, effectID); + mhm.setUnknown03(5); //set target is immune + DispatchMessage.sendToAllInRange(ac, mhm); + return; + } + } + + ac.modifyMana(modAmount, source); + + ModifyHealthMsg mhm = new ModifyHealthMsg(source, ac, 0f, modAmount, 0f, powerID, powerName, trains, + effectID); + if (effect instanceof DamageOverTimeJob) + mhm.setOmitFromChat(1); + DispatchMessage.sendToAllInRange(ac, mhm); + } + } + + private float calculateDamage(AbstractCharacter source, AbstractCharacter target, float minDamage, float maxDamage, AbstractWorldObject awo, int trains) { + // get range between min and max + float range = maxDamage - minDamage; + + // Damage is calculated twice to average a more central point + float damage = ThreadLocalRandom.current().nextFloat() * range; + damage = (damage + (ThreadLocalRandom.current().nextFloat() * range)) / 2; + + // put it back between min and max + damage += minDamage; + + Resists resists = null; + // get resists + if (AbstractWorldObject.IsAbstractCharacter(awo)) { + AbstractCharacter ac = (AbstractCharacter) awo; + resists = ac.getResists(); + } else if (awo.getObjectType().equals(Enum.GameObjectType.Building)) + resists = ((Building) awo).getResists(); + + // calculate resists in if any + if (resists != null) + damage = resists.getResistedDamage(source, target, damageType, damage * -1, trains) * -1; + + if (AbstractWorldObject.IsAbstractCharacter(awo)) { + AbstractCharacter ac = (AbstractCharacter) awo; + if (ac.isSit()) + damage *= 2.5f; // increase damage if sitting + } + + return damage; + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/ManaFullEffectModifier.java b/src/engine/powers/effectmodifiers/ManaFullEffectModifier.java new file mode 100644 index 00000000..ba637c39 --- /dev/null +++ b/src/engine/powers/effectmodifiers/ManaFullEffectModifier.java @@ -0,0 +1,53 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class ManaFullEffectModifier extends AbstractEffectModifier { + + public ManaFullEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + PlayerBonuses bonus = ac.getBonuses(); + if (this.percentMod != 0f) { //Stat Percent Modifiers + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + bonus.addFloat(this, amount); + } else { //Stat Modifiers + if (this.useRampAdd) + amount = this.minMod + (this.ramp * trains); + else + amount = this.minMod * (1 + (this.ramp * trains)); + bonus.addFloat(this, amount); + } + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/ManaRecoverRateEffectModifier.java b/src/engine/powers/effectmodifiers/ManaRecoverRateEffectModifier.java new file mode 100644 index 00000000..64886767 --- /dev/null +++ b/src/engine/powers/effectmodifiers/ManaRecoverRateEffectModifier.java @@ -0,0 +1,45 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class ManaRecoverRateEffectModifier extends AbstractEffectModifier { + + public ManaRecoverRateEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + PlayerBonuses bonus = ac.getBonuses(); + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + bonus.multRegen(this.modType, amount); //positive regen modifiers + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/MaxDamageEffectModifier.java b/src/engine/powers/effectmodifiers/MaxDamageEffectModifier.java new file mode 100644 index 00000000..e52045f2 --- /dev/null +++ b/src/engine/powers/effectmodifiers/MaxDamageEffectModifier.java @@ -0,0 +1,61 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Building; +import engine.objects.Item; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class MaxDamageEffectModifier extends AbstractEffectModifier { + + public MaxDamageEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + + } + + @Override + public void applyBonus(Item item, int trains) { + if (item == null) + return; + String key; float amount = 0f; + if (this.percentMod != 0f) { + if (this.useRampAdd) + amount = (this.percentMod + (this.ramp * trains)) / 100f; + else + amount = (this.percentMod * (1 + (this.ramp * trains))) / 100f; + amount = amount/100; + key = "max.percent"; + } else { + if (this.useRampAdd) + amount = this.minMod + (this.ramp * trains); + else + amount = this.minMod * (1 + (this.ramp * trains)); + key = "max"; + } + item.addBonus(this, amount); + } + + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/MeleeDamageEffectModifier.java b/src/engine/powers/effectmodifiers/MeleeDamageEffectModifier.java new file mode 100644 index 00000000..f822aba4 --- /dev/null +++ b/src/engine/powers/effectmodifiers/MeleeDamageEffectModifier.java @@ -0,0 +1,53 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class MeleeDamageEffectModifier extends AbstractEffectModifier { + + public MeleeDamageEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + PlayerBonuses bonus = ac.getBonuses(); + if (this.percentMod != 0f) { //Stat Percent Modifiers + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + bonus.addFloat(this, amount); + } else { //Stat Modifiers + if (this.useRampAdd) + amount = this.minMod + (this.ramp * trains); + else + amount = this.minMod * (1 + (this.ramp * trains)); + bonus.addFloat(this, amount); + } + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/MinDamageEffectModifier.java b/src/engine/powers/effectmodifiers/MinDamageEffectModifier.java new file mode 100644 index 00000000..90e73fe0 --- /dev/null +++ b/src/engine/powers/effectmodifiers/MinDamageEffectModifier.java @@ -0,0 +1,61 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Building; +import engine.objects.Item; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class MinDamageEffectModifier extends AbstractEffectModifier { + + public MinDamageEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + + } + + @Override + public void applyBonus(Item item, int trains) { + if (item == null) + return; + String key; float amount = 0f; + if (this.percentMod != 0f) { + if (this.useRampAdd) + amount = (this.percentMod + (this.ramp * trains)) / 100f; + else + amount = (this.percentMod * (1 + (this.ramp * trains))) / 100f; + amount = amount/100; + key = "min.percent"; + } else { + if (this.useRampAdd) + amount = this.minMod + (this.ramp * trains); + else + amount = this.minMod * (1 + (this.ramp * trains)); + key = "min"; + } + item.addBonus(this, amount); + } + + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/NoModEffectModifier.java b/src/engine/powers/effectmodifiers/NoModEffectModifier.java new file mode 100644 index 00000000..f5716e03 --- /dev/null +++ b/src/engine/powers/effectmodifiers/NoModEffectModifier.java @@ -0,0 +1,53 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.Enum.GameObjectType; +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class NoModEffectModifier extends AbstractEffectModifier { + + public NoModEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + //TODO check if anything needs removed. + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + PlayerBonuses bonus = ac.getBonuses(); + bonus.setBool(this.modType,this.sourceType,true); + + switch (this.sourceType){ + case Fly: + if (!ac.getObjectType().equals(GameObjectType.PlayerCharacter)) + return; + PlayerCharacter flyer = (PlayerCharacter)ac; + + if (flyer.getAltitude() > 0) + flyer.update(); + PlayerCharacter.GroundPlayer(flyer); + break; + + } + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/OCVEffectModifier.java b/src/engine/powers/effectmodifiers/OCVEffectModifier.java new file mode 100644 index 00000000..5c91affe --- /dev/null +++ b/src/engine/powers/effectmodifiers/OCVEffectModifier.java @@ -0,0 +1,53 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class OCVEffectModifier extends AbstractEffectModifier { + + public OCVEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + PlayerBonuses bonus = ac.getBonuses(); + if (this.percentMod != 0f) { //Stat Percent Modifiers + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + bonus.addFloat(this, amount); + } else { //Stat Modifiers + if (this.useRampAdd) + amount = this.minMod + (this.ramp * trains); + else + amount = this.minMod * (1 + (this.ramp * trains)); + bonus.addFloat(this, amount); + } + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/ParryEffectModifier.java b/src/engine/powers/effectmodifiers/ParryEffectModifier.java new file mode 100644 index 00000000..6a0f0f27 --- /dev/null +++ b/src/engine/powers/effectmodifiers/ParryEffectModifier.java @@ -0,0 +1,53 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class ParryEffectModifier extends AbstractEffectModifier { + + public ParryEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + PlayerBonuses bonus = ac.getBonuses(); + if (this.percentMod != 0f) { //Stat Percent Modifiers + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + bonus.addFloat(this, amount); + } else { //Stat Modifiers + if (this.useRampAdd) + amount = this.minMod + (this.ramp * trains); + else + amount = this.minMod * (1 + (this.ramp * trains)); + bonus.addFloat(this, amount); + } + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/PassiveDefenseEffectModifier.java b/src/engine/powers/effectmodifiers/PassiveDefenseEffectModifier.java new file mode 100644 index 00000000..9789c4c4 --- /dev/null +++ b/src/engine/powers/effectmodifiers/PassiveDefenseEffectModifier.java @@ -0,0 +1,53 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class PassiveDefenseEffectModifier extends AbstractEffectModifier { + + public PassiveDefenseEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + PlayerBonuses bonus = ac.getBonuses(); + if (this.percentMod != 0f) { //Stat Percent Modifiers + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + bonus.addFloat(this, amount); + } else { //Stat Modifiers + if (this.useRampAdd) + amount = this.minMod + (this.ramp * trains); + else + amount = this.minMod * (1 + (this.ramp * trains)); + bonus.addFloat(this, amount); + } + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/PowerCostEffectModifier.java b/src/engine/powers/effectmodifiers/PowerCostEffectModifier.java new file mode 100644 index 00000000..3a4c54d9 --- /dev/null +++ b/src/engine/powers/effectmodifiers/PowerCostEffectModifier.java @@ -0,0 +1,53 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class PowerCostEffectModifier extends AbstractEffectModifier { + + public PowerCostEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + PlayerBonuses bonus = ac.getBonuses(); + if (this.percentMod != 0f) { //Stat Percent Modifiers + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + bonus.addFloat(this, amount); + } else { //Stat Modifiers + if (this.useRampAdd) + amount = this.minMod + (this.ramp * trains); + else + amount = this.minMod * (1 + (this.ramp * trains)); + bonus.addFloat(this, amount); + } + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/PowerCostHealthEffectModifier.java b/src/engine/powers/effectmodifiers/PowerCostHealthEffectModifier.java new file mode 100644 index 00000000..e6006a3a --- /dev/null +++ b/src/engine/powers/effectmodifiers/PowerCostHealthEffectModifier.java @@ -0,0 +1,41 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Building; +import engine.objects.Item; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class PowerCostHealthEffectModifier extends AbstractEffectModifier { + + public PowerCostHealthEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/PowerDamageEffectModifier.java b/src/engine/powers/effectmodifiers/PowerDamageEffectModifier.java new file mode 100644 index 00000000..238e3fbd --- /dev/null +++ b/src/engine/powers/effectmodifiers/PowerDamageEffectModifier.java @@ -0,0 +1,53 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class PowerDamageEffectModifier extends AbstractEffectModifier { + + public PowerDamageEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + PlayerBonuses bonus = ac.getBonuses(); + if (this.percentMod != 0f) { //Stat Percent Modifiers + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + bonus.addFloat(this, amount); + } else { //Stat Modifiers + if (this.useRampAdd) + amount = this.minMod + (this.ramp * trains); + else + amount = this.minMod * (1 + (this.ramp * trains)); + bonus.addFloat(this, amount); + } + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/ProtectionFromEffectModifier.java b/src/engine/powers/effectmodifiers/ProtectionFromEffectModifier.java new file mode 100644 index 00000000..f329bbf4 --- /dev/null +++ b/src/engine/powers/effectmodifiers/ProtectionFromEffectModifier.java @@ -0,0 +1,42 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class ProtectionFromEffectModifier extends AbstractEffectModifier { + + public ProtectionFromEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + PlayerBonuses bonus = ac.getBonuses(); + if (bonus == null) + return; + bonus.setFloat(this, trains); + // bonus.setBool(this, true); + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/ResistanceEffectModifier.java b/src/engine/powers/effectmodifiers/ResistanceEffectModifier.java new file mode 100644 index 00000000..e4286e24 --- /dev/null +++ b/src/engine/powers/effectmodifiers/ResistanceEffectModifier.java @@ -0,0 +1,53 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class ResistanceEffectModifier extends AbstractEffectModifier { + + public ResistanceEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + PlayerBonuses bonus = ac.getBonuses(); + if (this.percentMod != 0f) { //Stat Percent Modifiers + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + bonus.addFloat(this, amount); + } else { //Stat Modifiers + if (this.useRampAdd) + amount = this.minMod + (this.ramp * trains); + else + amount = this.minMod * (1 + (this.ramp * trains)); + bonus.addFloat(this, amount); + } + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/ScaleHeightEffectModifier.java b/src/engine/powers/effectmodifiers/ScaleHeightEffectModifier.java new file mode 100644 index 00000000..6cd022a4 --- /dev/null +++ b/src/engine/powers/effectmodifiers/ScaleHeightEffectModifier.java @@ -0,0 +1,41 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Building; +import engine.objects.Item; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class ScaleHeightEffectModifier extends AbstractEffectModifier { + + public ScaleHeightEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/ScaleWidthEffectModifier.java b/src/engine/powers/effectmodifiers/ScaleWidthEffectModifier.java new file mode 100644 index 00000000..00e77e68 --- /dev/null +++ b/src/engine/powers/effectmodifiers/ScaleWidthEffectModifier.java @@ -0,0 +1,41 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Building; +import engine.objects.Item; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class ScaleWidthEffectModifier extends AbstractEffectModifier { + + public ScaleWidthEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/ScanRangeEffectModifier.java b/src/engine/powers/effectmodifiers/ScanRangeEffectModifier.java new file mode 100644 index 00000000..968f2e3e --- /dev/null +++ b/src/engine/powers/effectmodifiers/ScanRangeEffectModifier.java @@ -0,0 +1,53 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class ScanRangeEffectModifier extends AbstractEffectModifier { + + public ScanRangeEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + PlayerBonuses bonus = ac.getBonuses(); + if (this.percentMod != 0f) { //Stat Percent Modifiers + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + bonus.addFloat(this, amount); + } else { //Stat Modifiers + if (this.useRampAdd) + amount = this.minMod + (this.ramp * trains); + else + amount = this.minMod * (1 + (this.ramp * trains)); + bonus.addFloat(this, amount); + } + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/SeeInvisibleEffectModifier.java b/src/engine/powers/effectmodifiers/SeeInvisibleEffectModifier.java new file mode 100644 index 00000000..94ab63d9 --- /dev/null +++ b/src/engine/powers/effectmodifiers/SeeInvisibleEffectModifier.java @@ -0,0 +1,42 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class SeeInvisibleEffectModifier extends AbstractEffectModifier { + + public SeeInvisibleEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + if (ac == null) + return; + PlayerBonuses bonus = ac.getBonuses(); + if (bonus != null) + bonus.updateIfHigher(this, (float)trains); + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/SilencedEffectModifier.java b/src/engine/powers/effectmodifiers/SilencedEffectModifier.java new file mode 100644 index 00000000..75764cc1 --- /dev/null +++ b/src/engine/powers/effectmodifiers/SilencedEffectModifier.java @@ -0,0 +1,40 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class SilencedEffectModifier extends AbstractEffectModifier { + + public SilencedEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + PlayerBonuses bonus = ac.getBonuses(); + bonus.setBool(this.modType,this.sourceType, true); + + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/SkillEffectModifier.java b/src/engine/powers/effectmodifiers/SkillEffectModifier.java new file mode 100644 index 00000000..d49dd6f3 --- /dev/null +++ b/src/engine/powers/effectmodifiers/SkillEffectModifier.java @@ -0,0 +1,53 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class SkillEffectModifier extends AbstractEffectModifier { + + public SkillEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + PlayerBonuses bonus = ac.getBonuses(); + if (this.percentMod != 0f) { //Stat Percent Modifiers + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + bonus.addFloat(this, amount); + } else { //Stat Modifiers + if (this.useRampAdd) + amount = this.minMod + (this.ramp * trains); + else + amount = this.minMod * (1 + (this.ramp * trains)); + bonus.addFloat(this, amount); + } + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/SlayEffectModifier.java b/src/engine/powers/effectmodifiers/SlayEffectModifier.java new file mode 100644 index 00000000..c4608eee --- /dev/null +++ b/src/engine/powers/effectmodifiers/SlayEffectModifier.java @@ -0,0 +1,53 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class SlayEffectModifier extends AbstractEffectModifier { + + public SlayEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + PlayerBonuses bonus = ac.getBonuses(); + if (this.percentMod != 0f) { //Stat Percent Modifiers + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + bonus.addFloat(this, amount); + } else { //Stat Modifiers + if (this.useRampAdd) + amount = this.minMod + (this.ramp * trains); + else + amount = this.minMod * (1 + (this.ramp * trains)); + bonus.addFloat(this, amount); + } + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/SpeedEffectModifier.java b/src/engine/powers/effectmodifiers/SpeedEffectModifier.java new file mode 100644 index 00000000..9df8a13b --- /dev/null +++ b/src/engine/powers/effectmodifiers/SpeedEffectModifier.java @@ -0,0 +1,53 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class SpeedEffectModifier extends AbstractEffectModifier { + + public SpeedEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + //Logger.error(this.getSimpleClassName(), "Speed applied with " + trains + " trains"); + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + PlayerBonuses bonus = ac.getBonuses(); + if (this.percentMod != 0f) { //Stat Percent Modifiers + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + bonus.addFloat(this, amount); + } else { //Stat Modifiers + if (this.useRampAdd) + amount = this.minMod + (this.ramp * trains); + else + amount = this.minMod * (1 + (this.ramp * trains)); + bonus.addFloat(this, amount); + } + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/SpireBlockEffectModifier.java b/src/engine/powers/effectmodifiers/SpireBlockEffectModifier.java new file mode 100644 index 00000000..26dfb368 --- /dev/null +++ b/src/engine/powers/effectmodifiers/SpireBlockEffectModifier.java @@ -0,0 +1,39 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class SpireBlockEffectModifier extends AbstractEffectModifier { + + public SpireBlockEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + PlayerBonuses bonus = ac.getBonuses(); + bonus.setBool(this.modType,this.sourceType, true); + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/StaminaEffectModifier.java b/src/engine/powers/effectmodifiers/StaminaEffectModifier.java new file mode 100644 index 00000000..a15d2653 --- /dev/null +++ b/src/engine/powers/effectmodifiers/StaminaEffectModifier.java @@ -0,0 +1,230 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.Enum; +import engine.Enum.DamageType; +import engine.Enum.ModType; +import engine.Enum.SourceType; +import engine.gameManager.ChatManager; +import engine.jobs.AbstractEffectJob; +import engine.jobs.DamageOverTimeJob; +import engine.net.DispatchMessage; +import engine.net.client.msg.ModifyHealthMsg; +import engine.objects.*; +import engine.powers.ActionsBase; +import engine.powers.poweractions.AbstractPowerAction; +import engine.powers.poweractions.DamageOverTimePowerAction; +import engine.powers.poweractions.DirectDamagePowerAction; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.concurrent.ThreadLocalRandom; + +public class StaminaEffectModifier extends AbstractEffectModifier { + + private DamageType damageType; + + public StaminaEffectModifier(ResultSet rs) throws SQLException { + super(rs); + String damageTypeDB = rs.getString("type"); + try { + this.damageType = DamageType.valueOf(damageTypeDB); + } catch (IllegalArgumentException e) { + Logger.error("DamageType could not be loaded from database. " + "UUID = " + this.UUID + + " value received = '" + damageTypeDB + '\'', e); + } + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + if (awo == null) { + Logger.error( "_applyEffectModifier(): NULL AWO passed in."); + return; + } + + if (effect == null) { + Logger.error( "_applyEffectModifier(): NULL AbstractEffectJob passed in."); + return; + } + + if (!AbstractWorldObject.IsAbstractCharacter(awo)) + return; + AbstractCharacter awoac = (AbstractCharacter) awo; + + float modAmount = 0f; + + // Modify Stamina by percent + if (this.percentMod != 0f) { + float mod = 1f; + if (this.useRampAdd) + mod = (this.percentMod + (this.ramp * trains)) / 100; + else + mod = (this.percentMod * (1 + (this.ramp * trains))) / 100; + modAmount = mod * awoac.getStaminaMax(); + if (awoac.isSit()) + modAmount *= 2.5f; + + //debug for spell damage and atr + if (source.getDebug(16) && source.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) { + PlayerCharacter pc = (PlayerCharacter) source; + String smsg = "Percent Damage: " + mod * 100 + '%'; + ChatManager.chatSystemInfo(pc, smsg); + } + } + + // Modify Stamina by min/max amount + else if (this.minMod != 0f || this.maxMod != 0f) { + float min = this.minMod; + float max = this.maxMod; + if (this.ramp > 0f) { + float mod = this.ramp * trains; + if (this.useRampAdd) { + min += mod; + max += mod; + } else { + min *= (1 + mod); + max *= (1 + mod); + } + } + if (source.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) { + PlayerCharacter pc = (PlayerCharacter) source; + + float focus; + CharacterSkill skill = pc.getSkills().get(effect.getPower().getSkillName()); + if (skill == null) + focus = CharacterSkill.getQuickMastery(pc, effect.getPower().getSkillName()); + else + focus = skill.getModifiedAmount(); + //TODO clean up old formulas once new one is verified + // min *= (0.5 + 0.0075 * pc.getStatIntCurrent() + 0.011 * pc.getStatSpiCurrent() + 0.0196 * focus); + // max *= (0.62 + 0.0192 * pc.getStatIntCurrent() + 0.00415 * pc.getStatSpiCurrent() + 0.015 * focus); + float intt = (pc.getStatIntCurrent() >= 1) ? (float)pc.getStatIntCurrent() : 1f; + float spi = (pc.getStatSpiCurrent() >= 1) ? (float)pc.getStatSpiCurrent() : 1f; + // min *= (intt * 0.0045 + 0.055 * (float)Math.sqrt(intt - 0.5) + spi * 0.006 + 0.07 * (float)Math.sqrt(spi - 0.5) + 0.02 * (int)focus); + // max *= (intt * 0.0117 + 0.13 * (float)Math.sqrt(intt - 0.5) + spi * 0.0024 + (float)Math.sqrt(spi - 0.5) * 0.021 + 0.015 * (int)focus); + min = HealthEffectModifier.getMinDamage(min, intt, spi, focus); + max = HealthEffectModifier.getMaxDamage(max, intt, spi, focus); + + //debug for spell damage and atr + if (pc.getDebug(16)) { + String smsg = "Damage: " + (int)Math.abs(min) + " - " + (int)Math.abs(max); + ChatManager.chatSystemInfo(pc, smsg); + } + } + modAmount = calculateDamage(source, awoac, min, max, awo, trains); + PlayerBonuses bonus = source.getBonuses(); + + // Apply any power effect modifiers (such as stances) + if (bonus != null) + modAmount *= (1 + (bonus.getFloatPercentAll(ModType.PowerDamageModifier, SourceType.None))); + } + if (modAmount == 0f) + return; + if (AbstractWorldObject.IsAbstractCharacter(awo)) { + AbstractCharacter ac = (AbstractCharacter) awo; + int powerID = 0, effectID = 0; + String powerName = ""; + if (effect.getPower() != null) { + powerID = effect.getPower().getToken(); + powerName = effect.getPower().getName(); + } + if (effect.getEffect() != null) { + effectID = effect.getEffect().getToken(); + } + + //see if target is immune to heals + if (modAmount > 0f) { + boolean skipImmune = false; + if (effect.getAction().getPowerAction() instanceof DirectDamagePowerAction) { + ArrayList actions = effect.getPower().getActions(); + for (ActionsBase ab : actions) { + AbstractPowerAction apa = ab.getPowerAction(); + if (apa instanceof DamageOverTimePowerAction) + skipImmune = true; + } + } + PlayerBonuses bonus = ac.getBonuses(); + if (!skipImmune && bonus.getFloat(ModType.BlackMantle, SourceType.Heal) >= trains) { + ModifyHealthMsg mhm = new ModifyHealthMsg(source, ac, 0f, 0f, 0f, powerID, powerName, trains, effectID); + mhm.setUnknown03(5); //set target is immune + DispatchMessage.sendToAllInRange(ac, mhm); + + return; + } + } + + ac.modifyStamina(modAmount, source); + + ModifyHealthMsg mhm = new ModifyHealthMsg(source, ac, 0f, 0f, modAmount, powerID, powerName, trains, + effectID); + if (effect instanceof DamageOverTimeJob) + mhm.setOmitFromChat(1); + DispatchMessage.sendToAllInRange(ac, mhm); + } + } + + private float calculateDamage(AbstractCharacter source, AbstractCharacter target, float minDamage, float maxDamage, AbstractWorldObject awo, int trains) { + + // get range between min and max + float range = maxDamage - minDamage; + + // Damage is calculated twice to average a more central point + float damage = ThreadLocalRandom.current().nextFloat() * range; + damage = (damage + (ThreadLocalRandom.current().nextFloat() * range)) / 2; + + // put it back between min and max + damage += minDamage; + + Resists resists = null; + // get resists + if (AbstractWorldObject.IsAbstractCharacter(awo)) { + AbstractCharacter ac = (AbstractCharacter) awo; + resists = ac.getResists(); + } else if (awo.getObjectType().equals(Enum.GameObjectType.Building)) + resists = ((Building) awo).getResists(); + + // calculate resists in if any + if (resists != null) + damage = resists.getResistedDamage(source, target, damageType, damage * -1, trains) * -1; + + if (AbstractWorldObject.IsAbstractCharacter(awo)) { + AbstractCharacter ac = (AbstractCharacter) awo; + if (ac.isSit()) + damage *= 2.5f; // increase damage if sitting + } + + return damage; + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/StaminaFullEffectModifier.java b/src/engine/powers/effectmodifiers/StaminaFullEffectModifier.java new file mode 100644 index 00000000..1f2ac056 --- /dev/null +++ b/src/engine/powers/effectmodifiers/StaminaFullEffectModifier.java @@ -0,0 +1,53 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class StaminaFullEffectModifier extends AbstractEffectModifier { + + public StaminaFullEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + PlayerBonuses bonus = ac.getBonuses(); + if (this.percentMod != 0f) { //Stat Percent Modifiers + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + bonus.addFloat(this, amount); + } else { //Stat Modifiers + if (this.useRampAdd) + amount = this.minMod + (this.ramp * trains); + else + amount = this.minMod * (1 + (this.ramp * trains)); + bonus.addFloat(this, amount); + } + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/StaminaRecoverRateEffectModifier.java b/src/engine/powers/effectmodifiers/StaminaRecoverRateEffectModifier.java new file mode 100644 index 00000000..11a79d0f --- /dev/null +++ b/src/engine/powers/effectmodifiers/StaminaRecoverRateEffectModifier.java @@ -0,0 +1,47 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class StaminaRecoverRateEffectModifier extends AbstractEffectModifier { + + public StaminaRecoverRateEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + PlayerBonuses bonus = ac.getBonuses(); + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + + //Erection is this right? + amount = amount/100; + bonus.multRegen(this.modType, amount); //positive regen modifiers + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/StunnedEffectModifier.java b/src/engine/powers/effectmodifiers/StunnedEffectModifier.java new file mode 100644 index 00000000..e1e0918a --- /dev/null +++ b/src/engine/powers/effectmodifiers/StunnedEffectModifier.java @@ -0,0 +1,47 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.Enum.GameObjectType; +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class StunnedEffectModifier extends AbstractEffectModifier { + + public StunnedEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + if (ac.getObjectType() == GameObjectType.Mob) { + Mob mob = (Mob) ac; + } + + PlayerBonuses bonus = ac.getBonuses(); + bonus.setBool(this.modType,this.sourceType, true); + ac.cancelOnStun(); + ac.setIsCasting(false); + ac.stopMovement(ac.getLoc()); + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/ValueEffectModifier.java b/src/engine/powers/effectmodifiers/ValueEffectModifier.java new file mode 100644 index 00000000..359d1a93 --- /dev/null +++ b/src/engine/powers/effectmodifiers/ValueEffectModifier.java @@ -0,0 +1,41 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Building; +import engine.objects.Item; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class ValueEffectModifier extends AbstractEffectModifier { + + public ValueEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/WeaponProcEffectModifier.java b/src/engine/powers/effectmodifiers/WeaponProcEffectModifier.java new file mode 100644 index 00000000..90258c58 --- /dev/null +++ b/src/engine/powers/effectmodifiers/WeaponProcEffectModifier.java @@ -0,0 +1,47 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.gameManager.PowersManager; +import engine.jobs.AbstractEffectJob; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Building; +import engine.objects.Item; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class WeaponProcEffectModifier extends AbstractEffectModifier { + + public WeaponProcEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} + + public void applyProc(AbstractCharacter ac, AbstractWorldObject target) { + PowersManager.applyPower(ac, target, Vector3fImmutable.ZERO, this.string1, (int)this.percentMod, false); + } +} diff --git a/src/engine/powers/effectmodifiers/WeaponRangeEffectModifier.java b/src/engine/powers/effectmodifiers/WeaponRangeEffectModifier.java new file mode 100644 index 00000000..cc08ed6b --- /dev/null +++ b/src/engine/powers/effectmodifiers/WeaponRangeEffectModifier.java @@ -0,0 +1,54 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.*; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class WeaponRangeEffectModifier extends AbstractEffectModifier { + + public WeaponRangeEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) { + Float amount = 0f; + PlayerBonuses bonus = ac.getBonuses(); + if (this.percentMod != 0f) { //Stat Percent Modifiers + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + bonus.addFloat(this, amount); + } else { //Stat Modifiers + if (this.useRampAdd) + amount = this.minMod + (this.ramp * trains); + else + amount = this.minMod * (1 + (this.ramp * trains)); + bonus.addFloat(this, amount); + } + + } + + @Override + public void applyBonus(Item item, int trains) {} + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/effectmodifiers/WeaponSpeedEffectModifier.java b/src/engine/powers/effectmodifiers/WeaponSpeedEffectModifier.java new file mode 100644 index 00000000..b47d8642 --- /dev/null +++ b/src/engine/powers/effectmodifiers/WeaponSpeedEffectModifier.java @@ -0,0 +1,48 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.effectmodifiers; + +import engine.jobs.AbstractEffectJob; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Building; +import engine.objects.Item; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class WeaponSpeedEffectModifier extends AbstractEffectModifier { + + public WeaponSpeedEffectModifier(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _applyEffectModifier(AbstractCharacter source, AbstractWorldObject awo, int trains, AbstractEffectJob effect) { + + } + + @Override + public void applyBonus(AbstractCharacter ac, int trains) {} + + @Override + public void applyBonus(Item item, int trains) { + Float amount = 0f; + if (this.useRampAdd) + amount = this.percentMod + (this.ramp * trains); + else + amount = this.percentMod * (1 + (this.ramp * trains)); + amount = amount/100; + item.addBonus(this, amount); + } + + @Override + public void applyBonus(Building building, int trains) {} +} diff --git a/src/engine/powers/poweractions/AbstractPowerAction.java b/src/engine/powers/poweractions/AbstractPowerAction.java new file mode 100644 index 00000000..f7b1914f --- /dev/null +++ b/src/engine/powers/poweractions/AbstractPowerAction.java @@ -0,0 +1,274 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.gameManager.DbManager; +import engine.gameManager.PowersManager; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Item; +import engine.objects.PreparedStatementShared; +import engine.powers.ActionsBase; +import engine.powers.EffectsBase; +import engine.powers.PowersBase; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; + + +public abstract class AbstractPowerAction { + + protected PowersBase parent; + protected int UUID; + protected String IDString; + protected String type; + protected boolean isAggressive; + protected long validItemFlags; + + /** + * No Table ID Constructor + */ + public AbstractPowerAction() { + + } + + /** + * ResultSet Constructor + */ + public AbstractPowerAction(ResultSet rs) throws SQLException { + + this.UUID = rs.getInt("ID"); + this.IDString = rs.getString("IDString"); + this.type = rs.getString("type"); + int flags = rs.getInt("flags"); + this.isAggressive = ((flags & 128) != 0) ? true : false; + } + + public AbstractPowerAction( int uUID, String iDString, String type, boolean isAggressive, + long validItemFlags) { + super(); + UUID = uUID; + IDString = iDString; + this.type = type; + this.isAggressive = false; + } + + public static void getAllPowerActions(HashMap powerActions, HashMap powerActionsByID, HashMap effects) { + PreparedStatementShared ps = null; + try { + ps = new PreparedStatementShared("SELECT * FROM static_power_poweraction"); + ResultSet rs = ps.executeQuery(); + String IDString, type; + while (rs.next()) { + AbstractPowerAction apa; + type = rs.getString("type"); + IDString = rs.getString("IDString"); + int token = DbManager.hasher.SBStringHash(IDString); + //cache token, used for applying effects. + PowersManager.ActionTokenByIDString.put(IDString, token); + if (type.equals("ApplyEffect")) + apa = new ApplyEffectPowerAction(rs, effects); + else if (type.equals("ApplyEffects")) + apa = new ApplyEffectsPowerAction(rs, effects); + else if (type.equals("DeferredPower")) + apa = new DeferredPowerPowerAction(rs, effects); + else if (type.equals("DamageOverTime")) + apa = new DamageOverTimePowerAction(rs, effects); + else if (type.equals("Peek")) + apa = new PeekPowerAction(rs); + else if (type.equals("Charm")) + apa = new CharmPowerAction(rs); + else if (type.equals("Fear")) + apa = new FearPowerAction(rs); + else if (type.equals("Confusion")) + apa = new ConfusionPowerAction(rs); + else if (type.equals("RemoveEffect")) + apa = new RemoveEffectPowerAction(rs); + else if (type.equals("Track")) + apa = new TrackPowerAction(rs, effects); + else if (type.equals("DirectDamage")) + apa = new DirectDamagePowerAction(rs, effects); + else if (type.equals("Transform")) + apa = new TransformPowerAction(rs, effects); + else if (type.equals("CreateMob")) + apa = new CreateMobPowerAction(rs); + else if (type.equals("Invis")) + apa = new InvisPowerAction(rs, effects); + else if (type.equals("ClearNearbyAggro")) + apa = new ClearNearbyAggroPowerAction(rs); + else if (type.equals("MobRecall")) + apa = new MobRecallPowerAction(rs); + else if (type.equals("SetItemFlag")) + apa = new SetItemFlagPowerAction(rs); + else if (type.equals("SimpleDamage")) + apa = new SimpleDamagePowerAction(rs); + else if (type.equals("TransferStatOT")) + apa = new TransferStatOTPowerAction(rs, effects); + else if (type.equals("TransferStat")) + apa = new TransferStatPowerAction(rs, effects); + else if (type.equals("Teleport")) + apa = new TeleportPowerAction(rs); + else if (type.equals("TreeChoke")) + apa = new TreeChokePowerAction(rs); + else if (type.equals("Block")) + apa = new BlockPowerAction(rs); + else if (type.equals("Resurrect")) + apa = new ResurrectPowerAction(rs); + else if (type.equals("ClearAggro")) + apa = new ClearAggroPowerAction(rs); + else if (type.equals("ClaimMine")) + apa = new ClaimMinePowerAction(rs); + else if (type.equals("Recall")) + apa = new RecallPowerAction(rs); + else if (type.equals("SpireDisable")) + apa = new SpireDisablePowerAction(rs); + else if (type.equals("Steal")) + apa = new StealPowerAction(rs); + else if (type.equals("Summon")) + apa = new SummonPowerAction(rs); + else if (type.equals("RunegateTeleport")) + apa = new RunegateTeleportPowerAction(rs); + else if (type.equals("RunegateTeleport")) + apa = new RunegateTeleportPowerAction(rs); + else if (type.equals("OpenGate")) + apa = new OpenGatePowerAction(rs); + else { + Logger.error("valid type not found for poweraction of ID" + rs.getInt("ID")); + continue; + } + powerActions.put(IDString, apa); + powerActionsByID.put(apa.UUID, apa); + apa.validItemFlags = 0; + } + rs.close(); + } catch (Exception e) { + Logger.error( e.toString()); + } finally { + ps.release(); + } + + + + //Add OpenGatePowerAction + AbstractPowerAction openGateAction = new OpenGatePowerAction(5000, "OPENGATE", "OpenGate", false, 0); + powerActions.put("OPENGATE", openGateAction); + powerActionsByID.put(openGateAction.UUID, openGateAction); + } + + public void startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int numTrains, ActionsBase ab, PowersBase pb) { + this._startAction(source, awo, targetLoc, numTrains, ab, pb); + } + + public void startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int numTrains, ActionsBase ab, PowersBase pb, int duration) { + this._startAction(source, awo, targetLoc, numTrains, ab, pb,duration); + } + + protected abstract void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int numTrains, ActionsBase ab, PowersBase pb); + + protected abstract void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int numTrains, ActionsBase ab, PowersBase pb, int duration); + + public void handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int numTrains, ActionsBase ab, PowersBase pb) { + this._handleChant(source, target, targetLoc, numTrains, ab, pb); + } + + protected abstract void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int numTrains, ActionsBase ab, PowersBase pb); + + public int getUUID() { + return this.UUID; + } + + public String getIDString() { + return this.IDString; + } + + // public String getMessageType() { + // return this.type; + // } + + public boolean isAggressive() { + return this.isAggressive; + } + + public PowersBase getParent() { + return this.parent; + } + + public void setParent(PowersBase value) { + this.parent = value; + } + + public void applyEffectForItem(Item item, int trains) { + if (this instanceof ApplyEffectPowerAction) + ((ApplyEffectPowerAction)this)._applyEffectForItem(item, trains); + if (this instanceof ApplyEffectsPowerAction) + ((ApplyEffectsPowerAction)this)._applyEffectForItem(item, trains); + } + public void applyBakedInStatsForItem(Item item, int trains) { + if (this instanceof ApplyEffectPowerAction) + ((ApplyEffectPowerAction)this)._applyBakedInStatsForItem(item, trains); + if (this instanceof ApplyEffectsPowerAction) + ((ApplyEffectsPowerAction)this)._applyBakedInStatsForItem(item, trains); + } + + public EffectsBase getEffectsBase() { + if (this instanceof ApplyEffectPowerAction) + return ((ApplyEffectPowerAction)this).getEffect(); + else if (this instanceof ApplyEffectsPowerAction) + return ((ApplyEffectsPowerAction)this).getEffect(); + return null; + } + + public EffectsBase getEffectsBase2() { + if (this instanceof ApplyEffectsPowerAction) + return ((ApplyEffectsPowerAction)this).getEffect2(); + return null; + } + + public static void loadValidItemFlags(HashMap powerActions) { + PreparedStatementShared ps = null; + try { + ps = new PreparedStatementShared("SELECT * FROM `static_power_effect_allowed_item`"); + ResultSet rs = ps.executeQuery(); + String IDS; + long flags; + while (rs.next()) { + AbstractPowerAction apa; + flags = rs.getLong("flags"); + IDS = rs.getString("IDString"); + if (powerActions.containsKey(IDS)) { + apa = powerActions.get(IDS); + apa.validItemFlags = flags; + } else { + Logger.error("Unable to find PowerAction " + IDS); + continue; + } + } + rs.close(); + } catch (Exception e) { + Logger.error(e.toString()); + } finally { + ps.release(); + } + + } + + //These functions verify a powerAction is valid for an item type + public long getValidItemFlags() { + return this.validItemFlags; + } + + public String getType() { + return type; + } + +} diff --git a/src/engine/powers/poweractions/ApplyEffectPowerAction.java b/src/engine/powers/poweractions/ApplyEffectPowerAction.java new file mode 100644 index 00000000..95c5ae2f --- /dev/null +++ b/src/engine/powers/poweractions/ApplyEffectPowerAction.java @@ -0,0 +1,265 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + + + +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.Enum.GameObjectType; +import engine.Enum.ModType; +import engine.Enum.SourceType; +import engine.ai.MobileFSM; +import engine.gameManager.ChatManager; +import engine.jobs.ChantJob; +import engine.jobs.DeferredPowerJob; +import engine.jobs.FinishEffectTimeJob; +import engine.math.Vector3fImmutable; +import engine.net.DispatchMessage; +import engine.net.client.msg.chat.ChatSystemMsg; +import engine.objects.*; +import engine.powers.ActionsBase; +import engine.powers.EffectsBase; +import engine.powers.PowersBase; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; + +public class ApplyEffectPowerAction extends AbstractPowerAction { + + private String effectID; + private EffectsBase effect; + private String effectParentID; + private EffectsBase effectParent; + + public ApplyEffectPowerAction(ResultSet rs, HashMap effects) throws SQLException { + super(rs); + this.effectParentID = rs.getString("IDString"); + this.effectParent = effects.get(this.effectParentID); + this.effectID = rs.getString("effectID"); + this.effect = effects.get(this.effectID); + } + + public ApplyEffectPowerAction(ResultSet rs, EffectsBase effect) throws SQLException { + super(rs); + + this.effectID = rs.getString("effectID"); + this.effect = effect; + } + + public String getEffectID() { + return this.effectID; + } + + public EffectsBase getEffect() { + return this.effect; + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + if (this.effect == null || pb == null || ab == null) { + //TODO log error here + return; + } + + //add schedule job to end it if needed and add effect to pc + int duration = 0; + // if (pb.isChant()) + // duration = (int)pb.getChantDuration() * 1000; + // else + duration = ab.getDuration(trains); + String stackType = ab.getStackType(); + if (stackType.equals("WeaponMove")) { + DeferredPowerJob eff = new DeferredPowerJob(source, awo, stackType, trains, ab, pb, this.effect, this); + if (stackType.equals("IgnoreStack")) + awo.addEffect(Integer.toString(ab.getUUID()), 10000, eff, this.effect, trains); + else + awo.addEffect(stackType, 10000, eff, this.effect, trains); + if (awo.getObjectType().equals(GameObjectType.PlayerCharacter)) + ((PlayerCharacter)awo).setWeaponPower(eff); + this.effect.startEffect(source, awo, trains, eff); + } else { + FinishEffectTimeJob eff = new FinishEffectTimeJob(source, awo, stackType, trains, ab, pb, effect); + + if (blockInvul(pb, source, awo)) { + this.effect.endEffect(source, awo, trains, pb, eff); + return; + } + +// Effect lastEff = awo.getEffects().get(eff.getStackType()); +// +// if (lastEff != null && lastEff.getPowerToken() == eff.getPowerToken()) +// lastEff.cancelJob(true); + + if (duration > 0) { + if (stackType.equals("IgnoreStack")) + awo.addEffect(Integer.toString(ab.getUUID()), duration, eff, effect, trains); + else + awo.addEffect(stackType, duration, eff, effect, trains); + } else + awo.applyAllBonuses(); + // //TODO if chant, start cycle + // if (pb.isChant() && source.equals(awo)) { + // ChantJob cj = new ChantJob(source, awo, stackType, trains, ab, pb, effect, eff); + // source.setLastChant((int)(pb.getChantDuration()-2) * 1000, cj); + // eff.setChant(true); + // } + + if (this.effectID.equals("TAUNT")){ + + if (awo != null && awo.getObjectType() == GameObjectType.Mob){ + MobileFSM.setAggro((Mob)awo,source.getObjectUUID()); + ChatSystemMsg msg = ChatManager.CombatInfo(source, awo); + DispatchMessage.sendToAllInRange(source, msg); + } + } + this.effect.startEffect(source, awo, trains, eff); + } + } + + protected void _applyEffectForItem(Item item, int trains) { + if (item == null || this.effect == null) + return; + item.addEffectNoTimer(Integer.toString(this.effect.getUUID()), this.effect, trains,false); + item.addEffectNoTimer(Integer.toString(this.effectParent.getUUID()), this.effectParent, trains,false); + } + protected void _applyBakedInStatsForItem(Item item, int trains) { + if (item == null) + return; + if (this.effect == null){ + Logger.error( "Unknown Token: EffectBase ID " + this.effectID + '.'); + return; + } + + if (this.effectParent == null){ + Logger.error("Unknown Token: EffectBase ID " + this.effectParentID + '.'); + return; + } + Effect eff = item.addEffectNoTimer(Integer.toString(this.effect.getUUID()), this.effect, trains,false); + Effect eff3 = item.addEffectNoTimer(Integer.toString(this.effectParent.getUUID()), this.effectParent, trains,false); + if (eff != null && eff3 != null){ + eff3.setBakedInStat(true); + item.getEffectNames().add(this.effect.getIDString()); + item.getEffectNames().add(this.effectParent.getIDString()); + } + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + if (source != null) { + PlayerBonuses bonuses = source.getBonuses(); + + if (bonuses == null) + return; + + boolean noSilence = bonuses.getBool(ModType.Silenced, SourceType.None); + + if (noSilence) + return; + + } + String stackType = ab.stackType; + stackType = stackType.equals("IgnoreStack") ? Integer.toString(ab.getUUID()) : stackType; + + FinishEffectTimeJob eff = new FinishEffectTimeJob(source, target, stackType, trains, ab, pb, effect); + ChantJob cj = new ChantJob(source, target, stackType, trains, ab, pb, effect, eff); + //handle invul + if(pb.getUUID() != 334) + source.setLastChant((int)(pb.getChantDuration()) * 1000, cj); + else + source.setLastChant((int)(pb.getChantDuration()) * 1000, cj); + eff.setChant(true); + } + + private static boolean blockInvul(PowersBase pb, AbstractCharacter source, AbstractWorldObject awo) { + if (awo == null || pb == null || source == null) + return false; + + if (source.getObjectUUID() == awo.getObjectUUID()) + return false; + + if (!AbstractWorldObject.IsAbstractCharacter(awo)) + return false; + + AbstractCharacter ac = (AbstractCharacter) awo; + + + return false; + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int trains, ActionsBase ab, PowersBase pb, int duration) { + if (this.effect == null || pb == null || ab == null) { + //TODO log error here + return; + } + + //add schedule job to end it if needed and add effect to pc + // if (pb.isChant()) + // duration = (int)pb.getChantDuration() * 1000; + // else + + String stackType = ab.getStackType(); + if (stackType.equals("WeaponMove")) { + DeferredPowerJob eff = new DeferredPowerJob(source, awo, stackType, trains, ab, pb, this.effect, this); + if (stackType.equals("IgnoreStack")) + awo.addEffect(Integer.toString(ab.getUUID()), 10000, eff, this.effect, trains); + else + awo.addEffect(stackType, 10000, eff, this.effect, trains); + if (awo.getObjectType().equals(GameObjectType.PlayerCharacter)) + ((PlayerCharacter)awo).setWeaponPower(eff); + this.effect.startEffect(source, awo, trains, eff); + } else { + FinishEffectTimeJob eff = new FinishEffectTimeJob(source, awo, stackType, trains, ab, pb, effect); + + if (blockInvul(pb, source, awo)) { + this.effect.endEffect(source, awo, trains, pb, eff); + return; + } + + if (duration > 0) { + if (stackType.equals("IgnoreStack")) + awo.addEffect(Integer.toString(ab.getUUID()), duration, eff, effect, trains); + else + awo.addEffect(stackType, duration, eff, effect, trains); + } else + awo.applyAllBonuses(); + // //TODO if chant, start cycle + // if (pb.isChant() && source.equals(awo)) { + // ChantJob cj = new ChantJob(source, awo, stackType, trains, ab, pb, effect, eff); + // source.setLastChant((int)(pb.getChantDuration()-2) * 1000, cj); + // eff.setChant(true); + // } + + if (this.effectID.equals("TAUNT")){ + + if (awo != null && awo.getObjectType() == GameObjectType.Mob){ + MobileFSM.setAggro((Mob)awo,source.getObjectUUID()); + ChatSystemMsg msg = ChatManager.CombatInfo(source, awo); + DispatchMessage.sendToAllInRange(source, msg); + } + } + this.effect.startEffect(source, awo, trains, eff); + } + + } + +} diff --git a/src/engine/powers/poweractions/ApplyEffectsPowerAction.java b/src/engine/powers/poweractions/ApplyEffectsPowerAction.java new file mode 100644 index 00000000..901b34c6 --- /dev/null +++ b/src/engine/powers/poweractions/ApplyEffectsPowerAction.java @@ -0,0 +1,124 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Effect; +import engine.objects.Item; +import engine.powers.ActionsBase; +import engine.powers.EffectsBase; +import engine.powers.PowersBase; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; + + +public class ApplyEffectsPowerAction extends AbstractPowerAction { + private String IDString; + private String effectID; + private String effectID2; + private EffectsBase effect; + private EffectsBase effect2; + private EffectsBase effectParent; + + public ApplyEffectsPowerAction(ResultSet rs, HashMap effects) throws SQLException { + super(rs); + this.IDString = rs.getString("IDString"); + this.effectID = rs.getString("effectID"); + this.effectID2 = rs.getString("effectID2"); + this.effect = effects.get(this.effectID); + this.effect2 = effects.get(this.effectID2); + this.effectParent = effects.get(this.IDString); + + } + + public String getEffectID() { + return this.effectID; + } + + public String getEffectID2() { + return this.effectID2; + } + + public EffectsBase getEffect() { + return this.effect; + } + + public EffectsBase getEffect2() { + return this.effect2; + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + + } + + protected void _applyEffectForItem(Item item, int trains) { + if (item == null || this.effect == null) + return; + item.addEffectNoTimer(Integer.toString(this.effect.getUUID()), this.effect, trains,false); + if (this.effect2 != null) + item.addEffectNoTimer(Integer.toString(this.effect2.getUUID()), this.effect2, trains,false); + item.addEffectNoTimer(Integer.toString(this.effectParent.getUUID()), this.effectParent, trains,false); + } + protected void _applyBakedInStatsForItem(Item item, int trains) { + + if (item == null) + return; + + if (this.effect == null){ + Logger.error( "Unknown Token: EffectBase ID " + this.effectID + '.'); + return; + } + + if (this.effect2 == null){ + Logger.error( "Unknown Token: EffectBase ID " + this.effectID2 + '.'); + return; + } + + if (this.effectParent == null){ + Logger.error( "Unknown Token: EffectBase ID " + this.IDString + '.'); + return; + } + + Effect eff = item.addEffectNoTimer(Integer.toString(this.effect.getUUID()), this.effect, trains,false); + Effect eff2 = item.addEffectNoTimer(Integer.toString(this.effect2.getUUID()), this.effect2, trains,false); + Effect eff3 = item.addEffectNoTimer(Integer.toString(this.effectParent.getUUID()), this.effectParent, trains,false); + + if (eff != null && eff2 != null && eff3 != null){ + eff3.setBakedInStat(true); + item.getEffectNames().add(this.effect.getIDString()); + item.getEffectNames().add(this.effect2.getIDString()); + item.getEffectNames().add(this.effectParent.getIDString()); + } + } + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + public String getIDString() { + return IDString; + } + + public void setIDString(String iDString) { + IDString = iDString; + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/BlockPowerAction.java b/src/engine/powers/poweractions/BlockPowerAction.java new file mode 100644 index 00000000..db395299 --- /dev/null +++ b/src/engine/powers/poweractions/BlockPowerAction.java @@ -0,0 +1,43 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class BlockPowerAction extends AbstractPowerAction { + + public BlockPowerAction(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/CharmPowerAction.java b/src/engine/powers/poweractions/CharmPowerAction.java new file mode 100644 index 00000000..4d3b1cde --- /dev/null +++ b/src/engine/powers/poweractions/CharmPowerAction.java @@ -0,0 +1,73 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.Enum; +import engine.math.Vector3fImmutable; +import engine.net.client.ClientConnection; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Mob; +import engine.objects.PlayerCharacter; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class CharmPowerAction extends AbstractPowerAction { + + private int levelCap; + private int levelCapRamp; + + public CharmPowerAction(ResultSet rs) throws SQLException { + super(rs); + this.levelCap = rs.getInt("levelCap"); + this.levelCapRamp = rs.getInt("levelCapRamp"); + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + + if (source == null || awo == null || !(awo.getObjectType().equals(Enum.GameObjectType.Mob)) || !(source.getObjectType().equals(Enum.GameObjectType.PlayerCharacter))) + return; + + PlayerCharacter owner = (PlayerCharacter) source; + ClientConnection origin = owner.getClientConnection(); + + if (origin == null) + return; + + //verify is mob, not pet or guard + Mob mob = (Mob) awo; + if (!mob.isMob()) + return; + + //make sure mob isn't too high level + int cap = this.levelCap + (this.levelCapRamp * trains); + if (mob.getLevel() > cap && pb.getToken() != 1577464266) + return; + + //turn mob into pet. + owner.commandSiegeMinion(mob); + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/ClaimMinePowerAction.java b/src/engine/powers/poweractions/ClaimMinePowerAction.java new file mode 100644 index 00000000..76b3cd4c --- /dev/null +++ b/src/engine/powers/poweractions/ClaimMinePowerAction.java @@ -0,0 +1,61 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.Enum; +import engine.math.Vector3fImmutable; +import engine.objects.*; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class ClaimMinePowerAction extends AbstractPowerAction { + + public ClaimMinePowerAction(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + if (source == null || awo == null) + return; + + if (!(source.getObjectType().equals(Enum.GameObjectType.PlayerCharacter))) + return; + + if (!(awo.getObjectType().equals(Enum.GameObjectType.Building))) + return; + + Building b = (Building)awo; + + if (b.getRank() > 0) + return; + + Mine m = Mine.getMineFromTower(b.getObjectUUID()); + if (m == null) + return; + + m.claimMine((PlayerCharacter) source); + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/ClearAggroPowerAction.java b/src/engine/powers/poweractions/ClearAggroPowerAction.java new file mode 100644 index 00000000..4ac761e6 --- /dev/null +++ b/src/engine/powers/poweractions/ClearAggroPowerAction.java @@ -0,0 +1,52 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.Enum.GameObjectType; +import engine.ai.MobileFSM.STATE; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Mob; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class ClearAggroPowerAction extends AbstractPowerAction { + + public ClearAggroPowerAction(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + if (awo != null && awo.getObjectType() == GameObjectType.Mob){ + ((Mob)awo).setNoAggro(true); + ((Mob)awo).setState(STATE.Patrol); + } + + + + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/ClearNearbyAggroPowerAction.java b/src/engine/powers/poweractions/ClearNearbyAggroPowerAction.java new file mode 100644 index 00000000..a32dd972 --- /dev/null +++ b/src/engine/powers/poweractions/ClearNearbyAggroPowerAction.java @@ -0,0 +1,49 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.Enum.GameObjectType; +import engine.ai.MobileFSM.STATE; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Mob; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class ClearNearbyAggroPowerAction extends AbstractPowerAction { + + public ClearNearbyAggroPowerAction(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + if (source.getObjectType() == GameObjectType.Mob){ + ((Mob)source).setState(STATE.Patrol); + } + + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/ConfusionPowerAction.java b/src/engine/powers/poweractions/ConfusionPowerAction.java new file mode 100644 index 00000000..1c2e1843 --- /dev/null +++ b/src/engine/powers/poweractions/ConfusionPowerAction.java @@ -0,0 +1,43 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class ConfusionPowerAction extends AbstractPowerAction { + + public ConfusionPowerAction(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/CreateMobPowerAction.java b/src/engine/powers/poweractions/CreateMobPowerAction.java new file mode 100644 index 00000000..de78b27d --- /dev/null +++ b/src/engine/powers/poweractions/CreateMobPowerAction.java @@ -0,0 +1,175 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.Enum; +import engine.InterestManagement.WorldGrid; +import engine.ai.MobileFSM.STATE; +import engine.gameManager.DbManager; +import engine.gameManager.ZoneManager; +import engine.math.Vector3fImmutable; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.PetMsg; +import engine.objects.*; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; +import org.pmw.tinylog.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class CreateMobPowerAction extends AbstractPowerAction { + + private int mobID; + private int mobLevel; + + public CreateMobPowerAction(ResultSet rs) throws SQLException { + super(rs); + + this.mobID = rs.getInt("mobID"); + this.mobLevel = rs.getInt("mobLevel"); + } + + public int getMobID() { + return this.mobID; + } + + public int getMobLevel() { + return this.mobLevel; + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + + if (source == null || !(source.getObjectType().equals(Enum.GameObjectType.PlayerCharacter))) + return; + + PlayerCharacter owner = (PlayerCharacter) source; + Mob currentPet = owner.getPet(); + Zone seaFloor = ZoneManager.getSeaFloor(); + Guild guild = Guild.getErrantGuild(); + ClientConnection origin = owner.getClientConnection(); + + if (seaFloor == null || guild == null || origin == null) + return; + + MobBase mobbase = MobBase.getMobBase(mobID); + + if (mobbase == null) { + Logger.error("Attempt to summon pet with null mobbase: " + mobID); + return; + } + + if (mobbase.isNecroPet() && owner.inSafeZone()) + return; + + //create Pet + Mob pet = Mob.createPet(mobID, guild, seaFloor, owner, (short)mobLevel); + + + if(pet.getMobBaseID() == 12021 || pet.getMobBaseID() == 12022) { //is a necro pet + if(currentPet!= null && !currentPet.isNecroPet() && !currentPet.isSiege()) { + DbManager.removeFromCache(currentPet); + WorldGrid.RemoveWorldObject(currentPet); + currentPet.setState(STATE.Disabled); + currentPet.setCombatTarget(null); + + if (currentPet.getParentZone() != null) + currentPet.getParentZone().zoneMobSet.remove(currentPet); + + currentPet.getPlayerAgroMap().clear(); + + try { + currentPet.clearEffects(); + }catch(Exception e){ + Logger.error(e.getMessage()); + } + + //currentPet.disableIntelligence(); + }else if (currentPet != null && currentPet.isSiege()){ + currentPet.setMob(); + currentPet.setOwner(null); + currentPet.setCombatTarget(null); + + if (currentPet.isAlive()) + WorldGrid.updateObject(currentPet); + } + //remove 10th pet + + + owner.spawnNecroPet(pet); + + } + else { //is not a necro pet + if(currentPet != null) { + if(!currentPet.isNecroPet() && !currentPet.isSiege()) { + DbManager.removeFromCache(currentPet); + currentPet.setCombatTarget(null); + currentPet.setState(STATE.Disabled); + + currentPet.setOwner(null); + WorldGrid.RemoveWorldObject(currentPet); + + currentPet.getParentZone().zoneMobSet.remove(currentPet); + currentPet.getPlayerAgroMap().clear(); + currentPet.clearEffects(); + //currentPet.disableIntelligence(); + } + else { + if (currentPet.isSiege()){ + currentPet.setMob(); + currentPet.setOwner(null); + currentPet.setCombatTarget(null); + + if (currentPet.isAlive()) + WorldGrid.updateObject(currentPet); + } + + } + PlayerCharacter.auditNecroPets(owner); + PlayerCharacter.resetNecroPets(owner); + } + } + /* if(owner.getPet() != null) { + if(owner.getPet().getMobBaseID() != 12021 && owner.getPet().getMobBaseID() != 12022) { + //if not a necro pet, remove pet + WorldGrid.removeWorldObject(owner.getPet()); + owner.getPet().disableIntelligence(); + Mob.removePet(owner.getPet().getUUID()); + owner.setPet(null); + } + else { + //if it is a necro pet, add it to the line and set as mob + owner.getPet().setMob(); + } + }*/ + + // if (mobID == 12021 || mobID == 12022) //Necro Pets + // pet.setPet(owner, true); + owner.setPet(pet); + PetMsg pm = new PetMsg(5, pet); + Dispatch dispatch = Dispatch.borrow(owner, pm); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/DamageOverTimePowerAction.java b/src/engine/powers/poweractions/DamageOverTimePowerAction.java new file mode 100644 index 00000000..033f9b2d --- /dev/null +++ b/src/engine/powers/poweractions/DamageOverTimePowerAction.java @@ -0,0 +1,85 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.jobs.DamageOverTimeJob; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.powers.ActionsBase; +import engine.powers.EffectsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; + + +public class DamageOverTimePowerAction extends AbstractPowerAction { + + private String effectID; + private int numIterations; + private EffectsBase effect; + + public DamageOverTimePowerAction(ResultSet rs, HashMap effects) throws SQLException { + super(rs); + + this.effectID = rs.getString("effectID"); + this.numIterations = rs.getInt("numIterations"); + this.effect = effects.get(this.effectID); + } + + public String getEffectID() { + return this.effectID; + } + + public int getNumIterations() { + return this.numIterations; + } + + public EffectsBase getEffect() { + return this.effect; + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + if (this.effect == null || pb == null || ab == null) { + //TODO log error here + return; + } + //add schedule job to end it if needed and add effect to pc + int duration = ab.getDuration(trains); + String stackType = ab.getStackType(); + DamageOverTimeJob eff = new DamageOverTimeJob(source, awo, stackType, trains, ab, pb, this.effect, this); + int tick = eff.getTickLength(); + if (duration > 0) { + if (stackType.equals("IgnoreStack")) + awo.addEffect(Integer.toString(ab.getUUID()), eff.getTickLength(), eff, this.effect, trains); + else + awo.addEffect(stackType, tick, eff, this.effect, trains); + } + + //start effect icon for client. Skip applying dot until first iteration. + eff.setSkipApplyEffect(true); + this.effect.startEffect(source, awo, trains, eff); + eff.setSkipApplyEffect(false); + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/DeferredPowerPowerAction.java b/src/engine/powers/poweractions/DeferredPowerPowerAction.java new file mode 100644 index 00000000..d6cdabb9 --- /dev/null +++ b/src/engine/powers/poweractions/DeferredPowerPowerAction.java @@ -0,0 +1,95 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.jobs.DeferredPowerJob; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Mob; +import engine.objects.PlayerCharacter; +import engine.powers.ActionsBase; +import engine.powers.EffectsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; + + +public class DeferredPowerPowerAction extends AbstractPowerAction { + + private String effectID; + private String deferedPowerID; + private EffectsBase effect; + // private EffectsBase deferedPower; + + public DeferredPowerPowerAction(ResultSet rs, HashMap effects) throws SQLException { + super(rs); + + this.effectID = rs.getString("effectID"); + this.deferedPowerID = rs.getString("deferredPowerID"); + this.effect = effects.get(this.effectID); + } + + public String getEffectID() { + return this.effectID; + } + + public String getDeferredPowerID() { + return this.deferedPowerID; + } + + public EffectsBase getEffect() { + return this.effect; + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + if (this.effect == null || pb == null || ab == null) { + //TODO log error here + return; + } + + //add schedule job to end it if needed and add effect to pc + + String stackType = ab.getStackType(); + DeferredPowerJob eff = new DeferredPowerJob(source, awo, stackType, trains, ab, pb, this.effect, this); + if (stackType.equals("IgnoreStack")) + awo.addEffect(Integer.toString(ab.getUUID()), 10000, eff, this.effect, trains); + else + awo.addEffect(stackType, 10000, eff, this.effect, trains); + + switch (awo.getObjectType()){ + case PlayerCharacter: + ((PlayerCharacter)awo).setWeaponPower(eff); + break; + case Mob: + ((Mob)awo).setWeaponPower(eff); + break; + default: + break; + } + + + this.effect.startEffect(source, awo, trains, eff); + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/DirectDamagePowerAction.java b/src/engine/powers/poweractions/DirectDamagePowerAction.java new file mode 100644 index 00000000..955ea6a9 --- /dev/null +++ b/src/engine/powers/poweractions/DirectDamagePowerAction.java @@ -0,0 +1,93 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.jobs.FinishEffectTimeJob; +import engine.jobs.PersistentAoeJob; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.powers.ActionsBase; +import engine.powers.EffectsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; + + +public class DirectDamagePowerAction extends AbstractPowerAction { + + private String effectID; + private EffectsBase effect; + + public DirectDamagePowerAction(ResultSet rs, HashMap effects) throws SQLException { + super(rs); + + this.effectID = rs.getString("effectID"); + this.effect = effects.get(this.effectID); + } + + public String getEffectID() { + return this.effectID; + } + + public EffectsBase getEffect() { + return this.effect; + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + if (this.effect == null || pb == null || ab == null) { + //TODO log error here + return; + } + + //add schedule job to end it if needed and add effect to pc + int duration = ab.getDuration(trains); + String stackType = ab.getStackType(); + FinishEffectTimeJob eff = new FinishEffectTimeJob(source, awo, stackType, trains, ab, pb, this.effect); + eff.setSkipSendEffect(true); + if (duration > 0) { + if (stackType.equals("IgnoreStack")) + awo.addEffect(Integer.toString(ab.getUUID()), duration, eff, this.effect, trains); + else + awo.addEffect(stackType, duration, eff, this.effect, trains); + } + + // //if chant, start cycle + // if (pb.isChant() && targetLoc.x != 0f && targetLoc.z != 0f) { + // PersistentAoeJob paoe = new PersistentAoeJob(source, stackType, trains, ab, pb, effect, eff, targetLoc); + // source.addPersistantAoe(stackType, (int)(pb.getChantDuration() * 1000), paoe, effect, trains); + // eff.setChant(true); + // } + + this.effect.startEffect(source, awo, trains, eff); + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + String stackType = ab.getStackType(); + stackType = stackType.equals("IgnoreStack") ? Integer.toString(ab.getUUID()) : stackType; + FinishEffectTimeJob eff = new FinishEffectTimeJob(source, target, stackType, trains, ab, pb, this.effect); + if (targetLoc.x != 0f && targetLoc.z != 0f) { + PersistentAoeJob paoe = new PersistentAoeJob(source,target, stackType, trains, ab, pb, effect, eff, targetLoc); + source.addPersistantAoe(stackType, (int)(pb.getChantDuration() * 1000), paoe, effect, trains); + eff.setChant(true); + } + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/FearPowerAction.java b/src/engine/powers/poweractions/FearPowerAction.java new file mode 100644 index 00000000..23c41123 --- /dev/null +++ b/src/engine/powers/poweractions/FearPowerAction.java @@ -0,0 +1,76 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.Enum; +import engine.job.JobScheduler; +import engine.jobs.EndFearJob; +import engine.math.Vector3fImmutable; +import engine.net.client.ClientConnection; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Mob; +import engine.objects.PlayerCharacter; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class FearPowerAction extends AbstractPowerAction { + + private int levelCap; + private int levelCapRamp; + + public FearPowerAction(ResultSet rs) throws SQLException { + super(rs); + this.levelCap = rs.getInt("levelCap"); + this.levelCapRamp = rs.getInt("levelCapRamp"); + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + if (source == null || awo == null || !(awo.getObjectType().equals(Enum.GameObjectType.Mob)) || !(source.getObjectType().equals(Enum.GameObjectType.PlayerCharacter))) + return; + + PlayerCharacter owner = (PlayerCharacter) source; + ClientConnection origin = owner.getClientConnection(); + if (origin == null) + return; + + //verify is mob, not pet or guard + Mob mob = (Mob) awo; + if (!mob.isMob()) + return; + + //make sure mob isn't too high level + int cap = this.levelCap + (this.levelCapRamp * trains); + if (mob.getLevel() > cap || mob.getLevel() > 79) + return; + + //Apply fear to mob + int duration = 10 + ((int)(trains * 0.5)); + String stackType = ab.getStackType(); + EndFearJob efj = new EndFearJob(source, awo, stackType, trains, ab, pb, null); + ((Mob)awo).setFearedObject(source); + JobScheduler.getInstance().scheduleJob(efj, duration * 1000); + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/InvisPowerAction.java b/src/engine/powers/poweractions/InvisPowerAction.java new file mode 100644 index 00000000..cade11fc --- /dev/null +++ b/src/engine/powers/poweractions/InvisPowerAction.java @@ -0,0 +1,78 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.jobs.FinishEffectTimeJob; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.powers.ActionsBase; +import engine.powers.EffectsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; + + +public class InvisPowerAction extends AbstractPowerAction { + + private String effectID; + private EffectsBase effect; + + public InvisPowerAction(ResultSet rs, HashMap effects) throws SQLException { + super(rs); + + this.effectID = rs.getString("effectID"); + this.effect = effects.get(this.effectID); + } + + public String getEffectID() { + return this.effectID; + } + + public EffectsBase getEffect() { + return this.effect; + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + if (this.effect == null || pb == null || ab == null) { + //TODO log error here + return; + } + + // if (this.effect.ignoreMod()) + // trains = 50; //set above see invis for safe mode and csr-invis + + //add schedule job to end it if needed and add effect to pc + int duration = ab.getDuration(trains); + String stackType = ab.getStackType(); + FinishEffectTimeJob eff = new FinishEffectTimeJob(source, awo, stackType, trains, ab, pb, this.effect); + if (duration > 0) { + if (stackType.equals("IgnoreStack")) + awo.addEffect(Integer.toString(ab.getUUID()), duration, eff, this.effect, trains); + else + awo.addEffect(stackType, duration, eff, this.effect, trains); + } + this.effect.startEffect(source, awo, trains, eff); + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/MobRecallPowerAction.java b/src/engine/powers/poweractions/MobRecallPowerAction.java new file mode 100644 index 00000000..af5a1250 --- /dev/null +++ b/src/engine/powers/poweractions/MobRecallPowerAction.java @@ -0,0 +1,60 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.Enum.GameObjectType; +import engine.ai.MobileFSM; +import engine.gameManager.MovementManager; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Mob; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class MobRecallPowerAction extends AbstractPowerAction { + + public MobRecallPowerAction(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + if (!AbstractWorldObject.IsAbstractCharacter(awo) || source == null) + return; + AbstractCharacter awoac = (AbstractCharacter)awo; + + if (awo.getObjectType() != GameObjectType.Mob) + return; + + + MovementManager.translocate(awoac,awoac.getBindLoc(), null); + if (awoac.getObjectType() == GameObjectType.Mob){ + MobileFSM.setAwake((Mob)awoac,true); + } + + + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/OpenGatePowerAction.java b/src/engine/powers/poweractions/OpenGatePowerAction.java new file mode 100644 index 00000000..84032829 --- /dev/null +++ b/src/engine/powers/poweractions/OpenGatePowerAction.java @@ -0,0 +1,130 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.Enum; +import engine.Enum.BuildingGroup; +import engine.Enum.GameObjectType; +import engine.Enum.RunegateType; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Building; +import engine.objects.Runegate; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class OpenGatePowerAction extends AbstractPowerAction { + + /** + * ResultSet Constructor + */ + public OpenGatePowerAction(ResultSet rs) throws SQLException { + super(rs); + } + + public OpenGatePowerAction(int uUID, String iDString, String type, boolean isAggressive, long validItemFlags) { + super(uUID, iDString, type, isAggressive, validItemFlags); + // TODO Auto-generated constructor stub + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + + + if (awo.getObjectType().equals(GameObjectType.Building) == false) + return; + + Building targetBuilding = (Building) awo; + Runegate runeGate; + RunegateType runegateType; + RunegateType portalType; + int token; + + // Sanity check. + + if (source == null || awo == null || !(awo.getObjectType().equals(Enum.GameObjectType.Building)) || pb == null) + return; + + // Make sure building has a blueprint + + if (targetBuilding.getBlueprintUUID() == 0) + return; + + // Make sure building is actually a runegate. + + if (targetBuilding.getBlueprint().getBuildingGroup() != BuildingGroup.RUNEGATE) + return; + + // Which portal was opened? + + token = pb.getToken(); + portalType = RunegateType.AIR; + + switch (token) { + case 428937084: //Death Gate + portalType = RunegateType.OBLIV; + break; + + case 429756284: //Chaos Gate + portalType = RunegateType.CHAOS; + break; + + case 429723516: //Khar Gate + portalType = RunegateType.MERCHANT; + break; + + case 429559676: //Spirit Gate + portalType = RunegateType.SPIRIT; + break; + + case 429592444: //Water Gate + portalType = RunegateType.WATER; + break; + + case 429428604: //Fire Gate + portalType = RunegateType.FIRE; + break; + + case 429526908: //Air Gate + portalType = RunegateType.AIR; + break; + + case 429625212: //Earth Gate + portalType = RunegateType.EARTH; + break; + + default: + } + + // Which runegate was clicked on? + + runegateType = RunegateType.getGateTypeFromUUID(targetBuilding.getObjectUUID()); + runeGate = Runegate.getRunegates()[runegateType.ordinal()]; + + runeGate.activatePortal(portalType); + + + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/PeekPowerAction.java b/src/engine/powers/poweractions/PeekPowerAction.java new file mode 100644 index 00000000..289bc523 --- /dev/null +++ b/src/engine/powers/poweractions/PeekPowerAction.java @@ -0,0 +1,137 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.Enum; +import engine.gameManager.ChatManager; +import engine.math.Vector3fImmutable; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.msg.LootWindowResponseMsg; +import engine.objects.*; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.concurrent.ThreadLocalRandom; + + +public class PeekPowerAction extends AbstractPowerAction { + + public PeekPowerAction(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + + if (source == null || awo == null || !(source.getObjectType().equals(Enum.GameObjectType.PlayerCharacter))) + return; + + PlayerCharacter pc = null; + if (source.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) + pc = (PlayerCharacter) source; + + AbstractCharacter target = null; + if (AbstractWorldObject.IsAbstractCharacter(awo)) + target = (AbstractCharacter) awo; + + //test probability of successful peek + boolean peekSuccess = peekSuccess(source, awo); + if (peekSuccess) { + ChatManager.chatPeekSteal(pc, target, null, true, peekDetect(source, awo), -1); + } else { + ChatManager.chatPeekSteal(pc, target, null, false, false, -1); + return; + } + + LootWindowResponseMsg lwrm = null; + + if (awo.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) { + + PlayerCharacter tar = (PlayerCharacter)awo; + + if (!tar.isAlive()) + return; + + lwrm = new LootWindowResponseMsg(tar.getObjectType().ordinal(), tar.getObjectUUID(), tar.getInventory(true)); + } else if (awo.getObjectType().equals(Enum.GameObjectType.Mob)) { + + Mob tar = (Mob) awo; + + if (!tar.isAlive()) + return; + + lwrm = new LootWindowResponseMsg(tar.getObjectType().ordinal(), tar.getObjectUUID(), tar.getInventory(true)); + } + if (lwrm == null) + return; + + Dispatch dispatch = Dispatch.borrow(pc, lwrm); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + protected static boolean peekSuccess(AbstractCharacter pc, AbstractWorldObject awo) { + if (pc == null || awo == null || !AbstractWorldObject.IsAbstractCharacter(awo) || pc.getPowers() == null) + return false; + + int levelDif = pc.getLevel() - ((AbstractCharacter)awo).getLevel(); + + if (!pc.getPowers().containsKey(429494332)) + return false; + + CharacterPower cp = pc.getPowers().get(429494332); + int trains = cp.getTotalTrains(); + + float chance = 30 + (trains * 1.5f) + levelDif; + chance = (chance < 5f) ? 5f : chance; + chance = (chance > 95f) ? 95f : chance; + + float roll = ThreadLocalRandom.current().nextFloat() * 100f; + + return roll < chance; + + } + + + protected static boolean peekDetect(AbstractCharacter pc, AbstractWorldObject awo) { + if (pc == null || awo == null || !AbstractWorldObject.IsAbstractCharacter(awo) || pc.getPowers() == null) + return false; + + int levelDif = pc.getLevel() - ((AbstractCharacter)awo).getLevel(); + + if (!pc.getPowers().containsKey(429494332)) + return false; + + CharacterPower cp = pc.getPowers().get(429494332); + int trains = cp.getTotalTrains(); + + // check if peek is detected + float chance = 30 + (40-trains)*1.5f - levelDif; + chance = (chance < 5f) ? 5f : chance; + chance = (chance > 95f) ? 95f : chance; + + float roll = ThreadLocalRandom.current().nextFloat() * 100f; + return roll < chance; + + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/RecallPowerAction.java b/src/engine/powers/poweractions/RecallPowerAction.java new file mode 100644 index 00000000..3462a363 --- /dev/null +++ b/src/engine/powers/poweractions/RecallPowerAction.java @@ -0,0 +1,88 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.Enum.GameObjectType; +import engine.InterestManagement.WorldGrid; +import engine.ai.MobileFSM; +import engine.gameManager.MovementManager; +import engine.math.Vector3fImmutable; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.PromptRecallMsg; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Mob; +import engine.objects.PlayerCharacter; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; +import engine.server.MBServerStatics; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class RecallPowerAction extends AbstractPowerAction { + + public RecallPowerAction(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + if (!AbstractWorldObject.IsAbstractCharacter(awo) || source == null) + return; + AbstractCharacter awoac = (AbstractCharacter)awo; + + if (awo.getObjectType().equals(GameObjectType.PlayerCharacter)) { + + PlayerCharacter pc = (PlayerCharacter) awo; + + if (pc.hasBoon()) + return; + + ClientConnection cc = pc.getClientConnection(); + + if(source.getObjectUUID() != pc.getObjectUUID()) { + pc.setTimeStampNow("PromptRecall"); + pc.setTimeStamp("LastRecallType",1); //recall to bind + PromptRecallMsg promptRecallMsgmsg = new PromptRecallMsg(); + Dispatch dispatch = Dispatch.borrow(pc, promptRecallMsgmsg); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + + } else { + MovementManager.translocate(awoac, awoac.getBindLoc(), null); + } + } else { + Vector3fImmutable bindloc = awoac.getBindLoc(); + if (bindloc.x == 0.0f || bindloc.y == 0.0f) + awoac.setBindLoc(MBServerStatics.startX, MBServerStatics.startY, MBServerStatics.startZ); + awoac.teleport(awoac.getBindLoc()); + if (awoac.getObjectType() == GameObjectType.Mob){ + MobileFSM.setAwake((Mob)awoac,true); + if (awoac.isAlive()) + WorldGrid.updateObject(awoac); + } + + } + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/RemoveEffectPowerAction.java b/src/engine/powers/poweractions/RemoveEffectPowerAction.java new file mode 100644 index 00000000..d992ce81 --- /dev/null +++ b/src/engine/powers/poweractions/RemoveEffectPowerAction.java @@ -0,0 +1,62 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.Enum.EffectSourceType; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class RemoveEffectPowerAction extends AbstractPowerAction { + + + public EffectSourceType sourceType; + private boolean removeAll; + + public RemoveEffectPowerAction(ResultSet rs) throws SQLException { + super(rs); + String effectTypeToRemove = rs.getString("effectSourceToRemove").replace("-", "").trim(); + sourceType = EffectSourceType.GetEffectSourceType(effectTypeToRemove); + int flags = rs.getInt("flags"); + this.removeAll = ((flags & 2) != 0) ? true : false; + } + + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + if (awo != null) { + + + if (this.removeAll) + awo.removeEffectBySource(this.sourceType, trains, true); + else + if (this.getIDString().equals("SNC-003A")) + trains = 40; + awo.removeEffectBySource(this.sourceType, trains, false); + } + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/ResurrectPowerAction.java b/src/engine/powers/poweractions/ResurrectPowerAction.java new file mode 100644 index 00000000..85c04eab --- /dev/null +++ b/src/engine/powers/poweractions/ResurrectPowerAction.java @@ -0,0 +1,43 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class ResurrectPowerAction extends AbstractPowerAction { + + public ResurrectPowerAction(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/RunegateTeleportPowerAction.java b/src/engine/powers/poweractions/RunegateTeleportPowerAction.java new file mode 100644 index 00000000..559df3f9 --- /dev/null +++ b/src/engine/powers/poweractions/RunegateTeleportPowerAction.java @@ -0,0 +1,95 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.Enum; +import engine.Enum.RunegateType; +import engine.math.Vector3fImmutable; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.msg.PromptRecallMsg; +import engine.objects.*; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import static engine.math.FastMath.sqr; +import static engine.math.FastMath.sqrt; + +public class RunegateTeleportPowerAction extends AbstractPowerAction { + + /** + * ResultSet Constructor + */ + public RunegateTeleportPowerAction(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + + if (source == null || awo == null || !(awo .getObjectType().equals(Enum.GameObjectType.PlayerCharacter))) + return; + + PlayerCharacter pc = (PlayerCharacter) awo; + float dist = 9999999999f; + Building rg = null; + Vector3fImmutable rgLoc; + + for (Runegate runegate: Runegate.getRunegates()) { + + if ((runegate.getGateType() == RunegateType.OBLIV) || + (runegate.getGateType() == RunegateType.CHAOS)) + continue; + + for (Runegate thisGate : Runegate.getRunegates()) { + + rgLoc = thisGate.getGateType().getGateBuilding().getLoc(); + + float distanceToRunegateSquared = source.getLoc().distanceSquared2D(rgLoc); + + if (distanceToRunegateSquared < sqr(dist)) { + dist = sqrt(distanceToRunegateSquared); + rg = thisGate.getGateType().getGateBuilding(); + } + } + } + + if(source.getObjectUUID() != pc.getObjectUUID()) { + pc.setTimeStampNow("PromptRecall"); + pc.setTimeStamp("LastRecallType",0); //recall to rg + + if (rg != null) { + PromptRecallMsg promptRecallMsgmsg = new PromptRecallMsg(); + Dispatch dispatch = Dispatch.borrow(pc, promptRecallMsgmsg); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + } + + } else { + if (rg != null) { + pc.teleport(rg.getLoc()); + pc.setSafeMode(); + } + } + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/SetItemFlagPowerAction.java b/src/engine/powers/poweractions/SetItemFlagPowerAction.java new file mode 100644 index 00000000..e3571750 --- /dev/null +++ b/src/engine/powers/poweractions/SetItemFlagPowerAction.java @@ -0,0 +1,65 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.Enum; +import engine.gameManager.DbManager; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.CharacterItemManager; +import engine.objects.Item; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class SetItemFlagPowerAction extends AbstractPowerAction { + + public SetItemFlagPowerAction(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + + if (source == null || awo == null || !(awo .getObjectType().equals(Enum.GameObjectType.Item))) + return; + + Item item = (Item) awo; + + if (item.containerType != Enum.ItemContainerType.INVENTORY) + return; //Send an error here? + + //until this is shown to do something else, just use it as item identify spell. + item.setIsID(true); + + if (!DbManager.ItemQueries.UPDATE_FLAGS(item)) + item.setIsID(false); //update failed, reset + + //update inventory + CharacterItemManager cim = source.getCharItemManager(); + if (cim != null) + cim.updateInventory(); + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/SimpleDamagePowerAction.java b/src/engine/powers/poweractions/SimpleDamagePowerAction.java new file mode 100644 index 00000000..eb6a5b4d --- /dev/null +++ b/src/engine/powers/poweractions/SimpleDamagePowerAction.java @@ -0,0 +1,51 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class SimpleDamagePowerAction extends AbstractPowerAction { + + private int simpleDamage; + + public SimpleDamagePowerAction(ResultSet rs) throws SQLException { + super(rs); + + this.simpleDamage = rs.getInt("simpleDamage"); + } + + public int getSimpleDamage() { + return this.simpleDamage; + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/SpireDisablePowerAction.java b/src/engine/powers/poweractions/SpireDisablePowerAction.java new file mode 100644 index 00000000..7ad0bd30 --- /dev/null +++ b/src/engine/powers/poweractions/SpireDisablePowerAction.java @@ -0,0 +1,89 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.Enum.BuildingGroup; +import engine.Enum.GameObjectType; +import engine.gameManager.ChatManager; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.Building; +import engine.objects.PlayerCharacter; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class SpireDisablePowerAction extends AbstractPowerAction { + + /** + * ResultSet Constructor + */ + public SpireDisablePowerAction(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + if (awo == null) + return; + + if (source == null) + return; + + PlayerCharacter pc = null; + + if (source.getObjectType() == GameObjectType.PlayerCharacter) + pc = (PlayerCharacter)source; + else + return; + + if (awo.getObjectType() != GameObjectType.Building) + return; + + + //Check if Building is Spire. + + Building spire = (Building)awo; + + if ((spire.getBlueprintUUID() == 0) || + (spire.getBlueprint() != null && spire.getBlueprint().getBuildingGroup() != BuildingGroup.SPIRE)) { + ChatManager.chatSystemError((PlayerCharacter)source, "This Building is not a spire."); + return; + } + + if (!spire.isSpireIsActive()) + return; + + spire.disableSpire(false); + + if (trains > 20) + trains = 20; + + long duration = trains * 4500 + 30000; + spire.setTimeStamp("DISABLED", System.currentTimeMillis() + duration); + + + + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/StealPowerAction.java b/src/engine/powers/poweractions/StealPowerAction.java new file mode 100644 index 00000000..2cdc56db --- /dev/null +++ b/src/engine/powers/poweractions/StealPowerAction.java @@ -0,0 +1,201 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.Enum; +import engine.Enum.ItemType; +import engine.gameManager.ChatManager; +import engine.gameManager.CombatManager; +import engine.math.Vector3fImmutable; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.LootMsg; +import engine.objects.*; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; +import engine.server.MBServerStatics; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.concurrent.ThreadLocalRandom; + +import static engine.math.FastMath.sqr; + + +public class StealPowerAction extends AbstractPowerAction { + + /** + * ResultSet Constructor + */ + public StealPowerAction(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + + if (source == null || awo == null || !(source.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) || !(awo.getObjectType().equals(Enum.GameObjectType.Item))) + return; + + PlayerCharacter sourcePlayer = (PlayerCharacter) source; + + if (sourcePlayer.isSafeMode()) + return; + + if (!sourcePlayer.isAlive()) + return; + + //prevent stealing no steal mob loot + if (awo instanceof MobLoot && ((MobLoot)awo).noSteal()) + return; + + Item tar = (Item) awo; + AbstractWorldObject owner = (AbstractWorldObject) tar.getOwner(); + + if (owner == null) + return; + + + AbstractCharacter ownerAC = null; + if (AbstractWorldObject.IsAbstractCharacter(owner)) + ownerAC = (AbstractCharacter) owner; + + if (ownerAC != null) + if (ownerAC.getLoc().distanceSquared(sourcePlayer.getLoc()) > sqr(MBServerStatics.LOOT_RANGE)) + return; + + //only steal from players or mobs + + if (owner.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) { + + PlayerCharacter ownerPC = (PlayerCharacter)owner; + + if (ownerPC.isSafeMode() || sourcePlayer.inSafeZone() || ownerPC.inSafeZone()) + return; + + if (ownerPC.getLoc().distanceSquared(sourcePlayer.getLoc()) > sqr(MBServerStatics.LOOT_RANGE)) + return; + + //dupe check, validate player has item + if (!tar.validForInventory(ownerPC.getClientConnection(), ownerPC, ownerPC.getCharItemManager()))//pc.getCharItemManager())) + return; + + //mark thief and target as player aggressive + sourcePlayer.setLastPlayerAttackTime(); + ownerPC.setLastPlayerAttackTime(); + + //Handle target attacking back if in combat and has no other target + CombatManager.handleRetaliate(ownerAC, sourcePlayer); + + } else if (owner.getObjectType().equals(Enum.GameObjectType.Mob)) { + sourcePlayer.setLastMobAttackTime(); //mark thief as mob aggressive + } else + return; + + ClientConnection origin = sourcePlayer.getClientConnection(); + + if (origin == null) + return; + + int amount = getAmountToSteal(tar); + + //test probability of steal success + if (!stealSuccess(sourcePlayer, owner)) { + ChatManager.chatPeekSteal(sourcePlayer, ownerAC, tar, false, false, -1); + return; + } else { + ChatManager.chatPeekSteal(sourcePlayer, ownerAC, tar, true, false, amount); + //TODO send steal failure success + } + + //attempt transfer item + CharacterItemManager myCIM = sourcePlayer.getCharItemManager(); + CharacterItemManager ownerCIM = ((AbstractCharacter)owner).getCharItemManager(); + if (myCIM == null || ownerCIM == null) + return; + + if (tar.getItemBase().getType().equals(ItemType.GOLD)) { + //stealing gold + if (!myCIM.transferGoldToMyInventory((AbstractCharacter)owner, amount)) + return; + } else { + //stealing items + if (ownerCIM.lootItemFromMe(tar, sourcePlayer, origin, true, amount) == null) + return; + } + + //send loot message to person stealing. + LootMsg lm = new LootMsg(source.getObjectType().ordinal(), source.getObjectUUID(), owner.getObjectType().ordinal(), owner.getObjectUUID(), tar); + Dispatch dispatch = Dispatch.borrow(sourcePlayer, lm); + DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); + + //update thief's inventory + if (sourcePlayer.getCharItemManager() != null) + sourcePlayer.getCharItemManager().updateInventory(); + + //update victims inventory + if (owner.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) { + PlayerCharacter ownerPC = (PlayerCharacter) owner; + + if (ownerPC.getCharItemManager() != null) + ownerPC.getCharItemManager().updateInventory(); + } + + //TODO if victim is trading, cancel trade window for both people involved in trade + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + protected static boolean stealSuccess(PlayerCharacter pc, AbstractWorldObject awo) { + if (pc == null || awo == null || !AbstractWorldObject.IsAbstractCharacter(awo) || pc.getPowers() == null) + return false; + + int levelDif = pc.getLevel() - ((AbstractCharacter)awo).getLevel(); + + if (!pc.getPowers().containsKey(429396028)) + return false; + + CharacterPower cp = pc.getPowers().get(429396028); + int trains = cp.getTotalTrains(); + + float chance = 20 + (trains * 1.5f) + levelDif; + chance = (chance < 5f) ? 5f : chance; + chance = (chance > 85f) ? 85f : chance; + + float roll = ThreadLocalRandom.current().nextFloat() * 100f; + + return roll < chance; + + } + + //called to get amount of gold to steal between 0 and max gold + protected static int getAmountToSteal(Item i) { + if (i.getItemBase() != null && i.getItemBase().getUUID() == 7) { + int amount = i.getNumOfItems(); + if (amount < 1) + return -1; + int a = ThreadLocalRandom.current().nextInt(amount + 1); + int b = ThreadLocalRandom.current().nextInt(amount + 1); + int c = ThreadLocalRandom.current().nextInt(amount + 1); + return (a + b + c) / 3; + } else + return 0; + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/SummonPowerAction.java b/src/engine/powers/poweractions/SummonPowerAction.java new file mode 100644 index 00000000..32ef0e64 --- /dev/null +++ b/src/engine/powers/poweractions/SummonPowerAction.java @@ -0,0 +1,78 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.Enum; +import engine.gameManager.SessionManager; +import engine.gameManager.ZoneManager; +import engine.math.Vector3fImmutable; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.RecvSummonsRequestMsg; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.objects.PlayerCharacter; +import engine.objects.Zone; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class SummonPowerAction extends AbstractPowerAction { + + /** + * ResultSet Constructor + */ + public SummonPowerAction(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, + PowersBase pb) { + + if (source == null || awo == null || !(awo.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) || !(source.getObjectType().equals(Enum.GameObjectType.PlayerCharacter))) + return; + + PlayerCharacter target = (PlayerCharacter) awo; + + ClientConnection conn = SessionManager.getClientConnection(target); + + if (conn == null) + return; + + // TODO get location of summoning player + Zone zone = ZoneManager.findSmallestZone(source.getLoc()); + String location = "Somewhere"; + + if (zone != null) + location = zone.getName(); + + RecvSummonsRequestMsg rsrm = new RecvSummonsRequestMsg(source.getObjectType().ordinal(), source.getObjectUUID(), source.getFirstName(), + location, false); + + Dispatch dispatch = Dispatch.borrow(target, rsrm); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/TeleportPowerAction.java b/src/engine/powers/poweractions/TeleportPowerAction.java new file mode 100644 index 00000000..e04e642d --- /dev/null +++ b/src/engine/powers/poweractions/TeleportPowerAction.java @@ -0,0 +1,116 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.Enum; +import engine.Enum.ModType; +import engine.Enum.SourceType; +import engine.gameManager.MovementManager; +import engine.gameManager.PowersManager; +import engine.gameManager.ZoneManager; +import engine.math.Vector3fImmutable; +import engine.objects.*; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; +import engine.powers.effectmodifiers.AbstractEffectModifier; +import engine.server.MBServerStatics; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class TeleportPowerAction extends AbstractPowerAction { + + private boolean ignoreNoTeleSpire; + + public TeleportPowerAction(ResultSet rs) throws SQLException { + super(rs); + + int flags = rs.getInt("flags"); + this.ignoreNoTeleSpire = ((flags & 32768) != 0) ? true : false; + } + + public boolean ignoreNoTeleSpire() { + return this.ignoreNoTeleSpire; + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + + if (!AbstractWorldObject.IsAbstractCharacter(awo)) + return; + + AbstractCharacter awoac = (AbstractCharacter) awo; + + //verify targetLoc within range + + if (awo.getLoc().distanceSquared2D(targetLoc) > MBServerStatics.MAX_TELEPORT_RANGE * MBServerStatics.MAX_TELEPORT_RANGE) { + if (awo.equals(source)) + failTeleport(pb, awoac); + return; + } + + if (source.getBonuses().getBool(ModType.BlockedPowerType, SourceType.TELEPORT)) + return; + + City city = ZoneManager.getCityAtLocation(targetLoc); + + // Intentionally fail if target location is not on + // the actual city zone. + if (city != null) + if (city.isLocationOnCityZone(targetLoc) == false) + city = null; + + if (city != null){ + + for (String eff : city.getEffects().keySet()){ + + Effect spireEffect = city.getEffects().get(eff); + + for (AbstractEffectModifier aem : spireEffect.getEffectModifiers()){ + + if (aem.getType().equals("TELEPORT") && !this.ignoreNoTeleSpire){ + if (awo.equals(source)) + failTeleport(pb, awoac); + return; + } + } + } + } + + //TODO verify target loc is valid loc + + Regions region = Regions.GetRegionForTeleport(targetLoc); + + if (region != null && !region.isOutside()) + return; + + MovementManager.translocate(awoac,targetLoc, region); + } + + private static void failTeleport(PowersBase pb, AbstractCharacter awo) { + + if (pb == null || awo == null || (!(awo.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)))) + return; + + //teleport failed. Reset teleport power + PowersManager.finishRecycleTime(pb.getToken(), (PlayerCharacter) awo, true); + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + } +} \ No newline at end of file diff --git a/src/engine/powers/poweractions/TrackPowerAction.java b/src/engine/powers/poweractions/TrackPowerAction.java new file mode 100644 index 00000000..f12943c9 --- /dev/null +++ b/src/engine/powers/poweractions/TrackPowerAction.java @@ -0,0 +1,127 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.jobs.TrackJob; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.powers.ActionsBase; +import engine.powers.EffectsBase; +import engine.powers.PowersBase; +import engine.server.MBServerStatics; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; + + +public class TrackPowerAction extends AbstractPowerAction { + + private String effectID; + private boolean trackPlayer; + private boolean trackCorpse; + private boolean trackAll; + private boolean trackDragon; + private boolean trackGiant; + private boolean trackNPC; + private boolean trackUndead; + private boolean trackVampire; + private int maxTrack; + private EffectsBase effect; + + public TrackPowerAction(ResultSet rs, HashMap effects) throws SQLException { + super(rs); + + this.effectID = rs.getString("effectID"); + int flags = rs.getInt("flags"); + this.trackPlayer = ((flags & 1024) == 1) ? true : false; + this.trackCorpse = ((flags & 2048) == 1) ? true : false; + String trackFilter = rs.getString("trackFilter"); + this.trackAll = trackFilter.equals("All") ? true : false; + this.trackDragon = trackFilter.equals("Dragon") ? true : false; + this.trackGiant = trackFilter.equals("Giant") ? true : false; + this.trackNPC = trackFilter.equals("NPC") ? true : false; + this.trackUndead = trackFilter.equals("Undead") ? true : false; + this.trackVampire = trackFilter.equals("Vampire") ? true : false; + + this.maxTrack = rs.getInt("maxTrack"); + this.effect = effects.get(this.effectID); + } + + public String getEffectID() { + return this.effectID; + } + + public boolean trackPlayer() { + return this.trackPlayer; + } + + public boolean trackCorpse() { + return this.trackCorpse; + } + + public boolean trackAll() { + return this.trackAll; + } + + public boolean trackDragon() { + return this.trackDragon; + } + + public boolean trackGiant() { + return this.trackGiant; + } + + public boolean trackNPC() { + return this.trackNPC; + } + + public boolean trackUndead() { + return this.trackUndead; + } + + public boolean trackVampire() { + return this.trackVampire; + } + + public int getMaxTrack() { + return this.maxTrack; + } + + public EffectsBase getEffect() { + return this.effect; + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + if (source == null || awo == null || this.effect == null || pb == null || ab == null) { + //TODO log error here + return; + } + + //add schedule job to end it if needed and add effect to pc + int duration = MBServerStatics.TRACK_ARROW_SENSITIVITY; + String stackType = ab.getStackType(); + TrackJob eff = new TrackJob(source, awo, stackType, trains, ab, pb, this.effect, this); + source.addEffect(stackType, duration, eff, this.effect, trains); + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/TransferStatOTPowerAction.java b/src/engine/powers/poweractions/TransferStatOTPowerAction.java new file mode 100644 index 00000000..f3940233 --- /dev/null +++ b/src/engine/powers/poweractions/TransferStatOTPowerAction.java @@ -0,0 +1,68 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.jobs.TransferStatOTJob; +import engine.math.Vector3f; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.powers.ActionsBase; +import engine.powers.EffectsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; + + +public class TransferStatOTPowerAction extends TransferStatPowerAction { + + private int numIterations; + + public TransferStatOTPowerAction(ResultSet rs, HashMap effects) throws SQLException { + super(rs, effects); + + this.numIterations = rs.getInt("numIterations"); + } + + public int getNumIterations() { + return this.numIterations; + } + + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3f targetLoc, int trains, ActionsBase ab, PowersBase pb) { + this.__startAction(source, awo, trains, ab, pb); + } + + @Override + protected void __startAction(AbstractCharacter source, AbstractWorldObject awo, int trains, ActionsBase ab, PowersBase pb) { + if (this.effect == null || source == null || awo == null || ab == null || pb == null) + return; + + //add schedule job to end it if needed and add effect to pc + int duration = ab.getDuration(trains); + String stackType = ab.getStackType(); + stackType = (stackType.equals("IgnoreStack")) ? Integer.toString(ab.getUUID()) : stackType; + TransferStatOTJob eff = new TransferStatOTJob(source, awo, stackType, trains, ab, pb, this.effect, this); + int tick = eff.getTickLength(); + + if (duration > 0) + awo.addEffect(stackType, tick, eff, this.effect, trains); + + //start effect icon for client. Skip applying dot until first iteration. + eff.setSkipApplyEffect(true); + this.effect.startEffect(source, awo, trains, eff); + eff.setSkipApplyEffect(false); + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } +} diff --git a/src/engine/powers/poweractions/TransferStatPowerAction.java b/src/engine/powers/poweractions/TransferStatPowerAction.java new file mode 100644 index 00000000..cb2f7960 --- /dev/null +++ b/src/engine/powers/poweractions/TransferStatPowerAction.java @@ -0,0 +1,299 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.Enum; +import engine.Enum.DamageType; +import engine.Enum.ModType; +import engine.Enum.SourceType; +import engine.gameManager.ChatManager; +import engine.math.Vector3fImmutable; +import engine.net.AbstractNetMsg; +import engine.net.DispatchMessage; +import engine.net.client.msg.ModifyHealthKillMsg; +import engine.net.client.msg.ModifyHealthMsg; +import engine.objects.*; +import engine.powers.ActionsBase; +import engine.powers.EffectsBase; +import engine.powers.PowersBase; +import engine.powers.effectmodifiers.HealthEffectModifier; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.concurrent.ThreadLocalRandom; + +public class TransferStatPowerAction extends AbstractPowerAction { + + protected String effectID; + protected boolean transferFromHealth = false; + protected boolean transferFromMana = false; + protected boolean transferFromStamina = false; + protected boolean transferToHealth = false; + protected boolean transferToMana = false; + protected boolean transferToStamina = false; + protected float transferAmount; + protected float transferRamp; + protected boolean transferRampAdd; + protected float transferEfficiency; + protected float transferEfficiencyRamp; + protected boolean transferEfficiencyRampAdd; + protected boolean targetToCaster; + protected DamageType damageType; + protected EffectsBase effect; + + public TransferStatPowerAction(ResultSet rs, HashMap effects) throws SQLException { + super(rs); + this.effectID = rs.getString("effectID"); + String st = rs.getString("transferFromType"); + if (st.equals("HEALTH")) + this.transferFromHealth = true; + else if (st.equals("MANA")) + this.transferFromMana = true; + else + this.transferFromStamina = true; + st = rs.getString("transferToType"); + if (st.equals("HEALTH")) + this.transferToHealth = true; + else if (st.equals("MANA")) + this.transferToMana = true; + else + this.transferToStamina = true; + this.transferAmount = rs.getFloat("transferAmount"); + this.transferRamp = rs.getFloat("transferRamp"); + this.transferEfficiency = rs.getFloat("transferEfficiency"); + this.transferEfficiencyRamp = rs.getFloat("transferEfficiencyRamp"); + int flags = rs.getInt("flags"); + this.transferRampAdd = ((flags & 4096) != 0) ? true : false; + this.transferEfficiencyRampAdd = ((flags & 8192) != 0) ? true : false; + this.targetToCaster = ((flags & 16384) != 0) ? true : false; + this.effect = effects.get(this.effectID); + try { + String damageString = rs.getString("damageType"); + // Damage type can sometimes be null in the DB. + + if (damageString.isEmpty() == false) + this.damageType = DamageType.valueOf(damageString); + } catch (Exception e) { + this.damageType = null; + } + } + + public String getEffectID() { + return this.effectID; + } + + public boolean transferFromHealth() { + return this.transferFromHealth; + } + + public boolean transferFromMana() { + return this.transferFromMana; + } + + public boolean transferFromStamina() { + return this.transferFromStamina; + } + + public boolean transferToHealth() { + return this.transferToHealth; + } + + public boolean transferToMana() { + return this.transferToMana; + } + + public boolean transferToStamina() { + return this.transferToStamina; + } + + public EffectsBase getEffect() { + return this.effect; + } + + public float getTransferAmount(float trains) { + // if (this.transferRampAdd) + return this.transferAmount + (this.transferRamp * trains); + // else + // return this.transferAmount * (1 + (this.transferRamp * trains)); + } + + public float getTransferEfficiency(float trains) { + return this.transferEfficiency + (this.transferEfficiencyRamp * trains); + } + + public boolean targetToCaster() { + return this.targetToCaster; + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + this.__startAction(source, awo, trains, ab, pb); + } + + //Added for dependancy check on TransferStatOTPowerAction + protected void __startAction(AbstractCharacter source, AbstractWorldObject awo, int trains, ActionsBase ab, PowersBase pb) { + this.runAction(source, awo, trains, ab, pb); + } + + public void runAction(AbstractCharacter source, AbstractWorldObject awo, int trains, ActionsBase ab, PowersBase pb) { + if (source == null || awo == null || ab == null || pb == null) + return; + + if (!source.isAlive() || !awo.isAlive()) + return; + + AbstractWorldObject fromAwo; + AbstractWorldObject toAwo; + if (this.targetToCaster) { + fromAwo = awo; + toAwo = source; + } else { + fromAwo = source; + toAwo = awo; + } + + + + if (AbstractWorldObject.IsAbstractCharacter(fromAwo) && AbstractWorldObject.IsAbstractCharacter(toAwo)) { + AbstractCharacter from = (AbstractCharacter) fromAwo; + AbstractCharacter to = (AbstractCharacter) toAwo; + + //get amount to drain + float fromAmount = getTransferAmount(trains); + + //modify for resists if needed + if (this.damageType != null) { + Resists resists = from.getResists(); + if (resists != null) + fromAmount = resists.getResistedDamage(to, from, this.damageType, fromAmount * -1, trains) * -1; + } + + float min = fromAmount;// * (getTransferEfficiency(trains) / 100); + float max = min; + float damage = 0f; + + if (source.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) { + PlayerCharacter pc = (PlayerCharacter) source; + float focus; + CharacterSkill skill = pc.getSkills().get(pb.getSkillName()); + if (skill == null) + focus = CharacterSkill.getQuickMastery(pc, pb.getSkillName()); + else + focus = skill.getModifiedAmount(); + + //TODO fix this formula later + float intt = (pc.getStatIntCurrent() >= 1) ? (float)pc.getStatIntCurrent() : 1f; + float spi = (pc.getStatSpiCurrent() >= 1) ? (float)pc.getStatSpiCurrent() : 1f; + // min *= (intt * 0.0045 + 0.055 * (float)Math.sqrt(intt - 0.5) + spi * 0.006 + 0.07 * (float)Math.sqrt(spi - 0.5) + 0.02 * (int)focus); + // max *= (intt * 0.0117 + 0.13 * (float)Math.sqrt(intt - 0.5) + spi * 0.0024 + (float)Math.sqrt(spi - 0.5) * 0.021 + 0.015 * (int)focus); + // min *= (0.62 + 0.0192 * pc.getStatSpiCurrent() + 0.00415 * pc.getStatIntCurrent() + 0.015 * focus) / 2; + // max *= (0.62 + 0.0192 * pc.getStatIntCurrent() + 0.00415 * pc.getStatSpiCurrent() + 0.015 * focus) / 2; + min = HealthEffectModifier.getMinDamage(min, intt, spi, focus); + max = HealthEffectModifier.getMaxDamage(max, intt, spi, focus); + + // get range between min and max + float range = max - min; + + //debug for spell damage and atr + if (pc.getDebug(16)) { + String smsg = "Damage: " + (int)Math.abs(min) + " - " + (int)Math.abs(max); + ChatManager.chatSystemInfo(pc, smsg); + } + + // Damage is calculated twice to average a more central point + damage = ThreadLocalRandom.current().nextFloat() * range; + damage = (damage + (ThreadLocalRandom.current().nextFloat() * range)) / 2; + + // put it back between min and max + damage += min; + } + + // Apply any power effect modifiers (such as stances) + PlayerBonuses bonus = source.getBonuses(); + if (bonus != null) + damage *= (1 + bonus.getFloatPercentAll(ModType.PowerDamageModifier, SourceType.None)); + + //get amount to transfer + fromAmount = damage; + float toAmount = fromAmount * (getTransferEfficiency(trains) / 100); + + //get max amount to transfer, don't give more then the target has + float maxDrain; + if (this.transferFromHealth) + maxDrain = from.getCurrentHitpoints(); + else if (this.transferFromMana) + maxDrain = from.getMana(); + else + maxDrain = from.getStamina(); + if (toAmount > maxDrain) + toAmount = maxDrain; + + //prep messages for transfer + int powerID = pb.getToken(); + int effectID = 496519310; + String powerName = pb.getName(); + ModifyHealthMsg mhmTo; + // ModifyHealthMsg mhmFrom; + AbstractNetMsg mhmFrom = null; + + //stop if target is immune to drains + if ( from.getBonuses().getBool(ModType.ImmuneTo, SourceType.Drain)) { + ModifyHealthMsg mhm = new ModifyHealthMsg(source, to, 0f, 0f, 0f, powerID, powerName, trains, effectID); + mhm.setUnknown03(5); //set target is immune + DispatchMessage.sendToAllInRange(from, mhm); + return; + } + + //apply transfer bonus + if (this.transferToHealth) { + to.modifyHealth(toAmount, source, false); + mhmTo = new ModifyHealthMsg(source, to, toAmount, 0f, 0f, powerID, powerName, trains, effectID); + } else if (this.transferToMana) { + to.modifyMana(toAmount, source); + mhmTo = new ModifyHealthMsg(source, to, 0f, toAmount, 0f, powerID, powerName, trains, effectID); + } else { + to.modifyStamina(toAmount, source); + mhmTo = new ModifyHealthMsg(source, to, 0f, 0f, toAmount, powerID, powerName, trains, effectID); + } + + //subtract transfer amount + if (this.transferFromHealth) { + float modFrom = from.modifyHealth(-fromAmount, source, false); + float cur = from.getHealth(); + if (cur < 0 && modFrom != 0) + mhmFrom = new ModifyHealthKillMsg(source, from, -fromAmount, 0f, 0f, powerID, powerName, trains, effectID); + else + mhmFrom = new ModifyHealthMsg(source, from, -fromAmount, 0f, 0f, powerID, powerName, trains, effectID); + } else if (this.transferFromMana) { + from.modifyMana(-fromAmount, source); + mhmFrom = new ModifyHealthMsg(source, from, 0f, -fromAmount, 0f, powerID, powerName, trains, effectID); + } else { + from.modifyStamina(-fromAmount, source); + mhmFrom = new ModifyHealthMsg(source, from, 0f, 0f, -fromAmount, powerID, powerName, trains, effectID); + } + + DispatchMessage.sendToAllInRange(to, mhmTo); + DispatchMessage.sendToAllInRange(from, mhmFrom); + + } + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/TransformPowerAction.java b/src/engine/powers/poweractions/TransformPowerAction.java new file mode 100644 index 00000000..6f8d0be2 --- /dev/null +++ b/src/engine/powers/poweractions/TransformPowerAction.java @@ -0,0 +1,71 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.jobs.FinishEffectTimeJob; +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.powers.ActionsBase; +import engine.powers.EffectsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; + + +public class TransformPowerAction extends AbstractPowerAction { + + private String effectID; + private EffectsBase effect; + + public TransformPowerAction(ResultSet rs, HashMap effects) throws SQLException { + super(rs); + + this.effectID = rs.getString("effectID"); + this.effect = effects.get(this.effectID); + } + + public String getEffectID() { + return this.effectID; + } + + public EffectsBase getEffect() { + return this.effect; + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + if (this.effect == null || pb == null || ab == null) { + //TODO log error here + return; + } + + int duration = ab.getDuration(trains); + String stackType = ab.getStackType(); + stackType = (stackType.equals("IgnoreStack")) ? Integer.toString(ab.getUUID()) : stackType; + FinishEffectTimeJob eff = new FinishEffectTimeJob(source, awo, stackType, trains, ab, pb, effect); + if (duration > 0) + awo.addEffect(stackType, duration, eff, effect, trains); + this.effect.startEffect(source, awo, trains, eff); + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/powers/poweractions/TreeChokePowerAction.java b/src/engine/powers/poweractions/TreeChokePowerAction.java new file mode 100644 index 00000000..00a93f14 --- /dev/null +++ b/src/engine/powers/poweractions/TreeChokePowerAction.java @@ -0,0 +1,43 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.powers.poweractions; + +import engine.math.Vector3fImmutable; +import engine.objects.AbstractCharacter; +import engine.objects.AbstractWorldObject; +import engine.powers.ActionsBase; +import engine.powers.PowersBase; + +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class TreeChokePowerAction extends AbstractPowerAction { + + public TreeChokePowerAction(ResultSet rs) throws SQLException { + super(rs); + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + + } + + @Override + protected void _handleChant(AbstractCharacter source, AbstractWorldObject target, Vector3fImmutable targetLoc, int trains, ActionsBase ab, PowersBase pb) { + } + + @Override + protected void _startAction(AbstractCharacter source, AbstractWorldObject awo, Vector3fImmutable targetLoc, + int numTrains, ActionsBase ab, PowersBase pb, int duration) { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/server/MBServerStatics.java b/src/engine/server/MBServerStatics.java new file mode 100644 index 00000000..6c2b46fb --- /dev/null +++ b/src/engine/server/MBServerStatics.java @@ -0,0 +1,830 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.server; + +import engine.Enum; +import engine.gameManager.ConfigManager; +import engine.math.Vector3fImmutable; + +public class MBServerStatics { + + public static final int revisionNumber = 1; + + public static String getEmulatorVersion() { + return Integer.toString(revisionNumber); + } + + public static final String CMDLINE_ARGS_EXE_NAME_DELIMITER = "-name"; + public static final String CMDLINE_ARGS_CONFIG_FILE_PATH_DELIMITER = "-config"; + public static final String CMDLINE_ARGS_CALLER_DELIMITER = "-caller"; + public static final String CMDLINE_ARGS_REASON_DELIMITER = "-reason"; + public static final String EXISTING_CONNECTION_CLOSED = "An existing connection was forcibly closed by the remote host"; + public static final String RESET_BY_PEER = "Connection reset by peer"; + /* + * ####Debugging Flags#### + */ + public static final boolean POWERS_DEBUG = false; + public static final boolean MOVEMENT_SYNC_DEBUG = false; + public static final boolean BONUS_TRAINS_ENABLED = false; + public static final boolean REGENS_DEBUG = false; + public static final boolean SHOW_SAFE_MODE_CHANGE = false; + public static final boolean COMBAT_TARGET_HITBOX_DEBUG = false; // output + // hit box + // calcs + public static final boolean PRINT_INCOMING_OPCODES = false; // print + // incoming + // opcodes to + // console + + public static final int BANK_GOLD_LIMIT = 25000000; + public static final int PLAYER_GOLD_LIMIT = 10000000; + public static final int BUILDING_GOLD_LIMIT = 15000000; + + public static final String VENDOR_FULL = "This vendor has no more gold to give."; + public static final boolean HEIGHTMAP_DEBUG = false; + public static final boolean FAST_LOAD = false; // skip loading mobs, + // buildings, npcs + public static final boolean FAST_LOAD_INIT = false; // skip loading mobs, + // buildings, npcs + /* + * Login cache flags + */ + public static final boolean SKIP_CACHE_LOGIN = false; // skip caching // login server + public static final boolean SKIP_CACHE_LOGIN_PLAYER = false; // skip caching // on login + public static final boolean SKIP_CACHE_LOGIN_ITEM = false; // skip caching + + /* + * Logger + */ + public static final int bannerWidth = 80; + public static final int typeWidth = 10; + public static final int originWidth = 25; + public static final int logWidth = 80; + + /* + * ConfigSystem related + */ + public static final String DEFAULT_CONFIG_DIR = "mb.conf/"; + public static final String DEFAULT_DATA_DIR = "mb.data/"; + /* + * ChatManager related + */ + public static final int SHOUT_PERCEPTION_RADIUS_MOD = 2; + /* + * DevCmd related + */ + public static final String DEV_CMD_PREFIX = "./"; + + /* + * JobManager related + */ + + // The number of elements in INITIAL_WORKERS defines the initial number of + // job pools + public static final int[] INITIAL_JOBPOOL_WORKERS = { 4, 2, 1 }; + public static final int DEFAULT_JOBPOOL_WORKERS = 1; + public static final int DEFAULT_LOGIN_JOBPOOL_WORKERS = 5; + + public static final int JOBWORKER_IDLE_TIMEOUT_MS = 750; + public static final int JOBMANAGER_INTERNAL_MONITORING_INTERVAL_MS = 1000; + public static final int JOB_STALL_THRESHOLD_MS = 120 * 1000; + public static final int MAX_JOB_HISTORY_OBJECTS = 1000; // max number of + // historic jobs to + // store on queue + // after execution + public static final int JOBSTATISTICS_WAKE_INTERVAL_MS = 500; // wake up and + // gather + // job stats + // every X + // ms, + // decrease + // this is + // we blow + // the job + // history + // queue + + public static final int SCHEDULER_INITIAL_CAPACITY = 1000; + public static final int SCHEDULER_EXECUTION_TIME_COMPENSATION = 16; + + /* + * Concurrent Hash Map - Defaults + */ + + public static final int CHM_INIT_CAP = 10; + public static final float CHM_LOAD = 0.75f; + public static final int CHM_THREAD_HIGH = 4; + public static final int CHM_THREAD_MED = 2; + public static final int CHM_THREAD_LOW = 1; + + /* + * LoginServer related + */ + + public static final String PCMajorVer = "1.2.25.5"; + public static final String PCMinorVer = "5.25.5"; + + public static final String MACMajorVer = "1.2.24.3"; + public static final String MACMinorVer = "5.24.3"; + + /* + * LoginErrorMsg related + */ + public static final int LOGINERROR_INVALID_USERNAME_PASSWORD = 1; + public static final int LOGINERROR_ACCOUNT_SUSPENDED = 2; + + /* + * Message is Version: + */ + public static final int LOGINERROR_INCORRECT_CLIENT_VERSION = 3; + public static final int LOGINERROR_NOT_ALLOWED_TO_LOGIN_YET = 4; + + /* + * Message is 'Error =' + */ + public static final int LOGINERROR_LOGINSERVER_IS_UNAVAILABLE = 5; + public static final int LOGINERROR_INVALID_ADMIN_USERNAME_PASSWORD = 6; + public static final int LOGINERROR_NO_MORE_PLAYTIME_ON_ACCOUNT = 7; + public static final int LOGINERROR_ACCOUNT_DOESNT_HAVE_SUBSCRIPTION = 8; + public static final int LOGINERROR_ACCOUNT_INSECURE_CHANGE_PASSWORD = 9; + public static final int LOGINERROR_TOO_MANY_LOGIN_TRIES = 10; + + /* + * Message is 'Error =' + */ + public static final int LOGINERROR_NOMOREPLAYTIME = 7; + public static final int LOGINERROR_INACTIVE = 8; + public static final int LOGINERROR_UNABLE_TO_LOGIN = 11; + public static final int LOGINERROR_LOGINSERVER_BUSY = 12; + public static final int LOGINERROR_BLANK = 13; + + /* + * >13 = 'blank' 12 = 'Login Server is currently busy, please try again in a + * few minutes.' 11 = 'Unable to login. Please try again. If this problem + * persists, contact customer support (error = )' 10 = 'You have made too + * many unsuccessful login attempts, you must wait 15 minutes.' 9 = 'Your + * Account is insecure, you must change your password before logging in + * again.' 8 = 'This Account does not have an active Shadowbane + * Subscription' 7 = 'No More PlayTime on this account 6 = 'Invalid + * Administrator Username/Password' 5 = 'Login Server Is Unavailable (Error + * = 0)' 4 = 'YouAreNotAllowedToLoginYet' 3 = 'Incorrect ClientVersion, + * latest is' 2 = 'This Account Has Been Suspended' 1 = 'Invalid + * Username/Password' + */ + + /* + * Name Validation Related + */ + public static final int INVALIDNAME_FIRSTNAME_MUST_BE_LONGER = 1; + public static final int INVALIDNAME_FIRSTANDLAST_MUST_BE_SHORTER = 2; + public static final int INVALIDNAME_FIRSTNAME_MUST_NOT_HAVE_SPACES = 3; + public static final int INVALIDNAME_FIRSTNAME_INVALID_CHARACTERS = 4; + public static final int INVALIDNAME_PLEASE_CHOOSE_ANOTHER_FIRSTNAME = 5; + public static final int INVALIDNAME_PLEASE_CHOOSE_ANOTHER_LASTNAME = 7; + public static final int INVALIDNAME_LASTNAME_UNAVAILABLE = 8; + public static final int INVALIDNAME_FIRSTNAME_UNAVAILABLE = 9; + public static final int INVALIDNAME_WRONG_WORLD_ID = 10; + public static final int INVALIDNAME_GENERIC = 11; + + /* + * 1: A first name of at least 3 character(s) must be entered 2: Your first + * and last name cannot be more than 15 characters each. 3: Your first name + * may not contain spaces 4: There are invalid characters in the first name. + * 5: Please choose another first name 7: Please choose another last name 8: + * That last name is unavailable 9: That first name is unavailable 10: Your + * client sent an invalid world id 11: Invalid name. Choose another + */ + public static final int MIN_NAME_LENGTH = 3; + public static final int MAX_NAME_LENGTH = 15; + + /* + * ClientConnection related + */ + public static final boolean TCP_NO_DELAY_DEFAULT = true; + public static final byte MAX_CRYPTO_INIT_TRIES = 10; + + + /* + * EmuConnectionManager related + */ + public static final long delayBetweenConnectionChecks = 5000L; // in ms + public static final long delayBetweenReconnectAttempts = 2000L; // in ms + public static final int maxReconnectAttempts = 20; + public static final long reconnectTimeout = 15000L; + public static boolean DEBUG_PROTOCOL = false; + /* + * Account Related + */ + + public static final byte MAX_LOGIN_ATTEMPTS = 5; + public static final int RESET_LOGIN_ATTEMPTS_AFTER = (15 * 60 * 1000); // in + // ms + public static final int MAX_ACTIVE_GAME_ACCOUNTS_PER_DISCORD_ACCOUNT = 4; // 0 + // to + // disable + /* + * Character related + */ + + public static final byte MAX_NUM_OF_CHARACTERS = 7; + + public static final int STAT_STR_ID = 0x8AC3C0E6; + public static final int STAT_SPI_ID = 0xACB82E33; + public static final int STAT_CON_ID = 0xB15DC77E; + public static final int STAT_DEX_ID = 0xE07B3336; + public static final int STAT_INT_ID = 0xFF665EC3; + + /* + * Skill attributeIDs + */ + + public static final int SKILL_RUNNING = 5; + + /* + * EquipSlot + */ + + public static final int SLOT_UNEQUIPPED = 0; + public static final int SLOT_MAINHAND = 1; + public static final int SLOT_OFFHAND = 2; + public static final int SLOT_HELMET = 3; + public static final int SLOT_CHEST = 4; + public static final int SLOT_ARMS = 5; + public static final int SLOT_GLOVES = 6; + public static final int SLOT_RING1 = 7; + public static final int SLOT_RING2 = 8; + public static final int SLOT_NECKLACE = 9; + public static final int SLOT_LEGGINGS = 10; + public static final int SLOT_FEET = 11; + public static final int SLOT_HAIRSTYLE = 18; // 17 & 18? Weird. + public static final int SLOT_BEARDSTYLE = 17; // 17 & 18? Weird. + + // Equip[0] = Slot1 = Weapon MainHand + // Equip[1] = Slot2 = OffHand + // Equip[2] = Slot3 = Helmet + // Equip[3] = Slot4 = Chest + // Equip[4] = Slot5 = Arms + // Equip[5] = Slot6 = Gloves + // Equip[6] = Slot7 = Ring1 + // Equip[7] = Slot8 = Ring2 + // Equip[8] = Slot9 = Necklace + // Equip[9] = Slot10 = Leggings + // Equip[10] = Slot11 = Feet + // Equip[11] = Slot17 = HairStyle + // Equip[12] = Slot18 = BeardStyle + + /* + * Group Formation Names + */ + public static final String[] FORMATION_NAMES = { "Column", "Line", "Box", + "Triangle", "Circle", "Ranks", "Wedge", "Inverse Wedge", "T" }; + + /* + * Runes + */ + + public static final int RUNETYPE_TRAIT = 1; + + public static final int RUNE_COST_ATTRIBUTE_ID = 0; + + public static final int RUNE_STR_ATTRIBUTE_ID = 1; + public static final int RUNE_DEX_ATTRIBUTE_ID = 2; + public static final int RUNE_CON_ATTRIBUTE_ID = 3; + public static final int RUNE_INT_ATTRIBUTE_ID = 4; + public static final int RUNE_SPI_ATTRIBUTE_ID = 5; + + public static final int RUNE_STR_MAX_ATTRIBUTE_ID = 6; + public static final int RUNE_DEX_MAX_ATTRIBUTE_ID = 7; + public static final int RUNE_CON_MAX_ATTRIBUTE_ID = 8; + public static final int RUNE_INT_MAX_ATTRIBUTE_ID = 9; + public static final int RUNE_SPI_MAX_ATTRIBUTE_ID = 10; + + public static final int RUNE_STR_MIN_NEEDED_ATTRIBUTE_ID = 11; + public static final int RUNE_DEX_MIN_NEEDED_ATTRIBUTE_ID = 12; + public static final int RUNE_CON_MIN_NEEDED_ATTRIBUTE_ID = 13; + public static final int RUNE_INT_MIN_NEEDED_ATTRIBUTE_ID = 14; + public static final int RUNE_SPI_MIN_NEEDED_ATTRIBUTE_ID = 15; + + /* + * DBMan + */ + public static final int NO_DB_ROW_ASSIGNED_YET = Integer.MAX_VALUE; + + /* + * PreparedStatement query debugging + */ + public static final boolean DB_DEBUGGING_ON_BY_DEFAULT = false; // warning: + // not + // recommended + // for a + // live + // production + // server + public static final boolean ENABLE_QUERY_TIME_WARNING = true; + public static final boolean ENABLE_UPDATE_TIME_WARNING = true; + public static final boolean ENABLE_EXECUTION_TIME_WARNING = true; + + /* + * ClientEncryption + */ + public static final int AUTHENTICATION_WAIT_TIMEOUT = 1000 * 2; // seconds + public static final int MaxGetKeyFromClientTries = 4; + public static final int MaxProtocolMessagesPerSecond = 20; // 60 per second + + /* + * Guild Colors + */ + + // public static final int GUILD_COLOR_LIGHTGREEN = 0; + // public static final int GUILD_COLOR_GREEN = 1; + // public static final int GUILD_COLOR_DARKGREEN = 2; + // public static final int GUILD_COLOR_LIGHTBLUE = 3; + // public static final int GUILD_COLOR_BLUE = 4; + // public static final int GUILD_COLOR_DARKBLUE = 5; + // public static final int GUILD_COLOR_PURPLE = 6; + // public static final int GUILD_COLOR_DARKRED = 7; + // public static final int GUILD_COLOR_LIGHTRED = 8; + // public static final int GUILD_COLOR_ORANGE = 9; + // public static final int GUILD_COLOR_BROWNORANGE = 10; + // public static final int GUILD_COLOR_BROWN = 11; + // public static final int GUILD_COLOR_BROWNYELLOW = 12; + // public static final int GUILD_COLOR_YELLOW = 13; + // public static final int GUILD_COLOR_LIGHTGREY = 14; + // public static final int GUILD_COLOR_GREY = 15; + // public static final int GUILD_COLOR_DARKGREY = 16; + // public static final int GUILD_COLOR_BLACK = 17; + // public static final int GUILD_COLOR_BLUEGREEN = 18; + // public static final int GUILD_COLOR_WHITE = 19; + + /* + * Timeout Related + */ + public static final int AFK_TIMEOUT_MS = (30 * 60 * 1000) * 100; // Added + // *100 + // to + // discount + // it as + // a + // "random DC reason" + public static final int KEEPALIVE_TIMEOUT_MS = (2 * 60 * 1000) + + (15 * 1000); + public static final int TIMEOUT_CHECKS_TIMER_MS = (60 * 1000); + + /* + * Masks for Quad Tree. Masks should be multiple of 2. + */ + + public static final int MASK_PLAYER = 1; + public static final int MASK_MOB = 2; + public static final int MASK_PET = 4; + public static final int MASK_CORPSE = 8; + public static final int MASK_BUILDING = 16; + public static final int MASK_UNDEAD = 64; + public static final int MASK_BEAST = 128; + public static final int MASK_HUMANOID = 256; + public static final int MASK_NPC = 512; + public static final int MASK_IAGENT = 2048; + + public static final int MASK_DRAGON = 4096; + public static final int MASK_RAT = 8192; + public static final int MASK_SIEGE = 16384; + public static final int MASK_CITY = 32768; + public static final int MASK_ZONE = 65536; + + /* + * Combined QT Masks. For convenience + */ + + public static final int MASK_AGGRO = 5; // Player, Pet + public static final int MASK_MOBILE = 7; // Player, Mob, Pet + public static final int MASK_STATIC = 568; // Corpse, Building, Trigger, NPC + + /* + * World Coordinate Data + */ + public static final double MAX_WORLD_HEIGHT = -98304.0; + public static final double MAX_WORLD_WIDTH = 131072.0; + public static final float SEA_FLOOR_ALTITUDE = -1000f; + public static int SPATIAL_HASH_BUCKETSX = 16384; + public static int SPATIAL_HASH_BUCKETSY = 12288; + public static float MAX_PLAYER_X_LOC = 129999; + public static float MAX_PLAYER_Y_LOC = -97000; + public static String NO_DELETE_COMBAT = "Can't delete items when in Combat with another player."; + + /* + * Rates + */ + + public static float EXP_RATE_MOD = 2f; // Probably don't want to declare + // as final. + public static float GOLD_RATE_MOD = 1.0f; // Probably don't want to declare + // as final. + public static float DROP_RATE_MOD = 1.0f; // Probably don't want to declare + // as final. + + // Hotzones + public static float HOT_EXP_RATE_MOD = 2.0f; // Probably don't want to + // declare as final. + public static float HOT_GOLD_RATE_MOD = 1.5f; // Probably don't want to + // declare as final. + public static float HOT_DROP_RATE_MOD = 1.8f; // Probably don't want to + // declare as final. + + /* + * Ranges + */ + public static final int CHARACTER_LOAD_RANGE = 400; // load range of mobile objects + // (default: 300) + public static final int STRUCTURE_LOAD_RANGE = 700; // load range of + // (default: 600) + + public static float LOOT_RANGE = 100; + public static final int EXP_RANGE = 400; + public static final int GOLD_SPLIT_RANGE = 600; + // non-moving objects + public static final int SAY_RANGE = 200; + public static final int SHOUT_RANGE = 300; + public static final int STATIC_THRESHOLD = 75; // Range must travel before + // reloading statics + public static final int FORMATION_RANGE = 75; // Max Distance a player can + // be from group lead on + // formation move + public static final int OPENCLOSEDOORDISTANCE = 128; // Max distance a + public static final int DOOR_CLOSE_TIMER = 30000; // 30 seconds + // player can be from a door in order to toggle its state + public static final int TRADE_RANGE = 10; // Max distance a player can be + // from another player to trade + public static final int NPC_TALK_RANGE = 20; // Range player can be to talk + // to npc + public static final int MAX_TELEPORT_RANGE = 1020; // Max range teleports + // will work at + public static final int RANGED_WEAPON_RANGE = 35; // any weapon attack + // range beyond this + // is ranged. + public static final int CALL_FOR_HELP_RADIUS = 100; // Range mobs will + // respond to calls + // for help + + public static final int TREE_TELEPORT_RADIUS = 30; + + public static float MOB_SPEED_WALK = 6.5f; + + public static float MOB_SPEED_WALKCOMBAT = 4.4f; + + public static float MOB_SPEED_RUN = 14.67f; + + public static float MOB_SPEED_RUNCOMBAT = 14.67f; + + + /* + * Noob Island Start Location for new players + */ + + public static final int[] DEFAULTGRID = {-1,1}; + public static final float startX = 19128;// 70149f; //19318.0f; + public static final float startY = 94f; // 94f; + public static final float startZ = -73553; // -73661.0f; + public static final Vector3fImmutable DEFAULT_START = new Vector3fImmutable( + MBServerStatics.startX, MBServerStatics.startY, + MBServerStatics.startZ); + + /* + * Base movement speeds. Do NOT modify these. They must match the client + */ + public static final float FLYWALKSPEED = 6.33f; + public static final float FLYRUNSPEED = 18.38f; + public static final float SWIMSPEED = 6.5f; + public static final float WALKSPEED = 6.5f; + public static final float RUNSPEED = 14.67f; + public static final float COMBATWALKSPEED = 4.44f; + public static final float COMBATRUNSPEED = 14.67f; + public static final float RUNSPEED_MOB = 15.4f; + + public static final float MOVEMENT_DESYNC_TOLERANCE = 2f; // Distance out of + public static String ITEMNOTINVENTORY = "Item must be in your inventory."; + public static String ZEROITEM = "This item has zero quantity."; + // sync with + // client can be + // before + // generating + // debug + // messages + // max units a player can desync before the server stops forcing + // client->server sync + public static final float MOVEMENT_MAX_DESYNC = 1000; + + public static final int IGNORE_LIST_MAX = 60; + + public static final float NO_WEAPON_RANGE = 8f; // Range for attack with no + // weapon + + + public static final float REGEN_IDLE = .06f; + /* + * Base regen rates. Do NOT modify these. They must match the client %per + * second for health/mana. x per second for stamina. + */ + public static final float HEALTH_REGEN_SIT = 0.0033333f; // 100% in 3 + // minutes + public static final float HEALTH_REGEN_IDLE = 0.000666667f; // 100% in 25 + // minutes + public static final float HEALTH_REGEN_WALK = 0.0005f; // 100% in 33.33 + // minutes + public static final float HEALTH_REGEN_RUN = 0f; + public static final float HEALTH_REGEN_SWIM_NOSTAMINA = -.03f; // 100% in + // 33.33 + // seconds. + // Needs + // verified + + public static final float HEALTH_REGEN_SIT_STATIC = 0.33333f; // 100% in 3 + // minutes + public static final float HEALTH_REGEN_IDLE_STATIC = 0.0666667f; // 100% in + // 25 + // minutes + public static final float HEALTH_REGEN_WALK_STATIC = 0.05f; // 100% in 33.33 + // minutes + public static final float HEALTH_REGEN_RUN_STATIC = 0f; + public static final float HEALTH_REGEN_SWIM_NOSTAMINA_STATIC = 0f; // 100% + + public static final float MANA_REGEN_STATIC = 0.16666666666666666666666666666667f; + // in 30 + // seconds. + // Needs + // verified + + public static final float MANA_REGEN_SIT = 0.008333333f; // 100% in 2 + // minutes <= + // needs + // verified + public static final float MANA_REGEN_IDLE = 0.00166667f; // 100% in 10 + // minutes <= + // needs + // verified + public static final float MANA_REGEN_WALK = 0.00125f; // 100% in 13.333 + // minutes <= needs + // verified + public static final float MANA_REGEN_RUN = 0f; + + public static final float STAMINA_REGEN_SIT = 2f; // 2 per second + public static final float STAMINA_REGEN_IDLE = 0.2f; // 1 per 5 seconds + public static final float STAMINA_REGEN_WALK = 0f; + public static final float STAMINA_REGEN_RUN_COMBAT = -0.6499999762f; + public static final float STAMINA_REGEN_RUN_NONCOMBAT = -0.400000006f; + public static final float STAMINA_REGEN_SWIM = -1f; // -1 per second + public static float STAMINA_REGEN_FLY_IDLE = -2f; // needs verifying + public static float STAMINA_REGEN_FLY_WALK = -1f; // needs verifying + public static float STAMINA_REGEN_FLY_RUN = -1.400000006f; // needs verifying + public static float STAMINA_REGEN_FLY_RUN_COMBAT = -1.6499999762f; // needs verifying + + public static final int REGEN_SENSITIVITY_PLAYER = 250; // calc regen ever X + // ms + public static final int REGEN_SENSITIVITY_MOB = 1000; // calc regen ever X + // ms + /* + * Tombstone type to show Tombstone (2022); Tombstone, Grave (2023); + * Tombstone, Skull (2024); + */ + public static final int TOMBSTONE = 2024; + public static final int DEATH_SHROUD_DURATION = 1; // 3 minute death shroud + public static final int SAFE_MODE_DURATION = 1; // 3 minute safe mode + + /* + * Timers + */ + public static final int LOGOUT_TIMER_MS = 1000; // logout delay applied + // after the last + // aggressive action + public static final int CLEANUP_TIMER_MS = 15 * 60 * 1000; // Remove player + // from cache + // after 15 + // minutes + public static final int CORPSE_CLEANUP_TIMER_MS = 15 * 60 * 1000; // Cleanup + // corpse + // in + // world + // after + // 15 + // minutes + public static final int DEFAULT_SPAWN_TIME_MS = 3 * 60 * 1000; // 3 minute + // respawn + // on mobs + // default + public static final int SESSION_CLEANUP_TIMER_MS = 30 * 1000; // cleanup + // sessions + // for login + // 30 + // seconds + // after + // logout + public static final int MOVEMENT_FREQUENCY_MS = 1000; // Update movement + // once every X ms + public static final int FLY_FREQUENCY_MS = 1000; // Update flight once every + + public static final float FLY_RATE = .0078f; + // x ms + public static final int HEIGHT_CHANGE_TIMER_MS = 125; // Time in ms to fly + // up or down 1 unit + public static final long OPCODE_HANDLE_TIME_WARNING_MS = 250L; + public static final long DB_QUERY_WARNING_TIME_MS = 250L; + public static final long DB_UPDATE_WARNING_TIME_MS = 250L; + public static final long DB_EXECUTION_WARNING_TIME_MS = 250L; + public static boolean DB_ENABLE_QUERY_OUTPUT = false; + public static final int SUMMON_MAX_WAIT = 18000; // 18 seconds to accept + // summons + public static final int THIRTY_SECONDS = 30000; + public static final int FOURTYFIVE_SECONDS = 45000; + public static final int ONE_MINUTE = 60000; + public static final int FIVE_MINUTES = 300000; + public static final int FIFTEEN_MINUTES = 900000; + public static final int THIRTY_MINUTES = 1800000; + public static final long TWENTY_FOUR_HOURS = 86400000; + public static final int LOAD_OBJECT_DELAY = 500; // long to wait to update + public static int IPLimit = 5000; + // group list after + // LoadChar + public static final int UPDATE_LINK_WORLD = 2500; + public static final int UPDATE_LINK_LOGIN = 2500; + public static final int TELEPORT_TIME_IN_SECONDS = 10; + public static final int REPLEDGE_TIME_IN_SECONDS = 0; + public static final int CHECK_DATABASE_UPDATES = 10000; // update database + // changes every 10 + // seconds. + public static final int RUNEGATE_CLOSE_TIME = 30000; // runegate close timer + public static final long PLAYER_KILL_XP_TIMER = 60 * 60 * 1000; // 60 + // minutes + // between + // grant xp + // on same + // target + public static final int UPDATE_GROUP_RATE = 10000; // Update group info + // every 10 seconds + public static float PLAYER_HATE_DELIMITER = 50; // reduces 50 hate a second + // while player idling. + public static float PLAYER_COMBAT_HATE_MODIFIER = 2; + + /* + * AI + */ + + // The min distance from players at which the AI Manager feels safe to turn + // off a mob. + public static int AI_BASE_AGGRO_RANGE = 60; + public static int AI_DROP_AGGRO_RANGE = 60; + public static int AI_RECALL_RANGE = 400; + public static int AI_PULSE_MOB_THRESHOLD = 200; + public static int AI_THREAD_SLEEP = 1000; + public static int AI_PATROL_DIVISOR = 10; + public static int AI_POWER_DIVISOR = 20; + public static int AI_PET_HEEL_DISTANCE = 10; + public static int AI_PATROL_RADIUS = 60; + + public static float AI_MAX_ANGLE = 10f; + + public static final int AI_PET_TIME_BETWEEN_JOB_TICKS_MS = 250; + + // Pet Settings + public static final float PET_TELEPORT_DISTANCE = 600; // distance a pet + // teleports to + // player + public static final float PET_FOLLOW_DISTANCE = 10; // distance a pet starts + // moving towards owner + public static final float PET_REST_DISTANCE = 4; // distance a pet stops + // moving towards owner + + /* + * Combat + */ + public static final int COMBAT_SEND_DODGE = 20; + public static final int COMBAT_SEND_BLOCK = 21; + public static final int COMBAT_SEND_PARRY = 22; + public static final short LEVELCAP = 75; + public static final int LEVEL_CON_WHITE = 7; + public static final int RESPAWN_TIMER = 90 * 1000; + public static final int DESPAWN_TIMER = 12 * 1000; + public static final int DESPAWN_TIMER_WITH_LOOT = 90 * 1000; + public static final int DESPAWN_TIMER_ONCE_LOOTED = 5 * 1000; + public static final int MAX_COMBAT_HITBOX_RADIUS = 80; + public static final int PROC_CHANCE = 5; // %chance to proc + public static float PRODUCTION_TIME_MULTIPLIER = .5f; + + /* + * Mob loot -- gold calculations + */ + public static final String STRONGBOX_DELAY_STRING = "StrongboxSpam"; + public static final String STRONGBOX_DELAY_OUTPUT = "You must wait 1 minute to do this again."; + public static final int BIG_SPAM_DELAY = 10000; + public static String BIG_SPAM_DELAY_STRING = "BIGSPAM"; + + public static final double GOLD_DROP_PERCENTAGE_CHANCE = 61d; + public static final double GOLD_DROP_MULTIPLIER_GLOBAL = 1.0d; // tweak all + // rates at + // once + public static final double GOLD_DROP_MULTIPLIER_HOTZONE = 2.0d; + public static final double GOLD_DROP_MULTIPLIER_MAELSTROM = 1.1d; + public static final double GOLD_DROP_MULTIPLIER_OBLIVION = 1.1d; + public static final double[] GOLD_DROP_MINIMUM_PER_MOB_LEVEL = { 450, 450, + 450, 450, 450, // 0 - 4 + 450, 450, 450, 450, 450, // 5 - 9 + 450, 1000, 1000, 1000, 1000, // 10 - 14 + 1000, 1000, 1000, 1000, 1000, // 15 - 19 + 1000, 1000, 1000, 1000, 1000, // 20 - 24 + 2000, 2000, 2000, 2000, 2000, // 25 - 29 + 2000, 2000, 2000, 2000, 2000, // 30 - 34 + 2000, 2000, 2000, 2000, 2000, // 35 - 39 + 4000, 4000, 4000, 4000, 4000, // 40 - 44 + 4000, 4000, 4000, 4000, 4000, // 45 - 49 + 5000 // 50+ + }; + public static final double[] GOLD_DROP_MAXIMUM_PER_MOB_LEVEL = { 1000, + 1000, 1000, 1000, 1000, // 0 - 4 + 1000, 1000, 1000, 1000, 1000, // 5 - 9 + 1000, 2500, 2500, 2500, 2500, // 10 - 14 + 2500, 2500, 2500, 2500, 2500, // 15 - 19 + 2500, 2500, 2500, 2500, 2500, // 20 - 24 + 4000, 4000, 4000, 4000, 4000, // 25 - 29 + 4000, 4000, 4000, 4000, 4000, // 30 - 34 + 4000, 4000, 4000, 4000, 4000, // 35 - 39 + 9000, 9000, 9000, 9000, 9000, // 40 - 44 + 9000, 9000, 9000, 9000, 9000, // 45 - 49 + 12000 // 50+ + }; + + // DO NOT FINAL THESE FIELD! + public static Enum.AccountStatus accessLevel; // Min account level to login to server + public static boolean blockLogin = false; + public static boolean ENABLE_VAULT_FILL = false; + public static boolean ENABLE_MOB_LOOT = true; + public static boolean ENABLE_AUDIT_JOB_WORKERS = true; + public static boolean ENABLE_COMBAT_TARGET_HITBOX = true; + + /* + * Track Sensitivity + */ + // Rate that track arrow refreshes. When inside TRACK_ARROW_FAST_RANGE, use + // TRACK_ARROW_SENSITIVITY_FAST speed, otherwise use TRACK_ARROW_SENSITIVITY + // speed. + public static final float TRACK_ARROW_FAST_RANGE = 50f; // Range to go from + // Fast arrow to + // slow + public static final int TRACK_ARROW_SENSITIVITY = 1000; // Refresh track + // arrows every X ms + public static final int TRACK_ARROW_SENSITIVITY_FAST = 250; // Refresh track + // arrows every + // X ms + + /* + * Population breakpoints + */ + public static final int LOW_POPULATION = 100; + public static final int NORMAL_POPULATION = 500; + public static final int HIGH_POPULATION = 1000; + public static final int VERY_OVERPOPULATED_POPULATION = 3000; + public static final int FULL_POPULATION = 5000; + + // Refresh sensetivities + public static final int TRACK_WINDOW_THRESHOLD = 1000; // max refresh once + // every 1 seconds. + public static final int WHO_WINDOW_THRESHOLD = 3000; // max refresh once + // every 3 seconds. + public static final int VENDOR_WINDOW_THRESHOLD = 2000; // max refresh once + // every 2 seconds. + public static final int PURCHASE_THRESHOLD = 500; // max refresh once every + // 0.5 seconds. + public static final int SELL_THRESHOLD = 100; // max refresh once every 0.1 + // seconds. + public static final int MAX_PLAYER_LOAD_SIZE = 1000; + + // Mine related + public static final int MINE_EARLY_WINDOW = 16; // 3pm + public static final int MINE_LATE_WINDOW = 0; // Midnight + + // Race + public static final float RADIUS_ARACOIX = 0.68999999761581f; + public static final float RADIUS_MINOTAUR = 0.69960004091263f; + public static final float RADIUS_DWARF = 0; + public static final float RADIUS_HUMAN = 0; + public static final float RADIUS_NEPHILIM = 0; + public static final float RADIUS_AELFBORN = 0; + public static final float RADIUS_ELF = 0; + public static final float RADIUS_VAMPIRE = 0; + public static final float RADIUS_IREKEI = 0; + public static final float RADIUS_HALF_GIANT = 0; + public static final float RADIUS_SHADE = 0; + public static final float RADIUS_CENTAUR = 0.68999999761581f; + + public static String JUNIOR = "Junior"; + public static String VETERAN = "Veteran"; + public static String ELITE = "Elite"; + + public static int worldMapID = Integer.parseInt(ConfigManager.MB_WORLD_MAPID.getValue()); + public static int worldUUID = Integer.parseInt(ConfigManager.MB_WORLD_UUID.getValue()); + public static Enum.AccountStatus worldAccessLevel = Enum.AccountStatus.valueOf(ConfigManager.MB_WORLD_ACCESS_LVL.getValue()); +} diff --git a/src/engine/server/login/LoginServer.java b/src/engine/server/login/LoginServer.java new file mode 100644 index 00000000..07da1326 --- /dev/null +++ b/src/engine/server/login/LoginServer.java @@ -0,0 +1,524 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.server.login; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import engine.Enum; +import engine.gameManager.*; +import engine.job.JobScheduler; +import engine.jobs.CSessionCleanupJob; +import engine.net.Network; +import engine.net.client.ClientConnection; +import engine.net.client.ClientConnectionManager; +import engine.net.client.Protocol; +import engine.net.client.msg.login.ServerStatusMsg; +import engine.net.client.msg.login.VersionInfoMsg; +import engine.objects.*; +import engine.server.MBServerStatics; +import engine.util.ByteUtils; +import engine.util.ThreadUtils; +import org.pmw.tinylog.Configurator; +import org.pmw.tinylog.Level; +import org.pmw.tinylog.Logger; +import org.pmw.tinylog.labelers.TimestampLabeler; +import org.pmw.tinylog.policies.StartupPolicy; +import org.pmw.tinylog.writers.RollingFileWriter; + +import java.io.*; +import java.net.InetAddress; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDateTime; +import java.util.Iterator; + +import static java.lang.System.exit; + +public class LoginServer { + + + // Instance variables + + private VersionInfoMsg versionInfoMessage; + public static HikariDataSource connectionPool = null; + public static int population = 0; + public static boolean worldServerRunning = false; + public static boolean loginServerRunning = false; + + public static ServerStatusMsg serverStatusMsg = new ServerStatusMsg(0, (byte) 1); + + // This is the entrypoint for the MagicBane Login Server when + // it is executed by the command line scripts. The fun begins here! + + public static void main(String[] args) { + + LoginServer loginServer; + + // Initialize TinyLog logger with our own format + + Configurator.defaultConfig() + .addWriter(new RollingFileWriter("logs/login/login.txt", 30, new TimestampLabeler(), new StartupPolicy())) + .level(Level.DEBUG) + .formatPattern("{level} {date:yyyy-MM-dd HH:mm:ss.SSS} [{thread}] {class}.{method}({line}) : {message}") + .activate(); + + try { + + // Configure the the Login Server + + loginServer = new LoginServer(); + ConfigManager.loginServer = loginServer; + ConfigManager.handler = new LoginServerMsgHandler(loginServer); + + ConfigManager.serverType = Enum.ServerType.LOGINSERVER; + + if (ConfigManager.init() == false) { + Logger.error("ABORT! Missing config entry!"); + return; + } + + // Start the Login Server + + loginServer.init(); + loginServer.exec(); + + exit(0); + + } catch (Exception e) { + Logger.error(e); + e.printStackTrace(); + exit(1); + } + } + + // Mainline execution loop for the login server. + + private void exec() { + + + LocalDateTime nextCacheTime = LocalDateTime.now(); + LocalDateTime nextServerTime = LocalDateTime.now(); + LocalDateTime nextDatabaseTime = LocalDateTime.now(); + + loginServerRunning = true; + + while (true) { + + // Invalidate cache for players driven by forum + // and stored procedure forum_link_pass() + + try { + + // Run cache routine right away if requested. + + File cacheFile = new File("cacheInvalid"); + + + if (cacheFile.exists() == true) { + nextCacheTime = LocalDateTime.now(); + Files.deleteIfExists(Paths.get("cacheInvalid")); + } + + if (LocalDateTime.now().isAfter(nextCacheTime)) { + invalidateCacheList(); + nextCacheTime = LocalDateTime.now().plusSeconds(30); + } + + if (LocalDateTime.now().isAfter(nextServerTime)) { + checkServerHealth(); + nextServerTime = LocalDateTime.now().plusSeconds(1); + } + + if (LocalDateTime.now().isAfter(nextDatabaseTime)) { + String pop = SimulationManager.getPopulationString(); + Logger.info("Keepalive: " + pop); + nextDatabaseTime = LocalDateTime.now().plusMinutes(30); + } + + ThreadUtils.sleep(100); + } catch (Exception e) { + Logger.error(e); + e.printStackTrace(); + } + } + } + + // Constructor + + public LoginServer() { + + } + + private boolean init() { + + // Initialize Application Protocol + + Protocol.initProtocolLookup(); + + // Configure the VersionInfoMsgs: + + this.versionInfoMessage = new VersionInfoMsg(MBServerStatics.PCMajorVer, + MBServerStatics.PCMinorVer); + + Logger.info("Initializing Database Pool"); + initDatabasePool(); + + Logger.info("Initializing Database layer"); + initDatabaseLayer(); + + Logger.info("Initializing Network"); + Network.init(); + + Logger.info("Initializing Client Connection Manager"); + initClientConnectionManager(); + + // instantiate AccountManager + Logger.info("Initializing SessionManager."); + + // Sets cross server behavior + SessionManager.setCrossServerBehavior(0); + + // activate powers manager + Logger.info("Initializing PowersManager."); + PowersManager.initPowersManager(false); + + RuneBaseAttribute.LoadAllAttributes(); + RuneBase.LoadAllRuneBases(); + BaseClass.LoadAllBaseClasses(); + Race.loadAllRaces(); + RuneBaseEffect.LoadRuneBaseEffects(); + + Logger.info("Initializing Blueprint data."); + Blueprint.loadAllBlueprints(); + + Logger.info("Loading Kits"); + DbManager.KitQueries.GET_ALL_KITS(); + + Logger.info("Initializing ItemBase data."); + ItemBase.loadAllItemBases(); + + Logger.info("Initializing Race data"); + Enum.RaceType.initRaceTypeTables(); + Race.loadAllRaces(); + + Logger.info("Initializing Errant Guild"); + Guild.CreateErrantGuild(); + + Logger.info("Loading All Guilds"); + DbManager.GuildQueries.GET_ALL_GUILDS(); + + + Logger.info("***Boot Successful***"); + return true; + } + + private boolean initDatabaseLayer() { + + // Try starting a GOM <-> DB connection. + try { + + Logger.info("Configuring GameObjectManager to use Database: '" + + ConfigManager.MB_DATABASE_NAME.getValue() + "' on " + + ConfigManager.MB_DATABASE_ADDRESS.getValue() + ':' + + ConfigManager.MB_DATABASE_PORT.getValue()); + + DbManager.configureDatabaseLayer(); + + } catch (Exception e) { + Logger.error(e.getMessage()); + return false; + } + + PreparedStatementShared.submitPreparedStatementsCleaningJob(); + + if (MBServerStatics.DB_DEBUGGING_ON_BY_DEFAULT) { + PreparedStatementShared.enableDebugging(); + } + + return true; + } + + public void removeClient(ClientConnection conn) { + if (conn == null) { + Logger.info( + "ClientConnection null in removeClient."); + return; + } + String key = ByteUtils.byteArrayToSafeStringHex(conn + .getSecretKeyBytes()); + + CSessionCleanupJob cscj = new CSessionCleanupJob(key); + + JobScheduler.getInstance().scheduleJob(cscj, + MBServerStatics.SESSION_CLEANUP_TIMER_MS); + } + + private void initClientConnectionManager() { + + try { + + String name = ConfigManager.MB_WORLD_NAME.getValue(); + + // Find publicIP address for use in worldserver response + // message. Sending the client to an unroutable address + // doesn't work so well. + + URL whatismyip = new URL("http://checkip.amazonaws.com"); + BufferedReader in = new BufferedReader(new InputStreamReader( + whatismyip.openStream())); + ConfigManager.MB_PUBLIC_ADDR.setValue(in.readLine()); + Logger.info("Public address: " + ConfigManager.MB_PUBLIC_ADDR.getValue()); + + Logger.info("Magicbane network config: " + ConfigManager.MB_BIND_ADDR.getValue() + ":" + ConfigManager.MB_LOGIN_PORT.getValue()); + + InetAddress addy = InetAddress.getByName(ConfigManager.MB_BIND_ADDR.getValue()); + int port = Integer.parseInt(ConfigManager.MB_LOGIN_PORT.getValue()); + + ClientConnectionManager connectionManager = new ClientConnectionManager(name + ".ClientConnMan", addy, + port); + connectionManager.startup(); + + } catch (IOException e) { + Logger.error(e.toString()); + } + } + /* + * message handlers (relay) + */ + + // ============================== + // Support Functions + // ============================== + + public VersionInfoMsg getDefaultVersionInfo() { + return versionInfoMessage; + } + + //this updates a server being up or down without resending the entire char select screen. + public void updateServersForAll(boolean isRunning) { + + try { + + Iterator i = SessionManager.getAllActiveClientConnections().iterator(); + + while (i.hasNext()) { + + ClientConnection clientConnection = i.next(); + + if (clientConnection == null) + continue; + + Account ac = clientConnection.getAccount(); + + if (ac == null) + continue; + + boolean isUp = isRunning; + + + if (MBServerStatics.worldAccessLevel.ordinal() > ac.status.ordinal()) + isUp = false; + + LoginServer.serverStatusMsg.setServerID(MBServerStatics.worldMapID); + LoginServer.serverStatusMsg.setIsUp(isUp ? (byte) 1 : (byte) 0); + clientConnection.sendMsg(LoginServer.serverStatusMsg); + } + } catch (Exception e) { + Logger.error(e); + e.printStackTrace(); + } + } + + public void checkServerHealth() { + + // Check if worldserver is running + + if (!isPortInUse(Integer.parseInt(ConfigManager.MB_WORLD_PORT.getValue()))) { + worldServerRunning = false; + population = 0; + updateServersForAll(worldServerRunning); + return; + } + + // Worldserver is running and writes a polling file. + // Read the current population count from the server and + // update player displays accordingly. + + worldServerRunning = true; + population = readPopulationFile(); + updateServersForAll(worldServerRunning); + + } + + private void initDatabasePool() { + + HikariConfig config = new HikariConfig(); + + config.setMaximumPoolSize(33); // (16 cores 1 spindle) + + config.setJdbcUrl("jdbc:mysql://" + ConfigManager.MB_DATABASE_ADDRESS.getValue() + + ":" + ConfigManager.MB_DATABASE_PORT.getValue() + "/" + + ConfigManager.MB_DATABASE_NAME.getValue()); + config.setUsername(ConfigManager.MB_DATABASE_USER.getValue()); + config.setPassword(ConfigManager.MB_DATABASE_PASS.getValue()); + config.addDataSourceProperty("characterEncoding", "utf8"); + config.addDataSourceProperty("cachePrepStmts", "true"); + config.addDataSourceProperty("prepStmtCacheSize", "250"); + config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); + + connectionPool = new HikariDataSource(config); // setup the connection pool + + Logger.info("local database connection configured"); + } + + public void invalidateCacheList() { + + int objectUUID; + String objectType; + + try (Connection connection = connectionPool.getConnection(); + PreparedStatement statement = connection.prepareStatement("SELECT * FROM `login_cachelist`"); + ResultSet rs = statement.executeQuery()) { + + while (rs.next()) { + + objectUUID = rs.getInt("UID"); + objectType = rs.getString("type"); + + Logger.info("INVALIDATED : " + objectType + " UUID: " + objectUUID); + + switch (objectType) { + + case "account": + DbManager.removeFromCache(Enum.GameObjectType.Account, objectUUID); + break; + case "character": + DbManager.removeFromCache(Enum.GameObjectType.PlayerCharacter, objectUUID); + PlayerCharacter player = (PlayerCharacter) DbManager.getObject(Enum.GameObjectType.PlayerCharacter, objectUUID); + PlayerCharacter.initializePlayer(player); + player.getAccount().characterMap.replace(player.getObjectUUID(), player); + Logger.info("Player active state is : " + player.isActive()); + break; + } + + } + + } catch (SQLException e) { + Logger.info(e.toString()); + } + + // clear the db table + + try (Connection connection = connectionPool.getConnection(); + PreparedStatement statement = connection.prepareStatement("DELETE FROM `login_cachelist`")) { + + statement.execute(); + + } catch (SQLException e) { + Logger.info(e.toString()); + } + + + } + + public static boolean getActiveBaneQuery(PlayerCharacter playerCharacter) { + + boolean outStatus = false; + + // char has never logged on so cannot have dropped a bane + + if (playerCharacter.getHash() == null) + return outStatus; + + // query data warehouse for unresolved bane with this character + + try (Connection connection = connectionPool.getConnection(); + PreparedStatement statement = buildQueryActiveBaneStatement(connection, playerCharacter); + ResultSet rs = statement.executeQuery()) { + + while (rs.next()) { + + outStatus = true; + } + + } catch (SQLException e) { + Logger.error(e.toString()); + } + + return outStatus; + } + + private static PreparedStatement buildQueryActiveBaneStatement(Connection connection, PlayerCharacter playerCharacter) throws SQLException { + PreparedStatement outStatement; + String queryString = "SELECT `city_id` FROM `warehouse_banehistory` WHERE `char_id` = ? AND `RESOLUTION` = 'PENDING'"; + outStatement = connection.prepareStatement(queryString); + outStatement.setString(1, playerCharacter.getHash()); + return outStatement; + + } + + public static boolean isPortInUse(int port) { + + ProcessBuilder builder = new ProcessBuilder("/bin/bash", "-c", "lsof -i tcp:" + port + " | tail -n +2 | awk '{print $2}'"); + builder.redirectErrorStream(true); + Process process = null; + String line = null; + boolean portInUse = false; + + try { + process = builder.start(); + + InputStream is = process.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + + while ((line = reader.readLine()) != null) { + portInUse = true; + } + + } catch (IOException e) { + e.printStackTrace(); + } + + return portInUse; + } + + private int readPopulationFile() { + + ProcessBuilder builder = new ProcessBuilder("/bin/bash", "-c", "cat " + MBServerStatics.DEFAULT_DATA_DIR + ConfigManager.MB_WORLD_NAME.getValue().replaceAll("'","") + ".pop"); + builder.redirectErrorStream(true); + Process process = null; + String line = null; + int population = 0; + + try { + + process = builder.start(); + + InputStream is = process.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + + while ((line = reader.readLine()) != null) { + population = Integer.parseInt(line); + } + + } catch (IOException e) { + e.printStackTrace(); + return 0; + } + + return population; + } + +} diff --git a/src/engine/server/login/LoginServerMsgHandler.java b/src/engine/server/login/LoginServerMsgHandler.java new file mode 100644 index 00000000..640baf8f --- /dev/null +++ b/src/engine/server/login/LoginServerMsgHandler.java @@ -0,0 +1,444 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.server.login; + +import engine.Enum; +import engine.Enum.DispatchChannel; +import engine.Enum.GameObjectType; +import engine.gameManager.DbManager; +import engine.gameManager.SessionManager; +import engine.job.JobScheduler; +import engine.jobs.DisconnectJob; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.NetMsgHandler; +import engine.net.client.ClientConnection; +import engine.net.client.Protocol; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ServerInfoMsg; +import engine.net.client.msg.login.*; +import engine.objects.Account; +import engine.objects.GuildStatusController; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; +import engine.session.CSSession; +import engine.session.Session; +import engine.util.ByteUtils; +import engine.util.StringUtils; +import org.pmw.tinylog.Logger; + +public class LoginServerMsgHandler implements NetMsgHandler { + + private final LoginServer server; + + LoginServerMsgHandler(LoginServer server) { + super(); + this.server = server; + } + + /* + * ========================================================================= + * Client Messages + * ========================================================================= + */ + @Override + public boolean handleClientMsg(ClientNetMsg clientNetMsg) { + + if (clientNetMsg == null) { + Logger.error("Recieved null msg. Returning."); + return false; + } + + ClientConnection origin = (ClientConnection) clientNetMsg.getOrigin(); + Protocol protocolMsg = clientNetMsg.getProtocolMsg(); + + try { + + switch (protocolMsg) { + + case VERSIONINFO: + this.VerifyCorrectClientVersion((VersionInfoMsg) clientNetMsg); + break; + + case LOGIN: + if (LoginServer.loginServerRunning == true) + this.Login((ClientLoginInfoMsg) clientNetMsg, origin); + else + this.KickToLogin(MBServerStatics.LOGINERROR_LOGINSERVER_BUSY, "", origin); + break; + + case KEEPALIVESERVERCLIENT: + // echo the keep alive back + origin.sendMsg(clientNetMsg); + break; + + case SELECTSERVER: + this.SendServerInfo(origin); + break; + + case CREATECHAR: + this.CommitNewCharacter((CommitNewCharacterMsg) clientNetMsg, origin); + break; + + case REMOVECHAR: + this.DeleteCharacter((DeleteCharacterMsg) clientNetMsg, origin); + break; + + case SELECTCHAR: + this.RequestGameServer((GameServerIPRequestMsg) clientNetMsg, origin); + break; + + case SETSELECTEDOBECT: + // Why is this being sent to login server? + break; + + default: + String ocHex = StringUtils.toHexString(protocolMsg.opcode); + Logger.error("Cannot not handle Opcode: " + ocHex); + return false; + } + + } catch (Exception e) { + Logger.error("protocolMsg:" + protocolMsg + e.toString()); + return false; + } + + return true; + } + + private void VerifyCorrectClientVersion(VersionInfoMsg vim) { + ClientConnection cc; + String cMajorVer; + String cMinorVer; + VersionInfoMsg outVim; + + cc = (ClientConnection) vim.getOrigin(); + cMajorVer = vim.getMajorVersion(); + cMinorVer = vim.getMinorVersion(); + + if (!cMajorVer.equals(this.server.getDefaultVersionInfo().getMajorVersion())) { + this.KickToLogin(MBServerStatics.LOGINERROR_INCORRECT_CLIENT_VERSION, "Major Version Failure: " + cMajorVer, cc); + return; + } + + /* if (!cMinorVer.equals(this.server.getDefaultVersionInfo().getMinorVersion())) { + this.KickToLogin(MBServerStatics.LOGINERROR_INCORRECT_CLIENT_VERSION, "Minor Version Failure: " + cMinorVer, cc); + return; + } */ + + if (cMinorVer == null) { + this.KickToLogin(MBServerStatics.LOGINERROR_INCORRECT_CLIENT_VERSION, "Minor Version Failure: ", cc); + return; + } + + if (cMinorVer.length() < 8 || cMinorVer.length() > 16) { + this.KickToLogin(MBServerStatics.LOGINERROR_INCORRECT_CLIENT_VERSION, "Minor Version Failure: ", cc); + return; + } + + // set MachineID for this connection + + cc.machineID = cMinorVer; + + // send fake right back to the client + outVim = new VersionInfoMsg(vim.getMajorVersion(), this.server.getDefaultVersionInfo().getMinorVersion() ); + cc.sendMsg(outVim); + } + + // our data access should be in a separate object + private void Login(ClientLoginInfoMsg clientLoginInfoMessage, ClientConnection clientConnection) { + + // Add zero length strings to eliminate the need for null checking. + String uname = clientLoginInfoMessage.getUname(); + String pass = clientLoginInfoMessage.getPword(); + + // Check to see if there is actually any data in uname.pass + if (uname.length() == 0) { + this.KickToLogin(MBServerStatics.LOGINERROR_UNABLE_TO_LOGIN, "The username provided was zero length.", clientConnection); + return; + } + + if (pass.length() == 0) { + this.KickToLogin(MBServerStatics.LOGINERROR_UNABLE_TO_LOGIN, "The password provided was zero length.", clientConnection); + return; + } + + Account account; + + account = DbManager.AccountQueries.GET_ACCOUNT(uname); + + if (account == null) { + + this.KickToLogin(MBServerStatics.LOGINERROR_INVALID_USERNAME_PASSWORD, "Could not find account (" + uname + ')', clientConnection); + Logger.info("Could not find account (" + uname + ')'); + return; + + } + + if (account.getLastLoginFailure() + MBServerStatics.RESET_LOGIN_ATTEMPTS_AFTER < System.currentTimeMillis()) + account.resetLoginAttempts(); + + // TODO: Log the login attempts IP, name, password and timestamp + // Check number invalid login attempts. If 5 or greater, kick to login. + if (account.getLoginAttempts() >= MBServerStatics.MAX_LOGIN_ATTEMPTS) { + + this.KickToLogin(MBServerStatics.LOGINERROR_UNABLE_TO_LOGIN, "Too many login in attempts for '" + uname + '\'', clientConnection); + Logger.info("Too many login in attempts for '" + uname + '\''); + return; + } + + if (account.lastPasswordCheck < System.currentTimeMillis()) { + account.lastPasswordCheck = System.currentTimeMillis() + MBServerStatics.ONE_MINUTE; + } + + // Attempt to validate login + try { + if (!account.passIsValid(pass, clientConnection.getClientIpAddress(), clientConnection.machineID)) { + + account.incrementLoginAttempts(); + this.KickToLogin(MBServerStatics.LOGINERROR_INVALID_USERNAME_PASSWORD, "", clientConnection); + Logger.info("Incorrect password(" + uname + ')'); + return; + } + } catch (IllegalArgumentException e1) { + this.KickToLogin(MBServerStatics.LOGINERROR_UNABLE_TO_LOGIN, "", clientConnection); + Logger.info("Failed forum account validation(" + uname + ')'); + } + + // Account deactivated + + if (account.status.equals(Enum.AccountStatus.BANNED)) { + this.KickToLogin(MBServerStatics.LOGINERROR_NO_MORE_PLAYTIME_ON_ACCOUNT, "", clientConnection); + return; + } + + // Check to see if we have a Session mapped with this Account: + Session session = SessionManager.getSession(account); + + // If there is, then the account is in use and must be handled: + // kick the 'other connection' + if (session != null) + this.KickToLogin(MBServerStatics.LOGINERROR_UNABLE_TO_LOGIN, "Your account has been accessed from a different IP & Port.", session.getConn()); // Logout the character + + + // TODO implement character logout + // Get a new session + session = SessionManager.getNewSession(account, clientConnection); + + // Set Invalid Login Attempts to 0 + account.resetLoginAttempts(); + + // Send Login Response + ClientLoginInfoMsg loginResponse = new ClientLoginInfoMsg(clientLoginInfoMessage); + loginResponse.setUnknown06(8323072); + loginResponse.setUnknown07(3276800); + loginResponse.setUnknown08(196608); + loginResponse.setUnknown09((short) 15); + + clientConnection.sendMsg(loginResponse); + + // send character select screen + try { + this.sendCharacterSelectScreen(session); + } catch (Exception e) { + Logger.error("Unable to Send Character Select Screen to client"); + this.KickToLogin(MBServerStatics.LOGINERROR_UNABLE_TO_LOGIN, "Unable to send Character Select Screen to client.", clientConnection); + return; + } + + // Logging + String addyPort = clientConnection.getRemoteAddressAndPortAsString(); + int id = account.getObjectUUID(); + + Logger.info(uname + '(' + id + ") has successfully logged in from " + addyPort); + + } + + private void KickToLogin(int errCode, String message, ClientConnection origin) { + LoginErrorMsg msg = new LoginErrorMsg(errCode, message); + + PlayerCharacter player = origin.getPlayerCharacter(); + + if (player == null) { + origin.sendMsg(msg); + } else { + Dispatch dispatch = Dispatch.borrow(player, msg); + DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY); + } + + + Logger.info("Kicking to Login. Message: '" + message + '\''); + + DisconnectJob dj = new DisconnectJob(origin); + JobScheduler.getInstance().scheduleJob(dj, 250); + } + + protected void sendCharacterSelectScreen(Session s) { + sendCharacterSelectScreen(s, false); + } + + private void sendCharacterSelectScreen(Session s, boolean fromCommit) { + + if (s.getAccount() != null) { + CharSelectScreenMsg cssm = new CharSelectScreenMsg(s, fromCommit); + s.getConn().sendMsg(cssm); + } else { + Logger.error("No Account Found: Unable to Send Character Select Screen"); + this.KickToLogin(MBServerStatics.LOGINERROR_UNABLE_TO_LOGIN, "Unable to send Character Select Screen to client.", s.getConn()); + } + } + + private void SendServerInfo(ClientConnection conn) { + ServerInfoMsg sim = new ServerInfoMsg(); + + if (!conn.sendMsg(sim)) { + Logger.error("Failed to send message"); + + this.KickToLogin(MBServerStatics.LOGINERROR_UNABLE_TO_LOGIN, "Unable to send ServerInfoMsg to client.", conn); + } + } + + private void CommitNewCharacter(CommitNewCharacterMsg commitNewCharacterMessage, ClientConnection clientConnection) { + + Session session = SessionManager.getSession(clientConnection); + + if (session.getAccount() == null) + return; + + try { + // Check to see if there is an available slot. + if (session.getAccount().characterMap.size() >= MBServerStatics.MAX_NUM_OF_CHARACTERS) { + this.sendCharacterSelectScreen(session); + return; + } + + PlayerCharacter pc = PlayerCharacter.generatePCFromCommitNewCharacterMsg(session.getAccount(), commitNewCharacterMessage, clientConnection); + + if (pc == null) { + Logger.info("Player returned null while creating character."); + this.sendCharacterSelectScreen(session, true); + return; + } + + PlayerCharacter.initializePlayer(pc); + session.getAccount().characterMap.putIfAbsent(pc.getObjectUUID(), pc); + // Send back to Character Select Screen + this.sendCharacterSelectScreen(session, true); + + } catch (Exception e) { + Logger.error(e); + this.sendCharacterSelectScreen(session, true); + } + } + + public static void sendInvalidNameMsg(String firstName, String lastName, int errorCode, ClientConnection clientConnection) { + + InvalidNameMsg invalidNameMessage; + + if (firstName.length() > 256 || lastName.length() > 256) + invalidNameMessage = new InvalidNameMsg(firstName, lastName, errorCode); + else + invalidNameMessage = new InvalidNameMsg(firstName, lastName, errorCode); + + clientConnection.sendMsg(invalidNameMessage); + } + + private void DeleteCharacter(DeleteCharacterMsg msg, ClientConnection origin) { + + try { + PlayerCharacter player; + Session session; + + session = SessionManager.getSession(origin); + player = (PlayerCharacter) DbManager.getObject(GameObjectType.PlayerCharacter, msg.getCharacterUUID()); + + if (player == null) { + Logger.error("Delete Error: PlayerID=" + msg.getCharacterUUID() + " not found."); + this.sendCharacterSelectScreen(session); + return; + } + + if (session.getAccount() == null) { + Logger.error("Delete Error: Account not found."); + this.sendCharacterSelectScreen(session); + return; + } + + if (player.getAccount() != origin.getAccount()) { + Logger.error("Delete Error: Character " + player.getName() + " does not belong to account " + origin.getAccount().getUname()); + this.sendCharacterSelectScreen(session); + return; + } + + //Can't delete as Guild Leader + //TODO either find an error or just gdisband. + + if (GuildStatusController.isGuildLeader(player.getGuildStatus())) { + this.KickToLogin(MBServerStatics.LOGINERROR_UNABLE_TO_LOGIN, "Cannot delete a guild leader.", origin); + return; + } + + // check for active banes + + if (LoginServer.getActiveBaneQuery(player)) { + Logger.info("Character " + player.getName() + " has unresolved bane"); + this.KickToLogin(MBServerStatics.LOGINERROR_UNABLE_TO_LOGIN, "Player has unresolved bane.", origin); + return; + } + + player.getAccount().characterMap.remove(player.getObjectUUID()); + player.deactivateCharacter(); + + // TODO Delete Equipment + // Resend Character Select Screen. + this.sendCharacterSelectScreen(session); + + } catch (Exception e) { + Logger.error(e); + } + } + + private void RequestGameServer(GameServerIPRequestMsg gameServerIPRequestMessage, ClientConnection conn) { + + Session session; + PlayerCharacter player; + + session = SessionManager.getSession(conn); + player = (PlayerCharacter) DbManager.getObject(GameObjectType.PlayerCharacter, gameServerIPRequestMessage.getCharacterUUID()); + + if (player == null) { + Logger.info("Unable to find character ID " + gameServerIPRequestMessage.getCharacterUUID()); + this.KickToLogin(MBServerStatics.LOGINERROR_UNABLE_TO_LOGIN, "PlayerCharacter lookup failed in .RequestGameServer().", conn); + return; + } + + try { + if (!CSSession.updateCrossServerSession(ByteUtils.byteArrayToSafeStringHex(conn.getSecretKeyBytes()), gameServerIPRequestMessage.getCharacterUUID())) { + Logger.info("Failed to update Cross server session, Kicking to Login for Character " + player.getObjectUUID()); + this.KickToLogin(MBServerStatics.LOGINERROR_UNABLE_TO_LOGIN, "Failed to update Session Information", conn); + return; + } + } catch (Exception e) { + Logger.info("Failed to update Cross server session, Kicking to Login for Character " + player.getObjectUUID()); + Logger.error(e); + } + + // Set the last character used. + Account account = session.getAccount(); + account.setLastCharIDUsed(gameServerIPRequestMessage.getCharacterUUID()); + + GameServerIPResponseMsg gsiprm = new GameServerIPResponseMsg(); + + if (!conn.sendMsg(gsiprm)) { + Logger.error("Failed to send message"); + this.KickToLogin(MBServerStatics.LOGINERROR_UNABLE_TO_LOGIN, "Unable to send GameServerIPResponseMsg to client.", conn); + } + } +} diff --git a/src/engine/server/world/WorldServer.java b/src/engine/server/world/WorldServer.java new file mode 100644 index 00000000..371b44f5 --- /dev/null +++ b/src/engine/server/world/WorldServer.java @@ -0,0 +1,861 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + +package engine.server.world; + +import engine.Enum; +import engine.Enum.BuildingGroup; +import engine.Enum.DispatchChannel; +import engine.Enum.MinionType; +import engine.Enum.SupportMsgType; +import engine.InterestManagement.HeightMap; +import engine.InterestManagement.RealmMap; +import engine.InterestManagement.WorldGrid; +import engine.ai.MobileFSMManager; +import engine.db.archive.DataWarehouse; +import engine.exception.MsgSendException; +import engine.gameManager.*; +import engine.job.JobContainer; +import engine.job.JobScheduler; +import engine.jobs.LogoutCharacterJob; +import engine.jobs.MineActiveJob; +import engine.loot.LootManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.ItemProductionManager; +import engine.net.Network; +import engine.net.client.ClientConnection; +import engine.net.client.ClientConnectionManager; +import engine.net.client.ClientMessagePump; +import engine.net.client.Protocol; +import engine.net.client.msg.RefinerScreenMsg; +import engine.net.client.msg.TrainerInfoMsg; +import engine.net.client.msg.UpdateStateMsg; +import engine.net.client.msg.chat.ChatSystemMsg; +import engine.objects.*; +import engine.server.MBServerStatics; +import engine.util.ThreadUtils; +import engine.workthreads.DisconnectTrashTask; +import engine.workthreads.HourlyJobThread; +import engine.workthreads.PurgeOprhans; +import engine.workthreads.WarehousePushThread; +import org.pmw.tinylog.Configurator; +import org.pmw.tinylog.Level; +import org.pmw.tinylog.Logger; +import org.pmw.tinylog.labelers.TimestampLabeler; +import org.pmw.tinylog.policies.StartupPolicy; +import org.pmw.tinylog.writers.RollingFileWriter; + +import java.io.*; +import java.net.InetAddress; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Timer; + +import static engine.gameManager.SimulationManager.SERVERHEARTBEAT; +import static java.lang.System.exit; + +public class WorldServer { + + private static LocalDateTime bootTime = LocalDateTime.now(); + private static long lastHZChange = System.currentTimeMillis(); + public boolean isRunning = false; + + // Member variable declaration + + public static HashMap>> ZoneFidelityMobRunes = new HashMap<>(); + + public WorldServer() { + super(); + } + + public static void main(String[] args) { + + WorldServer worldServer; + + // Configure TinyLogger + Configurator.defaultConfig() + .addWriter(new RollingFileWriter("logs/world/world.txt", 30, new TimestampLabeler(), new StartupPolicy())) + .level(Level.DEBUG) + .formatPattern("{level} {date:yyyy-MM-dd HH:mm:ss.SSS} [{thread}] {class}.{method}({line}) : {message}") + .writingThread("main", 2) + .activate(); + + if (ConfigManager.init() == false) { + Logger.error("ABORT! Missing config entry!"); + return; + } + + try { + + worldServer = new WorldServer(); + + ConfigManager.serverType = Enum.ServerType.WORLDSERVER; + ConfigManager.worldServer = worldServer; + ConfigManager.handler = new ClientMessagePump(worldServer); + + worldServer.init(); + + int retVal = worldServer.exec(); + + if (retVal != 0) + Logger.error( + ".exec() returned value: '" + retVal); + exit(retVal); + + } catch (Exception e) { + Logger.error(e.getMessage()); + exit(1); + } + } + + public static long getLastHZChange() { + return lastHZChange; + } + + public static void setLastHZChange(long lastChange) { + lastHZChange = lastChange; + } + + public static void trainerInfo(TrainerInfoMsg msg, ClientConnection origin) { + + NPC npc = NPC.getFromCache(msg.getObjectID()); + float sellPercent = 1; + + if (npc != null){ + + if (origin.getPlayerCharacter() != null) + sellPercent = npc.getSellPercent(origin.getPlayerCharacter()); + else + sellPercent = npc.getSellPercent(); + + msg.setTrainPercent(sellPercent); //TrainMsg.getTrainPercent(npc)); + } + + Dispatch dispatch = Dispatch.borrow(origin.getPlayerCharacter(), msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + + } + + public static void refinerScreen(RefinerScreenMsg msg, ClientConnection origin) + throws MsgSendException { + + NPC npc = NPC.getFromCache(msg.getNpcID()); + + if (npc != null) + msg.setUnknown02(0); //cost to refine? + + Dispatch dispatch = Dispatch.borrow(origin.getPlayerCharacter(), msg); + DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); + } + + public static void shutdown() { + exit(1); + } + + public static String getUptimeString() { + String outString = null; + java.time.Duration uptimeDuration; + String newLine = System.getProperty("line.separator"); + + try { + outString = "[LUA_UPTIME()]" + newLine; + uptimeDuration = java.time.Duration.between(LocalDateTime.now(), WorldServer.bootTime); + long uptimeSeconds = Math.abs(uptimeDuration.getSeconds()); + String uptime = String.format("%d hours %02d minutes %02d seconds", uptimeSeconds / 3600, (uptimeSeconds % 3600) / 60, (uptimeSeconds % 60)); + outString += "uptime: " + uptime; + outString += " pop: " + SessionManager.getActivePlayerCharacterCount() + " max pop: " + SessionManager._maxPopulation; + } catch (Exception e) { + Logger.error("Failed to build string"); + } + return outString; + } + + private int exec() { + + LocalDateTime nextHeartbeatTime = LocalDateTime.now(); + LocalDateTime nextPopulationFileTime = LocalDateTime.now(); + LocalDateTime nextFlashTrashCheckTime = LocalDateTime.now(); + LocalDateTime nextHourlyJobTime = LocalDateTime.now().withMinute(0).withSecond(0).plusHours(1); + LocalDateTime nextWareHousePushTime = LocalDateTime.now();; + + // Begin execution of main game loop + + this.isRunning = true; + + while (true) { + + if (LocalDateTime.now().isAfter(nextHeartbeatTime)) { + SERVERHEARTBEAT.tick(); + nextHeartbeatTime = LocalDateTime.now().plusNanos(50000000); + } + + if (LocalDateTime.now().isAfter(nextPopulationFileTime)) { + writePopulationFile(); + nextPopulationFileTime = LocalDateTime.now().plusMinutes(1); + } + + if (LocalDateTime.now().isAfter(nextFlashTrashCheckTime)) { + processFlashFile(); + processTrashFile(); + nextFlashTrashCheckTime = LocalDateTime.now().plusSeconds(15); + } + + if (LocalDateTime.now().isAfter(nextHourlyJobTime)) { + Thread hourlyJobThread = new Thread(new HourlyJobThread()); + hourlyJobThread.setName("hourlyJob"); + hourlyJobThread.start(); + nextHourlyJobTime = LocalDateTime.now().withMinute(0).withSecond(0).plusHours(1); + } + + if (LocalDateTime.now().isAfter(nextWareHousePushTime)) { + Thread warehousePushThread = new Thread(new WarehousePushThread()); + warehousePushThread.setName("warehousePush"); + warehousePushThread.start(); + nextWareHousePushTime = LocalDateTime.now().plusMinutes(15); + } + + ThreadUtils.sleep(50); + } + } + + private void initClientConnectionManager() { + + try { + + String name = ConfigManager.MB_WORLD_NAME.getValue(); + + Logger.info("Magicbane network config: " + ConfigManager.MB_BIND_ADDR.getValue() + ":" + ConfigManager.MB_WORLD_PORT.getValue()); + + InetAddress addy = InetAddress.getByName(ConfigManager.MB_BIND_ADDR.getValue()); + int port = Integer.parseInt(ConfigManager.MB_WORLD_PORT.getValue()); + + ClientConnectionManager connectionManager = new ClientConnectionManager(name + ".ClientConnMan", addy, + port); + connectionManager.startup(); + + } catch (IOException e) { + Logger.error("Exception while creating a ClientConnectionManager."); + } + } + + private boolean init() { + + Logger.info("MAGICBANE SERVER GREETING:"); + Logger.info(ConfigManager.MB_WORLD_GREETING.getValue()); + + Logger.info("Initialize network protocol"); + Protocol.initProtocolLookup(); + + Logger.info("Initialize database layer"); + initDatabaselayer(); + + Logger.info("Setting cross server session behavior"); + SessionManager.setCrossServerBehavior(1); // Sets cross server behavior + + Logger.info("Starting Item Production thread"); + ItemProductionManager.ITEMPRODUCTIONMANAGER.startMessagePump(); + + Logger.info("Initializing Errant Guild"); + Guild.CreateErrantGuild(); + + Logger.info("Initializing PowersManager."); + // activate powers manager + PowersManager.initPowersManager(true); + + Logger.info("Initializing granted Skills for Runes"); + DbManager.SkillsBaseQueries.LOAD_ALL_RUNE_SKILLS(); + + Logger.info("Initializing Player Friends"); + DbManager.PlayerCharacterQueries.LOAD_PLAYER_FRIENDS(); + + Logger.info("Initializing NPC Profits"); + DbManager.NPCQueries.LOAD_NPC_PROFITS(); + + Logger.info("Initializing MeshBounds"); + MeshBounds.InitializeBuildingBounds(); + + // Load ItemBases + Logger.info("Loading ItemBases"); + ItemBase.loadAllItemBases(); + + Logger.info("Loading PromotionClasses"); + DbManager.PromotionQueries.GET_ALL_PROMOTIONS(); + + Logger.info("Loading NPC and Mob Equipment Sets"); + EquipmentSetEntry.LoadAllEquipmentSets(); + + Logger.info("Loading Gold Loot for Mobbases"); + MobbaseGoldEntry.LoadMobbaseGold(); + + Logger.info("Loading fidelity mob runes."); + DbManager.MobQueries.LOAD_RUNES_FOR_FIDELITY_MOBS(); + + //load lootTable + Logger.info("Loading Loot Tables"); + LootTable.populateLootTables(); + + // Load new loot system + Logger.info("Loading SuperLoot Tables"); + LootManager.loadLootData(); + RuneBaseAttribute.LoadAllAttributes(); + RuneBase.LoadAllRuneBases(); + BaseClass.LoadAllBaseClasses(); + Race.loadAllRaces(); + RuneBaseEffect.LoadRuneBaseEffects(); + + Logger.info("Loading MobBases."); + DbManager.MobBaseQueries.GET_ALL_MOBBASES(); + + //load item enchantment values + DbManager.LootQueries.LOAD_ENCHANT_VALUES(); + + //initialize realms + Logger.info("Loading Realms"); + Realm.loadAllRealms(); + + Logger.info("Loading Kits"); + DbManager.KitQueries.GET_ALL_KITS(); + + Logger.info("Loading World Grid"); + WorldGrid.InitializeGridObjects(); + + Logger.info("Starting InterestManager."); + WorldGrid.startLoadJob(); + + + Logger.info("Loading Spaital Hash"); + RealmMap.loadRealmImageMap(); + + DbManager.MobBaseQueries.SET_AI_DEFAULTS(); + + Logger.info("Loading blueprint data."); + StaticColliders.loadAllStaticColliders(); + BuildingRegions.loadAllStaticColliders(); + Blueprint.loadAllDoorNumbers(); + Blueprint.loadAllBlueprints(); + + Logger.info("Loading Special Loot For Mobs"); + DbManager.SpecialLootQueries.GenerateSpecialLoot(); + + Logger.info("Initializing Heightmap data"); + HeightMap.loadAlHeightMaps(); + + Logger.info("Loading Race data"); + Enum.RaceType.initRaceTypeTables(); + Race.loadAllRaces(); + + Logger.info("Loading building mountpoint data."); + BuildingLocation.loadAllLocations(); + + // Starting before loading of structures/guilds/characters + // so the database connections are available to write + // historical data. + + Logger.info("Starting Data Warehouse"); + DataWarehouse.bootStrap(); + + Logger.info("Loading Minion Bases."); + MinionType.InitializeMinions(); + + Logger.info("Loading Support Types"); + SupportMsgType.InitializeSupportMsgType(); + + //Load Buildings, Mobs and NPCs for server + + getWorldBuildingsMobsNPCs(); + + // Configure realms for serialization + // Doing this after the world is loaded + + Logger.info("Configuring realm serialization data"); + + try{ + Realm.configureAllRealms(); + }catch(Exception e){ + Logger.error( e.getMessage()); + } + + Logger.info("Loading Mine data."); + //DbManager.MineQueries.syncMineWindowsWithToday(); + Mine.loadAllMines(); + + Logger.info("Loading Shrine data."); + DbManager.ShrineQueries.LOAD_ALL_SHRINES(); + + Logger.info("Initialize Resource type lookup"); + Enum.ResourceType.InitializeResourceTypes(); + + Logger.info("Loading Warehouse data."); + DbManager.WarehouseQueries.LOAD_ALL_WAREHOUSES(); + + Logger.info("Loading Runegate data."); + Runegate.loadAllRunegates(); + + Logger.info("Loading Pirate Names."); + NPC.loadAllPirateNames(); + + Logger.info("Loading Max Skills for Trainers"); + DbManager.SkillsBaseQueries.LOAD_ALL_MAX_SKILLS_FOR_CONTRACT(); + + //pick a startup Hotzone + ZoneManager.generateAndSetRandomHotzone(); + + Logger.info("Loading All Players from database to Server Cache"); + long start = System.currentTimeMillis(); + try{ + DbManager.PlayerCharacterQueries.GET_ALL_CHARACTERS(); + }catch(Exception e){ + e.printStackTrace(); + } + + long end = System.currentTimeMillis(); + Logger.info("Loading All Players took " + (end - start) + " ms."); + + ItemProductionManager.ITEMPRODUCTIONMANAGER.initialize(); + + Logger.info("Loading Player Heraldries"); + DbManager.PlayerCharacterQueries.LOAD_HERALDY(); + + Logger.info("Running Heraldry Audit for Deleted Players"); + Heraldry.AuditHeraldry(); + + if (ZoneManager.getHotZone() != null) + WorldServer.setLastHZChange(System.currentTimeMillis()); + + //Start Mines. + + MineActiveJob maj = new MineActiveJob(); + maj.run(); + + Logger.info("Starting Mobile AI FSM"); + MobileFSMManager.getInstance(); + + + for (Zone zone : ZoneManager.getAllZones()) { + if (zone.getHeightMap() != null) { + if (zone.getHeightMap().getBucketWidthX() == 0) { + System.out.println("Zone load num: " + zone.getLoadNum() + " has no bucket width"); + } + } + } + + Logger.info("World data loaded."); + + //set default accesslevel for server *** Refactor who two separate variables? + MBServerStatics.accessLevel = MBServerStatics.worldAccessLevel; + Logger.info("Default access level set to " + MBServerStatics.accessLevel); + + Logger.info("Initializing Network"); + Network.init(); + + Logger.info("Initializing Client Connection Manager"); + initClientConnectionManager(); + + Logger.info("Starting message pumps"); + DispatchMessage.startMessagePump(); + + // Run maintenance + MaintenanceManager.dailyMaintenance(); + + // Disabled but kept in case of emergency + Logger.info("Starting Orphan Item Purge"); + PurgeOprhans.startPurgeThread(); + + // Calculate bootstrap time and rest boot time to current time. + java.time.Duration bootDuration = java.time.Duration.between(LocalDateTime.now(), bootTime); + long bootSeconds = Math.abs(bootDuration.getSeconds()); + String boottime = String.format("%d hours %02d minutes %02d seconds", bootSeconds / 3600, (bootSeconds % 3600) / 60, (bootSeconds % 60)); + Logger.info("Bootstrap time was " + boottime); + + bootTime = LocalDateTime.now(); + LootTable.initialized = true; + + Logger.info("Running garbage collection..."); + System.gc(); + return true; + } + + protected boolean initDatabaselayer() { + + // Try starting a GOM <-> DB connection. + try { + + Logger.info("Configuring GameObjectManager to use Database: '" + + ConfigManager.MB_DATABASE_NAME.getValue() + "' on " + + ConfigManager.MB_DATABASE_ADDRESS.getValue() + ':' + + ConfigManager.MB_DATABASE_PORT.getValue()); + + DbManager.configureDatabaseLayer(); + + } catch (Exception e) { + Logger.error(e.getMessage()); + return false; + } + + PreparedStatementShared.submitPreparedStatementsCleaningJob(); + + if (MBServerStatics.DB_DEBUGGING_ON_BY_DEFAULT) { + PreparedStatementShared.enableDebugging(); + } + + return true; + } + + + private void getWorldBuildingsMobsNPCs() { + + ArrayList rootParent; + + rootParent = DbManager.ZoneQueries.GET_MAP_NODES(MBServerStatics.worldUUID); + + if (rootParent.isEmpty()) { + Logger.error("populateWorldBuildings: No entries found in worldMap for parent " + MBServerStatics.worldUUID); + return; + } + + //Set sea floor object for server + Zone seaFloor = rootParent.get(0); + seaFloor.setParent(null); + ZoneManager.setSeaFloor(seaFloor); + + // zoneManager.addZone(seaFloor.getLoadNum(), seaFloor); <- DIE IN A FUCKING CAR FIRE BONUS CODE LIKE THIS SUCKS FUCKING DICK + + rootParent.addAll(DbManager.ZoneQueries.GET_ALL_NODES(seaFloor)); + + long start = System.currentTimeMillis(); + + for (Zone zone : rootParent) { + + try { + ZoneManager.addZone(zone.getLoadNum(), zone); + + try{ + zone.generateWorldAltitude(); + }catch(Exception e){ + Logger.error( e.getMessage()); + e.printStackTrace(); + } + + //Handle Buildings + + ArrayList bList; + bList = DbManager.BuildingQueries.GET_ALL_BUILDINGS_FOR_ZONE(zone); + + for (Building b : bList) { + + try { + b.setObjectTypeMask(MBServerStatics.MASK_BUILDING); + b.setLoc(b.getLoc()); + } catch (Exception e) { + Logger.error( b.getObjectUUID() + " returned an Error Message :" + e.getMessage()); + } + } + + //Handle Mobs + ArrayList mobs; + mobs = DbManager.MobQueries.GET_ALL_MOBS_FOR_ZONE(zone); + + for (Mob m : mobs) { + m.setObjectTypeMask(MBServerStatics.MASK_MOB | m.getTypeMasks()); + m.setLoc(m.getLoc()); + m.setParentZone(zone); + + //ADD GUARDS HERE. + if (m.getBuilding() != null && m.getBuilding().getBlueprint() != null && m.getBuilding().getBlueprint().getBuildingGroup() == BuildingGroup.BARRACK) + DbManager.MobQueries.LOAD_PATROL_POINTS(m); + } + + //Handle npc's + ArrayList npcs; + + // Ignore npc's on the seafloor (npc guild leaders, etc) + + if (zone.equals(seaFloor)) + continue; + + npcs = DbManager.NPCQueries.GET_ALL_NPCS_FOR_ZONE(zone); + + for (NPC n : npcs) { + + try { + n.setObjectTypeMask(MBServerStatics.MASK_NPC); + n.setLoc(n.getLoc()); + n.setParentZone(zone); + } catch (Exception e) { + Logger.error( n.getObjectUUID() + " returned an Error Message :" + e.getMessage()); + } + } + + //Handle cities + + City.loadCities(zone); + ZoneManager.populateWorldZones(zone); + + } catch (Exception e) { + Logger.info(e.getMessage() + zone.getName() + ' ' + zone.getObjectUUID()); + } + } + + Logger.info("time to load: " + (System.currentTimeMillis() - start) + " ms"); + } + + /** + * Called to remove a client on "leave world", "quit game", killed client + * process, etc. + */ + + public void removeClient(ClientConnection origin) { + + if (origin == null) { + Logger.info( + "ClientConnection null in removeClient."); + return; + } + + PlayerCharacter pc = SessionManager.getPlayerCharacter( + origin); + + if (pc == null) + // TODO log this + return; + + //cancel any trade + if (pc.getCharItemManager() != null) + pc.getCharItemManager().endTrade(true); + + // logout + long delta = MBServerStatics.LOGOUT_TIMER_MS; + + if (System.currentTimeMillis() - pc.getTimeStamp("LastCombatPlayer") < 60000) { + delta = 60000; + + } + pc.stopMovement(pc.getLoc()); + UpdateStateMsg updateStateMsg = new UpdateStateMsg(); + updateStateMsg.setPlayer(pc); + + updateStateMsg.setActivity(5); + DispatchMessage.dispatchMsgToInterestArea(pc, updateStateMsg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); + + if (pc.getRegion() != null) + if (PlayerCharacter.CanBindToBuilding(pc, pc.getRegion().parentBuildingID)) + pc.bindBuilding = pc.getRegion().parentBuildingID; + else + pc.bindBuilding = 0; + + pc.getLoadedObjects().clear(); + pc.getLoadedStaticObjects().clear(); + + LogoutCharacterJob logoutJob = new LogoutCharacterJob(pc, this); + JobContainer jc = JobScheduler.getInstance().scheduleJob(logoutJob, + System.currentTimeMillis() + delta); + pc.getTimers().put("Logout", jc); + pc.getTimestamps().put("logout", System.currentTimeMillis()); + + //send update to friends that you are logged off. + + PlayerFriends.SendFriendsStatus(pc,false); + + } + + public void logoutCharacter(PlayerCharacter player) { + + if (player == null) { + Logger.error("Unable to find PlayerCharacter to logout"); + return; + } + + player.getTimestamps().put("logout", System.currentTimeMillis()); + player.setEnteredWorld(false); + + // remove from simulation and zero current loc + + WorldGrid.RemoveWorldObject(player); + + // clear Logout Timer + + if (player.getTimers() != null) + player.getTimers().remove("Logout"); + + if (player.getPet() != null) + player.getPet().dismiss(); + + player.dismissNecroPets(); + + // Set player inactive so they quit loading for other players + + player.setActive(false); + + // Remove from group + + Group group = GroupManager.getGroup(player); + + try { + if (group != null) + GroupManager.LeaveGroup(player); + } catch (MsgSendException e) { + Logger.error( e.toString()); + } + + player.respawnLock.writeLock().lock(); + try{ + if (!player.isAlive()) + player.respawn(false, false, true); + }catch(Exception e){ + Logger.error(e); + }finally{ + player.respawnLock.writeLock().unlock(); + } + } + + + public static void writePopulationFile() { + + int population = SessionManager.getActivePlayerCharacterCount(); +try { + + + File populationFile = new File(MBServerStatics.DEFAULT_DATA_DIR + ConfigManager.MB_WORLD_NAME.getValue().replaceAll("'","") + ".pop"); + FileWriter fileWriter; + + try { + fileWriter = new FileWriter(populationFile, false); + fileWriter.write(Integer.toString(population)); + fileWriter.close(); + } catch (IOException e) { + e.printStackTrace(); + } + +}catch(Exception e){ + Logger.error(e); +} + } + + private void processTrashFile() { + + ArrayList machineList; + ArrayList trashList = new ArrayList<>(); + ArrayList accountList = new ArrayList<>(); + + File trashFile = new File("trash"); + + if (trashFile.exists() == false) + return; + + // Build list of machineID's in the trash file + + machineList = DbManager.AccountQueries.GET_TRASH_LIST(); + + // Build list of trash characters associated with that machineID + + for (String machineID:machineList) { + trashList = DbManager.AccountQueries.GET_ALL_CHARS_FOR_MACHINE(machineID); + + + // Deactivate these players and add them to loginCache table + + for (PlayerCharacter trashPlayer : trashList) { + + if (trashPlayer == null) + continue; + + // Need to collate accounts. + + if (!accountList.contains(trashPlayer.getAccount().getObjectUUID())) + accountList.add(trashPlayer.getAccount().getObjectUUID()); + + DbManager.PlayerCharacterQueries.SET_ACTIVE(trashPlayer, false); + DbManager.AccountQueries.INVALIDATE_LOGIN_CACHE(trashPlayer.getObjectUUID(), "character"); + } + } + + // delete vault of associated accounts and then invalidate them + // in the login cache. + + for (Integer accountID : accountList) { + DbManager.AccountQueries.DELETE_VAULT_FOR_ACCOUNT(accountID); + DbManager.AccountQueries.INVALIDATE_LOGIN_CACHE(accountID, "account"); + } + + // Trigger the Login Server to invalidate these accounts in the cache.. + + try { + Files.write(Paths.get("cacheInvalid"), "".getBytes()); + } catch (IOException e) { + e.printStackTrace(); + } + + // If any of these players are active disconnect them. + // The account and player should be removed from the login + // server cache file by now. + + Timer timer = new Timer("Disconnect Trash"); + timer.schedule(new DisconnectTrashTask( trashList ), 3000L); + + // Clean up after ourselves + + try { + Files.deleteIfExists(Paths.get("trash")); + DbManager.AccountQueries.CLEAR_TRASH_TABLE(); + } catch (IOException e) { + e.printStackTrace(); + } + + } + + private void processFlashFile() { + + File flashFile = new File("flash"); + String flashString; + List fileContents; + + if (flashFile.exists() == false) + return; + + try { + fileContents = Files.readAllLines(Paths.get("flash")); + } catch (IOException e) { + return; + } + + // Flash file detected: read contents + // and send as a flash. + + flashString = fileContents.toString(); + + if (flashString == null) + return; + + if (flashString == "") + flashString = "Rebooting for to fix bug."; + + Logger.info( "Sending flash from external interface"); + Logger.info( "Msg: " + flashString); + + ChatSystemMsg msg = new ChatSystemMsg(null, flashString); + msg.setChannel(engine.Enum.ChatChannelType.FLASH.getChannelID()); + msg.setMessageType(Enum.ChatMessageType.INFO.ordinal()); + DispatchMessage.dispatchMsgToAll(msg); + + // Delete file + + try { + Files.deleteIfExists(Paths.get("flash")); + } catch (IOException e) { + e.printStackTrace(); + } + + } +} \ No newline at end of file diff --git a/src/engine/session/CSSession.java b/src/engine/session/CSSession.java new file mode 100644 index 00000000..c6704d96 --- /dev/null +++ b/src/engine/session/CSSession.java @@ -0,0 +1,123 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.session; + +import engine.gameManager.DbManager; +import engine.objects.AbstractGameObject; +import engine.objects.Account; +import engine.objects.PlayerCharacter; + +import java.net.InetAddress; + + +public class CSSession extends AbstractGameObject { + + private String sessionID; + private PlayerCharacter playerCharacter; + private Account account; + + + private String machineID; + + public CSSession(String sessionID, Account acc, PlayerCharacter pc, String machineID) { + super(); + this.sessionID = sessionID; + this.playerCharacter = pc; + this.account = acc; + this.machineID = machineID; + + if (this.playerCharacter != null) + PlayerCharacter.initializePlayer(this.playerCharacter); + } + + public PlayerCharacter getPlayerCharacter() { + return this.playerCharacter; + } + + public void setPlayerCharacter(PlayerCharacter pc) { + this.playerCharacter = pc; + } + + public Account getAccount() { + return this.account; + } + + public void setAccount(Account acc) { + this.account = acc; + } + + @Override + public void removeFromCache() {} + + /* + * Database + */ + public static boolean addCrossServerSession(String secKey, Account acc, InetAddress inet, String machineID) { + return DbManager.CSSessionQueries.ADD_CSSESSION(secKey, acc, inet, machineID); + // PreparedStatementShared ps = null; + // try { + // ps = prepareStatement("INSERT INTO sessions (secretKey, accountID, vbID, sessionIP) VALUES (?,?,?,INET_ATON(?))"); + // ps.setString(1, secKey); + // ps.setInt(2, acc.getUUID(), true); + // ps.setInt(3, acc.getVBID()); + // ps.setString(4, StringUtils.InetAddressToClientString(inet)); + // if (ps.executeUpdate() > 0) + // return true; + // } catch (SQLException e) { + // Logger.error("CSSession", "Failed to create cross server session"); + // } finally { + // ps.release(); + // } + // return false; + } + + public static boolean deleteCrossServerSession(String secKey) { + return DbManager.CSSessionQueries.DELETE_CSSESSION(secKey); + // PreparedStatementShared ps = null; + // try { + // ps = prepareStatement("DELETE FROM sessions WHERE secretKey = ?"); + // ps.setString(1, secKey); + // if (ps.executeUpdate() > 0) + // return true; + // } catch (SQLException e) { + // Logger.error("CSSession", "Failed to delete cross server session"); + // } finally { + // ps.release(); + // } + // return false; + } + + public static boolean updateCrossServerSession(String secKey, int charID) { + return DbManager.CSSessionQueries.UPDATE_CSSESSION(secKey, charID); + } + + public static CSSession getCrossServerSession(String secKey) { + + CSSession sessionInfo; + + try { + sessionInfo = DbManager.CSSessionQueries.GET_CSSESSION(secKey); + } catch (Exception e) { + sessionInfo = null; + } + + return sessionInfo; + } + + public String getMachineID() { + return machineID; + } + + @Override + public void updateDatabase() { + // TODO Auto-generated method stub + + } +} diff --git a/src/engine/session/Session.java b/src/engine/session/Session.java new file mode 100644 index 00000000..d02ffd94 --- /dev/null +++ b/src/engine/session/Session.java @@ -0,0 +1,71 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + package engine.session; + +import engine.Enum; +import engine.gameManager.DbManager; +import engine.net.client.ClientConnection; +import engine.objects.Account; +import engine.objects.PlayerCharacter; + + +public class Session { + + private SessionID sessionID; + private PlayerCharacter playerCharacter; + private Account account; + private ClientConnection conn; + + public Session(SessionID sessionID, Account acc, ClientConnection conn) { + super(); + this.sessionID = sessionID; + this.playerCharacter = null; + this.account = acc; + this.conn = conn; + } + + public PlayerCharacter getPlayerCharacter() { + if (this.playerCharacter != null) { + + if (DbManager.inCache(Enum.GameObjectType.PlayerCharacter, this.playerCharacter.getObjectUUID())) + this.playerCharacter = (PlayerCharacter) DbManager.getFromCache(Enum.GameObjectType.PlayerCharacter, this.playerCharacter.getObjectUUID()); + } + return this.playerCharacter; + } + + public void setPlayerCharacter(PlayerCharacter pc) { + this.playerCharacter = pc; + } + + public SessionID getSessionID() { + return this.sessionID; + } + + public void setSessionID(SessionID sessionID) { + this.sessionID = sessionID; + } + + public Account getAccount() { + return this.account; + } + + public void setAccount(Account acc) { + this.account = acc; + } + + public ClientConnection getConn() { + return this.conn; + } + + public void setConn(ClientConnection conn) { + this.conn = conn; + } + +} diff --git a/src/engine/session/SessionID.java b/src/engine/session/SessionID.java new file mode 100644 index 00000000..921b976b --- /dev/null +++ b/src/engine/session/SessionID.java @@ -0,0 +1,50 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.session; + +import engine.util.ByteUtils; + +public class SessionID { + + private final byte[] data; + private final String dataAsString; + + public SessionID(byte[] data) { + super(); + this.data = data; + this.dataAsString = ByteUtils.byteArrayToStringHex(data); + } + + @Override + public boolean equals(Object obj) { + boolean out = false; + if(obj instanceof SessionID) { + out = true; + SessionID id = (SessionID) obj; + for (int i = 0; out && i < id.data.length; ++i) { + if (id.data[i] != this.data[i]) { + out = false; + } + } + } + + return out; + } + + @Override + public String toString() { + return this.dataAsString; + } + + public final byte[] getData() { + return data; + } + +} diff --git a/src/engine/util/ByteAnalyzer.java b/src/engine/util/ByteAnalyzer.java new file mode 100644 index 00000000..a70de21c --- /dev/null +++ b/src/engine/util/ByteAnalyzer.java @@ -0,0 +1,224 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + package engine.util; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; + +public class ByteAnalyzer { + + public static void analyze4Bytes(byte[] data, String Label, + boolean switchEndian) throws IOException { + + ByteArrayInputStream bias = new ByteArrayInputStream(data); + DataInputStream dis = new DataInputStream(bias); + dis.mark(4); + + System.out.println("Analysis 4 bytes: (" + Label + ')'); + System.out.println("\t Hex: " + ByteUtils.byteArrayToStringHex(data)); + System.out.println(ByteAnalyzer.buildAll4(dis, switchEndian)); + System.out.println(ByteAnalyzer.buildUTF8(dis)); + System.out.println(ByteAnalyzer.buildUTF16(dis)); + System.out.println(ByteAnalyzer.buildRawNumericalBytes(dis)); + System.out.println(); + } + + public static void analyze8Bytes(byte[] data, String Label, + boolean switchEndian) throws IOException { + + ByteArrayInputStream bias = new ByteArrayInputStream(data); + DataInputStream dis = new DataInputStream(bias); + dis.mark(8); + + System.out.println("Analysis for 8 bytes: (" + Label + ')'); + System.out.println("\tHex: " + ByteUtils.byteArrayToStringHex(data)); + System.out.println(ByteAnalyzer.buildAll8(dis, switchEndian)); + dis.reset(); + System.out.println(ByteAnalyzer.buildUTF8(dis)); + System.out.println(ByteAnalyzer.buildUTF16(dis)); + System.out.println(ByteAnalyzer.buildRawNumericalBytes(dis)); + System.out.println("\n"); + } + + public static String buildAll8(DataInputStream indis, boolean se) + throws IOException { + byte[] ba = new byte[8]; + indis.read(ba); + DataInputStream dis = new DataInputStream(new ByteArrayInputStream(ba)); + dis.mark(8); + + String out = ""; + + out += buildFromTemplate("8", dis, se); + out += buildFromTemplate("4.4", dis, se); + + out += buildFromTemplate("4.2.2", dis, se); + out += buildFromTemplate("4.2.1.1", dis, se); + out += buildFromTemplate("4.1.2.1", dis, se); + out += buildFromTemplate("4.1.1.2", dis, se); + out += buildFromTemplate("4.1.1.1.1", dis, se); + + out += buildFromTemplate("2.2.4", dis, se); + out += buildFromTemplate("2.1.1.4", dis, se); + out += buildFromTemplate("1.2.1.4", dis, se); + out += buildFromTemplate("1.1.2.4", dis, se); + out += buildFromTemplate("1.1.1.1.4", dis, se); + + out += buildFromTemplate("2.4.2", dis, se); + out += buildFromTemplate("2.4.1.1", dis, se); + out += buildFromTemplate("1.1.4.2", dis, se); + out += buildFromTemplate("1.1.4.1.1", dis, se); + + out += buildFromTemplate("2.1.2.2.1", dis, se); + out += buildFromTemplate("2.1.2.1.2", dis, se); + out += buildFromTemplate("1.2.2.2.1", dis, se); + out += buildFromTemplate("1.2.2.1.2", dis, se); + + out += buildFromTemplate("1.1.1.2.2.1", dis, se); + out += buildFromTemplate("2.1.2.1.1.1", dis, se); + out += buildFromTemplate("1.1.1.2.1.1.1", dis, se); + out += buildFromTemplate("1.1.1.1.1.1.1.1", dis, se); + + out += buildFromTemplate("2.1.1.1.1.1.1", dis, se); + out += buildFromTemplate("1.2.1.1.1.1.1", dis, se); + out += buildFromTemplate("1.1.2.1.1.1.1", dis, se); + + out += buildFromTemplate("1.1.1.1.2.1.1", dis, se); + out += buildFromTemplate("1.1.1.1.1.2.1", dis, se); + out += buildFromTemplate("1.1.1.1.1.1.2", dis, se); + + return out; + } + + public static String buildAll4(DataInputStream indis, boolean se) + throws IOException { + byte[] ba = new byte[4]; + indis.read(ba); + DataInputStream dis = new DataInputStream(new ByteArrayInputStream(ba)); + dis.mark(4); + + String out = ""; + out += buildFromTemplate("4", dis, se); + out += buildFromTemplate("2.2", dis, se); + out += buildFromTemplate("2.1.1", dis, se); + out += buildFromTemplate("1.2.1", dis, se); + out += buildFromTemplate("1.1.2", dis, se); + out += buildFromTemplate("1.1.1.1", dis, se); + + return out; + } + + public static String buildFromTemplate(String template, + DataInputStream dis, boolean se) throws IOException { + String out = '\t' + template + ": "; + + for (int i = template.length(); i < 16; ++i) { + out += " "; + } + + template = template.replace(".", ""); + String[] items = template.split(""); + dis.mark(dis.available()); + + for (String s : items) { + if (s.equals("1")) { + out += " (B:" + dis.readByte(); + dis.reset(); + out += "/uB:" + dis.readUnsignedByte() + ')'; + } else if (s.equals("2")) { + byte[] read = new byte[2]; + dis.read(read); + byte[] use = new byte[2]; + if (se) { + use = ByteUtils.switchByteArrayEndianness(read); + } else { + use = read; + } + out += " (S:" + + new DataInputStream(new ByteArrayInputStream(use)) + .readShort(); + out += "/uS:" + + new DataInputStream(new ByteArrayInputStream(use)) + .readUnsignedShort() + ')'; + + } else if (s.equals("4")) { + byte[] read = new byte[4]; + dis.read(read); + byte[] use = new byte[4]; + if (se) { + use = ByteUtils.switchByteArrayEndianness(read); + } else { + use = read; + } + out += " (I:"; + out += new DataInputStream(new ByteArrayInputStream(use)) + .readInt(); + + out += " / F:"; + out += new DataInputStream(new ByteArrayInputStream(use)) + .readFloat() + + ")"; + } else if (s.equals("8")) { + byte[] read = new byte[8]; + dis.read(read); + + byte[] use = new byte[8]; + if (se) { + use = ByteUtils.switchByteArrayEndianness(read); + } else { + use = read; + } + out += " (L:"; + out += new DataInputStream(new ByteArrayInputStream(use)) + .readLong(); + + out += " / D:"; + out += new DataInputStream(new ByteArrayInputStream(use)) + .readDouble() + + ")"; + } + } + dis.reset(); + return out + '\n'; + } + + public static String buildUTF8(DataInputStream dis) throws IOException { + dis.mark(dis.available()); + String out = "\tUTF-8: "; + while (dis.available() > 1) { + out += " '" + (char) dis.read() + '\''; + } + dis.reset(); + return out; + } + + public static String buildUTF16(DataInputStream dis) throws IOException { + dis.mark(dis.available()); + String out = "\tUTF-16:"; + while (dis.available() > 1) { + out += " '" + dis.readChar() + '\''; + } + dis.reset(); + return out; + } + + public static String buildRawNumericalBytes(DataInputStream dis) + throws IOException { + dis.mark(dis.available()); + String out = "\tRaw Bytes (int vals): "; + while (dis.available() > 1) { + out += " '" + dis.read() + '\''; + } + dis.reset(); + return out; + } + +} diff --git a/src/engine/util/ByteBufferUtils.java b/src/engine/util/ByteBufferUtils.java new file mode 100644 index 00000000..f3aa91c8 --- /dev/null +++ b/src/engine/util/ByteBufferUtils.java @@ -0,0 +1,221 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.util; + +import java.io.UnsupportedEncodingException; +import java.nio.BufferOverflowException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; + +public class ByteBufferUtils { + public static String getString(ByteBuffer bb) + throws BufferUnderflowException { + return getString(bb, false, false); + } + + public static String getString(ByteBuffer bb, boolean switchEndian, boolean small) + throws BufferUnderflowException { + String out = ""; + synchronized (bb) { + + //This version works with non-latin characters + int stringLen; + if (small) + stringLen = (int)bb.get(); + else + stringLen = bb.getInt(); + if (switchEndian) + stringLen = ((Integer.reverseBytes(stringLen)) * 2); + else + stringLen *= 2; + byte[] b = new byte[stringLen]; + for (int i=0;i 255) + length = 255; //limit for smallString + + synchronized (bb) { + // Write length + if (small) + bb.put((byte)length); + else { + if (switchEndian) { + bb.putInt(Integer.reverseBytes(length)); + } else { + bb.putInt(length); + } + } + // Write chars + for (int i=0;i= (bb.capacity() * 0.9); + } + + //FIXME: Replace these!!! +// public static ByteBuffer resizeByteBuffer(ByteBuffer bb, int multiplyer) { +// +// ByteBuffer out = ByteBuffer.allocate(bb.capacity() * multiplyer); +// +// // Copy the data to a temp buf +// bb.flip(); +// out.put(bb); +// +// return out; +// } +// +// public static ByteBuffer shrinkByteBuffer(ByteBuffer bb) { +// +// bb.flip(); +// ByteBuffer out = ByteBuffer.allocate(bb.remaining()); +// out.put(bb); +// return out; +// } + +} diff --git a/src/engine/util/ByteUtils.java b/src/engine/util/ByteUtils.java new file mode 100644 index 00000000..51fb65c9 --- /dev/null +++ b/src/engine/util/ByteUtils.java @@ -0,0 +1,171 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + package engine.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +public abstract class ByteUtils { + + private ByteUtils() { + } + + public static byte[] switchByteArrayEndianness(byte[] in) { + int size = in.length; + + byte[] out = new byte[size]; + + for (int i = 0; i < size; ++i) { + out[size - i - 1] = in[i]; + } + return out; + } + + /* + * Converts a single byte to a hex StringBuffer + */ + public static void byteToStringHex(byte b, StringBuffer buf) { + char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F' }; + int high = ((b & 0xf0) >> 4); + int low = (b & 0x0f); + buf.append(hexChars[high]); + buf.append(hexChars[low]); + } + + /* + * Converts a single byte to a hex String + */ + public static String byteToStringHex(byte b) { + StringBuffer sb = new StringBuffer(); + byteToStringHex(b, sb); + return sb.toString(); + } + + /* + * Converts a byte array to hex String + */ + public static String byteArrayToStringHex(byte[] block) { + StringBuffer buf = new StringBuffer(); + int len = block.length; + + for (int i = 0; i < len; i++) { + ByteUtils.byteToStringHex(block[i], buf); + if (i < len - 1) { + buf.append(':'); + } + } + return buf.toString(); + } + + /* + * Converts a single byte to a hex StringBuffer + */ + public static void byteToSafeStringHex(byte b, StringBuffer buf) { + char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f' }; + int high = ((b & 0xf0) >> 4); + int low = (b & 0x0f); + buf.append(hexChars[high]); + buf.append(hexChars[low]); + } + + /* + * Converts a single byte to a hex String + */ + public static String byteToSafeStringHex(byte b) { + StringBuffer sb = new StringBuffer(); + byteToSafeStringHex(b, sb); + return sb.toString(); + } + + /* + * Converts a byte array to hex String + */ + public static String byteArrayToSafeStringHex(byte[] block) { + StringBuffer buf = new StringBuffer(); + + int len = block.length; + + for (int i = 0; i < len; i++) { + ByteUtils.byteToSafeStringHex(block[i], buf); + } + return buf.toString(); + } + + /* + * Converts a hex string to Byte Array + */ + public static byte[] stringHexToByteArray(String hex) { + int length = hex.length(); + char[] hexchar = hex.toCharArray(); + byte[] ret = new byte[length / 2]; + int i1 = 0; + + for (int i = 0; i < length - 1; i += 2) { + ret[i1] = (byte) (Character.digit(hexchar[i], 16) * 16 + Character + .digit(hexchar[i + 1], 16)); + i1++; + } + + return ret; + } + + /* + * Converts a hex string formatted by our byteToStringHex to a byte array + * returns null if passed a null string + */ + public static byte[] formattedStringHexToByteArray(String hex) { + if(hex == null){ + return null; + } + + String tmpString = hex.replaceAll(":",""); + int length = tmpString.length(); + char[] hexchar = tmpString.toCharArray(); + byte[] ret = new byte[length / 2]; + int i1 = 0; + + for (int i = 0; i < length - 1; i += 2) { + ret[i1] = (byte) (Character.digit(hexchar[i], 16) * 16 + Character + .digit(hexchar[i + 1], 16)); + i1++; + } + + return ret; + } + + public static byte[] compress(final byte[] in) throws IOException{ + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final GZIPOutputStream gzOs = new GZIPOutputStream(out); + gzOs.write(in); + gzOs.close(); + return out.toByteArray(); + } + + public static byte[] decompress(final byte[] in) throws IOException{ + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final GZIPInputStream gzIs = new GZIPInputStream(new ByteArrayInputStream(in)); + final byte[] buffer = new byte[512]; + int lastRead = 0; + + lastRead = gzIs.read(buffer); + while (lastRead > 0) { + out.write(buffer,0,lastRead); + lastRead = gzIs.read(buffer); + } + gzIs.close(); + return out.toByteArray(); + } + +} diff --git a/src/engine/util/Hasher.java b/src/engine/util/Hasher.java new file mode 100644 index 00000000..2914c03a --- /dev/null +++ b/src/engine/util/Hasher.java @@ -0,0 +1,382 @@ +package engine.util; + + +import java.util.*; + +/* + * Author: https://github.com/peet/hashids.java + */ + +public class Hasher { + + private static final String DEFAULT_ALPHABET = "xcS4F6h89aUbideAI7tkynuopqrXCgTE5GBKHLMjfRsz"; + private static final int[] PRIMES = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43}; + private static final int[] SEPS_INDICES = {0, 4, 8, 12}; + + private String salt_ = ""; + + private String alphabet_ = ""; + + private int minHashLength_; + + private ArrayList seps_ = new ArrayList<>(); + private ArrayList guards_ = new ArrayList<>(); + + public Hasher() { + this(""); + } + + public Hasher(String salt) { + this(salt, 0); + } + + public Hasher(String salt, int minHashLength) { + this(salt, minHashLength, DEFAULT_ALPHABET); + } + + public Hasher(String salt, int minHashLength, String alphabet) { + if (alphabet == null || alphabet.trim().isEmpty()) { + throw new IllegalArgumentException("alphabet must not be empty"); + } + + if (salt != null) { + salt_ = salt; + } + + if (minHashLength > 0) { + minHashLength_ = minHashLength; + } + + alphabet_ = join(new LinkedHashSet<>(Arrays.asList(alphabet.split(""))), ""); + + if (alphabet_.length() < 4) { + throw new IllegalArgumentException("Alphabet must contain at least 4 unique characters."); + } + + for (int prime : PRIMES) { + if (prime < alphabet_.length()) { + char c = alphabet_.charAt(prime - 1); + seps_.add(c); + alphabet_ = alphabet_.replace(c, ' '); + } + } + + for (int index : SEPS_INDICES) { + if (index < seps_.size()) { + guards_.add(seps_.get(index)); + seps_.remove(index); + } + } + + alphabet_ = consistentShuffle(alphabet_.replaceAll(" ", ""), salt_); + } + + public String encrypt(long... numbers) { + return encode(numbers, alphabet_, salt_, minHashLength_); + } + + public long[] decrypt(String hash) { + return decode(hash); + } + + private String encode(long[] numbers, String alphabet, String salt, int minHashLength) { + String ret = ""; + String seps = consistentShuffle(join(seps_, ""), join(numbers, "")); + char lotteryChar = 0; + + for (int i = 0; i < numbers.length; i++) { + if (i == 0) { + String lotterySalt = join(numbers, "-"); + for (long number : numbers) { + lotterySalt += "-" + (number + 1) * 2; + } + String lottery = consistentShuffle(alphabet, lotterySalt); + lotteryChar = lottery.charAt(0); + ret += lotteryChar; + + alphabet = lotteryChar + alphabet.replaceAll(String.valueOf(lotteryChar), ""); + } + + alphabet = consistentShuffle(alphabet, ((int) lotteryChar & 12345) + salt); + ret += hash(numbers[i], alphabet); + + if (i + 1 < numbers.length) { + ret += seps.charAt((int) ((numbers[i] + i) % seps.length())); + } + } + + if (ret.length() < minHashLength) { + int firstIndex = 0; + for (int i = 0; i < numbers.length; i++) { + firstIndex += (i + 1) * numbers[i]; + } + + int guardIndex = firstIndex % guards_.size(); + char guard = guards_.get(guardIndex); + ret = guard + ret; + + if (ret.length() < minHashLength) { + guardIndex = (guardIndex + ret.length()) % guards_.size(); + guard = guards_.get(guardIndex); + ret += guard; + } + } + + while (ret.length() < minHashLength) { + long[] padArray = new long[]{alphabet.charAt(1), alphabet.charAt(0)}; + String padLeft = encode(padArray, alphabet, salt, 0); + String padRight = encode(padArray, alphabet, join(padArray, ""), 0); + + ret = padLeft + ret + padRight; + int excess = ret.length() - minHashLength; + if (excess > 0) { + ret = ret.substring(excess / 2, excess / 2 + minHashLength); + } + alphabet = consistentShuffle(alphabet, salt + ret); + } + + return ret; + } + + private static String hash(long number, String alphabet) { + String hash = ""; + + while (number > 0) { + hash = alphabet.charAt((int) (number % alphabet.length())) + hash; + number /= alphabet.length(); + } + + return hash; + } + + private static long unhash(String hash, String alphabet) { + int number = 0; + + for (int i = 0; i < hash.length(); i++) { + int pos = alphabet.indexOf(hash.charAt(i)); + number += pos * (int) Math.pow(alphabet.length(), hash.length() - i - 1); + } + + return number; + } + + private long[] decode(String hash) { + List ret = new ArrayList<>(); + String originalHash = hash; + + if (hash != null && !hash.isEmpty()) { + String alphabet = ""; + char lotteryChar = 0; + + for (char guard : guards_) { + hash = hash.replaceAll(String.valueOf(guard), " "); + } + + String[] hashSplit = hash.split(" "); + + hash = hashSplit[hashSplit.length == 3 || hashSplit.length == 2 ? 1 : 0]; + + for (char sep : seps_) { + hash = hash.replaceAll(String.valueOf(sep), " "); + } + + String[] hashArray = hash.split(" "); + for (int i = 0; i < hashArray.length; i++) { + String subHash = hashArray[i]; + + if (subHash != null && !subHash.isEmpty()) { + if (i == 0) { + lotteryChar = hash.charAt(0); + subHash = subHash.substring(1); + alphabet = lotteryChar + alphabet_.replaceAll(String.valueOf(lotteryChar), ""); + } + } + + if (alphabet.length() > 0) { + alphabet = consistentShuffle(alphabet, ((int) lotteryChar & 12345) + salt_); + ret.add(unhash(subHash, alphabet)); + } + } + } + + long[] numbers = longListToPrimitiveArray(ret); + + if (!encrypt(numbers).equals(originalHash)) { + return new long[0]; + } + + return numbers; + } + + private static String consistentShuffle(String alphabet, String salt) { + String ret = ""; + + if (!alphabet.isEmpty()) { + List alphabetArray = charArrayToStringList(alphabet.toCharArray()); + if (salt == null || salt.isEmpty()) { + salt = new String(new char[]{'\0'}); + } + + int[] sortingArray = new int[salt.length()]; + for (int i = 0; i < salt.length(); i++) { + sortingArray[i] = salt.charAt(i); + } + + for (int i = 0; i < sortingArray.length; i++) { + boolean add = true; + + for (int k = i; k != sortingArray.length + i - 1; k++) { + int nextIndex = (k + 1) % sortingArray.length; + + if (add) { + sortingArray[i] += sortingArray[nextIndex] + (k * i); + } else { + sortingArray[i] -= sortingArray[nextIndex]; + } + + add = !add; + } + + sortingArray[i] = Math.abs(sortingArray[i]); + } + + int i = 0; + while (alphabetArray.size() > 0) { + int pos = sortingArray[i]; + if (pos >= alphabetArray.size()) { + pos %= alphabetArray.size(); + } + ret += alphabetArray.get(pos); + alphabetArray.remove(pos); + i = ++i % sortingArray.length; + } + } + return ret; + } + + public String getSalt() { + return salt_; + } + + public String getAlphabet() { + return alphabet_; + } + + public int getMinHashLength() { + return minHashLength_; + } + + public static String getVersion() { + return "0.1.4"; + } + + private static long[] longListToPrimitiveArray(List longs) { + long[] longArr = new long[longs.size()]; + int i = 0; + for (long l : longs) { + longArr[i++] = l; + } + return longArr; + } + + private static List charArrayToStringList(char[] chars) { + ArrayList list = new ArrayList<>(chars.length); + for (char c : chars) { + list.add(String.valueOf(c)); + } + return list; + } + + private static String join(long[] a, String delimiter) { + ArrayList strList = new ArrayList<>(a.length); + for (long l : a) { + strList.add(String.valueOf(l)); + } + return join(strList, delimiter); + } + + private static String join(Collection s, String delimiter) { + Iterator iter = s.iterator(); + if (iter.hasNext()) { + StringBuilder builder = new StringBuilder(s.size()); + builder.append(iter.next()); + while (iter.hasNext()) { + builder.append(delimiter); + builder.append(iter.next()); + } + return builder.toString(); + } + return ""; + } + + public static int SBStringHash(String toHash) { + byte[] hashArray = toHash.getBytes(); + int hash = 0; + int shift = 0; + + if (toHash.equals("SafeModeA")) + return -1661750486; + if (toHash.equals("SafeModeB")) + return -1661751254; + + if (toHash.equals("INVIS-D")) + return -1661751254; + + if (toHash.equals("SafeMode")) + return -1661750486; + + + if ((hashArray.length != 8 && hashArray.length != 7) || hashArray[3] != 45) { + hash = 0; + shift = 0; + for (int i = 0; i < hashArray.length; i++) { + if (i == 0) + shift = 0; + else + shift = shift + 5; + int toShift = hashArray[i] - 0x20; + hash ^= toShift << shift; + + if (shift > 24) { + + + int newShift = 0x20 - shift; + hash ^= toShift >> newShift; + + if (shift >= 27) { + shift = shift - 0x20; + } + } + } + return hash; + } else { + int ecx = 0; + if (hashArray.length == 8) { + ecx = hashArray[7]; + } + int eax = hashArray[4]; + int esi = ecx * 0x8; + eax = eax ^ esi; + ecx = ecx ^ 0x5A0; + esi = hashArray[5]; + eax = eax << 4; + eax = eax ^ esi; + esi = hashArray[6]; + eax = eax << 4; + eax = eax ^ esi; + esi = hashArray[2]; + eax = eax << 5; + eax = eax ^ esi; + esi = hashArray[1]; + int edx = hashArray[0]; + eax = eax << 5; + eax = eax ^ esi; + ecx = ecx / 2; + ecx = ecx / 2; + eax = eax << 5; + ecx = ecx ^ edx; + eax = eax ^ ecx; + return eax; + } + } +} \ No newline at end of file diff --git a/src/engine/util/MapLoader.java b/src/engine/util/MapLoader.java new file mode 100644 index 00000000..b2ac6849 --- /dev/null +++ b/src/engine/util/MapLoader.java @@ -0,0 +1,104 @@ +/* + * Copyright MagicBane 2013 + */ + +package engine.util; + +import engine.Enum.RealmType; +import engine.server.MBServerStatics; +import engine.server.world.WorldServer; +import org.pmw.tinylog.Logger; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +public enum MapLoader { + + MAPLOADER; + + public static int[][] loadMap() { + + BufferedImage image; + int[][] realmMap; + long timeToLoad = System.currentTimeMillis(); + long bytesRead = 0; + long realmsWritten = 0; + + Integer realmUUID = null; + + // Load image from disk + + try { + image = ImageIO.read(new File(MBServerStatics.DEFAULT_DATA_DIR + "realmmap.png")); + + // Array size determined by image size + MBServerStatics.SPATIAL_HASH_BUCKETSX = image.getWidth(); + MBServerStatics.SPATIAL_HASH_BUCKETSY = image.getHeight(); + realmMap = new int[MBServerStatics.SPATIAL_HASH_BUCKETSX][MBServerStatics.SPATIAL_HASH_BUCKETSY]; + } catch (IOException e) { + Logger.error( "Error loading realm map: " + e.toString()); + return null; + } + + // Flip image on the y axis + + image = flipImage(image); + + // Initialize color lookup table + + for (RealmType realm : RealmType.values()) { + realm.addToColorMap(); + } + + // Load spatial imageMap with color data from file + + for (int i = 0; i < MBServerStatics.SPATIAL_HASH_BUCKETSY; i++) { + for (int j = 0; j < MBServerStatics.SPATIAL_HASH_BUCKETSX; j++) { + try { + int rgb = image.getRGB(j, i); + realmUUID = RealmType.getRealmIDByRGB(rgb); + + if (realmUUID == null) { + Logger.error("Corrupted png: unknown color " + rgb); + WorldServer.shutdown(); + } + + realmMap[j][i] = realmUUID.intValue(); + bytesRead++; + + if (realmUUID.intValue() != 0) + realmsWritten++; + + }catch (Exception e){ + // Logger.error("REALMEDIT ERROR", e.getMessage()); + continue; + } + + + } + } + timeToLoad = System.currentTimeMillis() - timeToLoad; + + Logger.info( bytesRead + " pixels processed in " + timeToLoad / 1000 + " seconds"); + Logger.info("Realm pixels written : " + realmsWritten); + image = null; + return realmMap; + } + + public static BufferedImage flipImage(BufferedImage img) { + + int w = img.getWidth(); + int h = img.getHeight(); + + BufferedImage dimg = new BufferedImage(w, h, img.getColorModel() + .getTransparency()); + + Graphics2D g = dimg.createGraphics(); + g.drawImage(img, 0, 0, w, h, 0, h, w, 0, null); + g.dispose(); + return dimg; + } +} diff --git a/src/engine/util/MiscUtils.java b/src/engine/util/MiscUtils.java new file mode 100644 index 00000000..550d21f5 --- /dev/null +++ b/src/engine/util/MiscUtils.java @@ -0,0 +1,94 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.util; + +import engine.server.MBServerStatics; + +import java.util.regex.Pattern; + + +public class MiscUtils { + + // no need to recompile these each call, put them in object scope and + // compile just once. + private static final Pattern lastNameRegex = Pattern + .compile("^[A-Za-z][-'A-Za-z\\x20]*$"); + private static final Pattern firstNameRegex = Pattern + .compile("^[A-Za-z]+$"); + + public static boolean checkIfFirstNameInvalid(String firstName) { + if ((firstName == null) || (firstName.length() == 0) + || (firstName.length() > MBServerStatics.MAX_NAME_LENGTH) + || (firstName.length() < MBServerStatics.MIN_NAME_LENGTH)) { + return true; + } + return (!firstNameRegex.matcher(firstName).matches()); + } + + public static boolean checkIfLastNameInvalid(String lastName) { + if ((lastName != null) && (lastName.length() != 0)) { + // make sure it's less than max length + return lastName.length() > MBServerStatics.MAX_NAME_LENGTH; + // first character: A-Z, a-z + // remaining chars (optional): hyphen, apostrophe, A-Z, a-z, space +// return (!lastNameRegex.matcher(lastName).matches()); + } + // empty last names are fine, return false + return false; + } + + public static String getCallingMethodName() { + StackTraceElement e[] = Thread.currentThread().getStackTrace(); + int numElements = e.length; + + if (numElements < 1) { + return "NoStack"; + } + + if (numElements == 1) { + return e[0].getMethodName(); + } else if (numElements == 2) { + return e[1].getMethodName(); + } else if (numElements == 3) { + return e[2].getMethodName(); + } else { + return e[3].getMethodName(); + } + } + + public static String getCallStackAsString() { + String out = ""; + + StackTraceElement e[] = Thread.currentThread().getStackTrace(); + int numElements = e.length; + + for (int i = (numElements - 1); i > 1; --i) { + + String[] classStack = e[i].getClassName().split("\\."); + String methName = e[i].getMethodName(); + + String className = classStack[classStack.length - 1]; + + if (methName.equals("")) { + methName = className; + } + + out += className + '.' + methName + "()"; + + if (i > 2) { + out += " -> "; + } + + } + + return out; + } + +} diff --git a/src/engine/util/StringUtils.java b/src/engine/util/StringUtils.java new file mode 100644 index 00000000..e8cbbe66 --- /dev/null +++ b/src/engine/util/StringUtils.java @@ -0,0 +1,150 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + + package engine.util; + +import java.net.InetAddress; +import java.util.StringTokenizer; + +public class StringUtils { + + public static String addWS(String s, int totalLen) { + if (s.length() >= totalLen) { + return s; + } + + int diff = totalLen - s.length(); + + String out = s; + + for (int i = 0; i < diff; ++i) { + out += " "; + } + return out; + } + + public static String bannerize(String s, int totalLen) { + if (s.length() >= totalLen) { + return s; + } + + int diff = totalLen - s.length(); + int halfDiff = diff / 2; + + String side = ""; + for (int i = 0; i < halfDiff; ++i) { + side += "*"; + } + + return side + ' ' + s + ' ' + side; + } + + public static String InetAddressToClientString(InetAddress address) { + return address.toString().replaceAll("/", ""); + } + + public static String toHexString(int i) { + return Integer.toHexString(i).toUpperCase(); + } + + public static String toHexString(long l) { + return Long.toHexString(l).toUpperCase(); + } + + // Well done IDA Pro. + + public static int hashString(String toHash) { + byte[] hashArray = toHash.getBytes(); + int hash = 0; + int shift = 0; + if (hashArray.length == 8 ||hashArray.length == 7){ + int ecx = 0; + if (hashArray.length == 8){ + ecx = hashArray[7]; + } + int eax = hashArray[4]; + int esi = ecx * 0x8; + eax ^= esi; + ecx ^= 0x5A0; + esi = hashArray[5]; + eax <<= 4; + eax ^= esi; + esi = hashArray[6]; + eax <<= 4; + eax ^= esi; + esi = hashArray[2]; + eax <<= 5; + eax ^= esi; + esi = hashArray[1]; + int edx = hashArray[0]; + eax <<= 5; + eax ^= esi; + ecx /= 2; + ecx /= 2; + eax <<= 5; + ecx ^= edx; + eax ^= ecx; + return eax; + }else{ + + for (int i = 0; i 24){ + int newShift = 0x20 - shift; + int newShifted = toShift >> newShift; + hash ^= newShifted; + if (shift > 27){ + shift -= 0x20; + } + } + } + return hash; + } + } + + public static String wordWrap(String text,int LineWidth) + { + StringTokenizer st=new StringTokenizer(text); + int SpaceLeft=LineWidth; + int SpaceWidth=80; + String outString = ""; + + while(st.hasMoreTokens()) + { + String word=st.nextToken(); + if((word.length()+SpaceWidth)>SpaceLeft) + { + outString+= '\n' +word+ ' '; + SpaceLeft=LineWidth-word.length(); + } + else + { + outString+=word+ ' '; + SpaceLeft-=(word.length()+SpaceWidth); + } + + } + + return outString; + +} + +public static String truncate(String input, int length) { + if (input != null && input.length() > length) + input = input.substring(0, length); + return input; +} + +} diff --git a/src/engine/util/ThreadUtils.java b/src/engine/util/ThreadUtils.java new file mode 100644 index 00000000..8b802141 --- /dev/null +++ b/src/engine/util/ThreadUtils.java @@ -0,0 +1,43 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.util; + +import org.pmw.tinylog.Logger; + + +public abstract class ThreadUtils { + + private ThreadUtils() { + } + + /** + * Force the current thread to sleep for sec seconds and ms + * milliseconds. + * + * + */ + public static void sleep(int sec, long ms) { + try { + Thread.sleep((1000L * sec) + ms); + } catch (InterruptedException e) { + Logger.error( e.toString()); + } + } + + /** + * Force the current thread to sleep for ms milliseconds. + * + * + */ + public static void sleep(long ms) { + ThreadUtils.sleep(0, ms); + } + +} diff --git a/src/engine/workthreads/DestroyCityThread.java b/src/engine/workthreads/DestroyCityThread.java new file mode 100644 index 00000000..47e3d97f --- /dev/null +++ b/src/engine/workthreads/DestroyCityThread.java @@ -0,0 +1,155 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.workthreads; + +/* + * This thread is spawned to process destruction + * of a player owned city, including subguild + * and database cleanup. + * + * The 'destroyed' city zone persists until the + * next reboot. + */ + +import engine.Enum; +import engine.gameManager.DbManager; +import engine.gameManager.GuildManager; +import engine.gameManager.ZoneManager; +import engine.math.Vector3fImmutable; +import engine.objects.Building; +import engine.objects.City; +import engine.objects.Guild; +import engine.objects.Zone; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; + +public class DestroyCityThread implements Runnable { + + City city; + + public DestroyCityThread(City city) { + + this.city = city; + } + + public void run(){ + + // Member variable declaration + + Zone cityZone; + Zone newParent; + Guild formerGuild; + Vector3fImmutable localCoords; + ArrayList subGuildList; + + // Member variable assignment + + cityZone = city.getParent(); + newParent = cityZone.getParent(); + formerGuild = city.getTOL().getGuild(); + + // Former guild loses it's tree! + + if (DbManager.GuildQueries.SET_GUILD_OWNED_CITY(formerGuild.getObjectUUID(), 0)) { + + //Successful Update of guild + + formerGuild.setGuildState(engine.Enum.GuildState.Errant); + formerGuild.setNation(null); + formerGuild.setCityUUID(0); + GuildManager.updateAllGuildTags(formerGuild); + GuildManager.updateAllGuildBinds(formerGuild, null); + } + + // By losing the tree, the former owners lose all of their subguilds. + + if (formerGuild.getSubGuildList().isEmpty() == false) { + + subGuildList = new ArrayList<>(); + + for (Guild subGuild : formerGuild.getSubGuildList()) { + subGuildList.add(subGuild); + } + + for (Guild subGuild : subGuildList) { + formerGuild.removeSubGuild(subGuild); + } + } + + // Build list of buildings within this parent zone + + for (Building cityBuilding : cityZone.zoneBuildingSet) { + + // Sanity Check in case player deletes the building + // before this thread can get to it + + if (cityBuilding == null) + continue; + + // Do nothing with the banestone. It will be removed elsewhere + + if (cityBuilding.getBlueprint().getBuildingGroup().equals(Enum.BuildingGroup.BANESTONE)) + continue; + + // All buildings are moved to a location relative + // to their new parent zone + + localCoords = ZoneManager.worldToLocal(cityBuilding.getLoc(), newParent); + + DbManager.BuildingQueries.MOVE_BUILDING(cityBuilding.getObjectUUID(), newParent.getObjectUUID(), localCoords.x, localCoords.y, localCoords.z); + + // All buildings are re-parented to a zone one node + // higher in the tree (continent) as we will be + // deleting the city zone very shortly. + + if (cityBuilding.getParentZoneID() != newParent.getParentZoneID()) + cityBuilding.setParentZone(newParent); + + // No longer a tree, no longer any protection contract! + + cityBuilding.setProtectionState(Enum.ProtectionState.NONE); + + // Destroy all remaining city assets + + if ((cityBuilding.getBlueprint().getBuildingGroup() == Enum.BuildingGroup.BARRACK) + || (cityBuilding.getBlueprint().isWallPiece()) + || (cityBuilding.getBlueprint().getBuildingGroup() == Enum.BuildingGroup.SHRINE) + || (cityBuilding.getBlueprint().getBuildingGroup() == Enum.BuildingGroup.TOL) + || (cityBuilding.getBlueprint().getBuildingGroup() == Enum.BuildingGroup.SPIRE) + || (cityBuilding.getBlueprint().getBuildingGroup() == Enum.BuildingGroup.WAREHOUSE)) { + + if (cityBuilding.getRank() != -1) + cityBuilding.setRank(-1); + } + } + + if (city.getRealm() != null) + city.getRealm().removeCity(city.getObjectUUID()); + + // It's now safe to delete the city zone from the database + // which will cause a cascade delete of everything else + + + if (DbManager.ZoneQueries.DELETE_ZONE(cityZone) == false) { + Logger.error("DestroyCityThread", "Database error when deleting city zone: " + cityZone.getObjectUUID()); + return; + } + + // Refresh the city for map requests + + City.lastCityUpdate = System.currentTimeMillis(); + + // Zone and city should vanish upon next reboot + // if the codebase reaches here. + + Logger.info(city.getParent().getName() + " uuid:" + city.getObjectUUID() + "has been destroyed!"); + } +} diff --git a/src/engine/workthreads/DisconnectTrashTask.java b/src/engine/workthreads/DisconnectTrashTask.java new file mode 100644 index 00000000..97a5f012 --- /dev/null +++ b/src/engine/workthreads/DisconnectTrashTask.java @@ -0,0 +1,52 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.workthreads; + +import engine.Enum; +import engine.gameManager.DbManager; +import engine.gameManager.SessionManager; +import engine.objects.PlayerCharacter; +import engine.session.Session; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; +import java.util.TimerTask; + +public class DisconnectTrashTask extends TimerTask { + + private final ArrayList trashList; + + // Pass it a list of characters and it will disconnect them + // 5 seconds in the future. + + public DisconnectTrashTask(ArrayList trashList) + { + this.trashList = new ArrayList<>(trashList); + } + + public void run() { + + Logger.info("Disconnecting actives from pool of: " + trashList.size()); + + Session trashSession; + int accountUID; + + for (PlayerCharacter trashPlayer:trashList) { + trashSession = SessionManager.getSession(trashPlayer); + accountUID = trashPlayer.getAccount().getObjectUUID(); + + if (trashSession != null) + trashSession.getConn().disconnect(); + + // Remove account from cache + + DbManager.removeFromCache(Enum.GameObjectType.Account, accountUID); + } + }; +} diff --git a/src/engine/workthreads/HourlyJobThread.java b/src/engine/workthreads/HourlyJobThread.java new file mode 100644 index 00000000..9ea37d97 --- /dev/null +++ b/src/engine/workthreads/HourlyJobThread.java @@ -0,0 +1,131 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.workthreads; + +import engine.Enum; +import engine.gameManager.DbManager; +import engine.gameManager.SimulationManager; +import engine.gameManager.ZoneManager; +import engine.net.MessageDispatcher; +import engine.objects.*; +import engine.server.world.WorldServer; +import org.pmw.tinylog.Logger; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; + +public class HourlyJobThread implements Runnable { + + private static int hotzoneCount = 0; + + public HourlyJobThread() { + + } + + public void run() { + + // *** REFACTOR: TRY TRY TRY TRY {{{{{{{{{{{ OMG + + Logger.info("Hourly job is now running."); + + try { + + ZoneManager.generateAndSetRandomHotzone(); + Zone hotzone = ZoneManager.getHotZone(); + + if (hotzone == null) { + Logger.error( "Null hotzone returned from mapmanager"); + } else { + Logger.info( "new hotzone: " + hotzone.getName()); + WorldServer.setLastHZChange(System.currentTimeMillis()); + } + + } catch (Exception e) { + Logger.error( e.toString()); + } + + //updateMines. + try { + + // Update mine effective date if this is a midnight window + + if (LocalDateTime.now().getHour() == 0 || LocalDateTime.now().getHour() == 24) + Mine.effectiveMineDate = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0); + + ArrayList mines = Mine.getMines(); + LocalDateTime now = LocalDateTime.now(); + + for (Mine mine : mines) { + try { + + if (mine.getOwningGuild() == null) { + mine.handleStartMineWindow(); + Mine.setLastChange(System.currentTimeMillis()); + continue; + } + + //handle claimed mines + LocalDateTime mineWindow = mine.openDate.withMinute(0).withSecond(0).withNano(0); + + if (mineWindow != null && now.plusMinutes(1).isAfter(mineWindow)) + if (!mine.getIsActive()) { + mine.handleStartMineWindow(); + Mine.setLastChange(System.currentTimeMillis()); + + } + else if (mine.handleEndMineWindow()) + Mine.setLastChange(System.currentTimeMillis()); + } catch (Exception e) { + Logger.error ("mineID: " + mine.getObjectUUID(), e.toString()); + } + } + } catch (Exception e) { + Logger.error( e.toString()); + } + + for (Mine mine : Mine.getMines()) { + + try { + mine.depositMineResources(); + } catch (Exception e) { + Logger.info(e.getMessage() + " for Mine " + mine.getObjectUUID()); + } + } + + + // Update city population values + + ConcurrentHashMap map = DbManager.getMap(Enum.GameObjectType.City); + + if (map != null) { + + for (AbstractGameObject ago : map.values()){ + + City city = (City)ago; + + if (city != null) + if (city.getGuild() != null) { + ArrayList guildList = Guild.GuildRoster(city.getGuild()); + city.setPopulation(guildList.size()); + } + } + City.lastCityUpdate = System.currentTimeMillis(); + } else { + Logger.error("missing city map"); + } + + // Log metrics to console + Logger.info( WorldServer.getUptimeString()); + Logger.info( SimulationManager.getPopulationString()); + Logger.info( MessageDispatcher.getNetstatString()); + Logger.info(PurgeOprhans.recordsDeleted.toString() + "orphaned items deleted"); + } +} diff --git a/src/engine/workthreads/PurgeOprhans.java b/src/engine/workthreads/PurgeOprhans.java new file mode 100644 index 00000000..c75f4d69 --- /dev/null +++ b/src/engine/workthreads/PurgeOprhans.java @@ -0,0 +1,65 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.workthreads; + +import engine.db.archive.DataWarehouse; +import org.pmw.tinylog.Logger; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.concurrent.atomic.LongAdder; + +/* + * This thread runs at bootstrap to ensure cleanup of + * orphaned items (deleted items). God does this mess + * ever need to be refactored and re-use of item uuid's + * implemented. + */ +public class PurgeOprhans implements Runnable { + + public static LongAdder recordsDeleted = new LongAdder(); + + public PurgeOprhans() { + + recordsDeleted.reset(); + + } + + public static void startPurgeThread() { + + Thread purgeOrphans; + purgeOrphans = new Thread(new PurgeOprhans()); + + purgeOrphans.setName("purgeOrphans"); + purgeOrphans.start(); + } + + public void run() { + + // Member variable declaration + + try ( + Connection connection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = connection.prepareStatement("SELECT * from `object` where `type` = 'item' AND `parent` IS NULL", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + ResultSet rs = statement.executeQuery()) { + + while (rs.next()) { + rs.deleteRow(); + recordsDeleted.increment(); + } + + } catch (Exception e) { + Logger.error( e.toString()); + } + + Logger.info("Thread is exiting with " + recordsDeleted.toString() + " items deleted"); + } + +} diff --git a/src/engine/workthreads/TransferCityThread.java b/src/engine/workthreads/TransferCityThread.java new file mode 100644 index 00000000..ab2d049a --- /dev/null +++ b/src/engine/workthreads/TransferCityThread.java @@ -0,0 +1,99 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.workthreads; + +/* + * This thread is spawned to process transfer + * ownership of a player owned city, including + * subguild and database cleanup. + * + */ + +import engine.Enum; +import engine.gameManager.BuildingManager; +import engine.gameManager.DbManager; +import engine.gameManager.GuildManager; +import engine.net.DispatchMessage; +import engine.net.client.msg.CityZoneMsg; +import engine.objects.AbstractCharacter; +import engine.objects.City; +import engine.objects.Guild; +import org.pmw.tinylog.Logger; + +import java.util.ArrayList; + +public class TransferCityThread implements Runnable { + + City city; + AbstractCharacter newOwner; + + public TransferCityThread(City city, AbstractCharacter newOwner) { + + this.city = city; + this.newOwner = newOwner; + } + + public void run(){ + + Guild formerGuild; + ArrayList subGuildList; + + formerGuild = this.city.getTOL().getGuild(); + + // Former guild loses it's tree! + + if (formerGuild != null) + if (DbManager.GuildQueries.SET_GUILD_OWNED_CITY(formerGuild.getObjectUUID(), 0)) { + formerGuild.setGuildState(Enum.GuildState.Errant); + formerGuild.setNation(null); + formerGuild.setCityUUID(0); + GuildManager.updateAllGuildTags(formerGuild); + GuildManager.updateAllGuildBinds(formerGuild, null); + } + + // By losing the tree, the former owners lose all of their subguilds. + + if (formerGuild.getSubGuildList().isEmpty() == false) { + + subGuildList = new ArrayList<>(); + + for (Guild subGuild : formerGuild.getSubGuildList()) { + subGuildList.add(subGuild); + } + + for (Guild subGuild : subGuildList) { + formerGuild.removeSubGuild(subGuild); + } + } + + //Reset TOL to rank 1 + + city.getTOL().setRank(1); + + // Transfer all assets to new owner + + city.claim(newOwner); + + //Set name of City to attacker's guild name + BuildingManager.setUpgradeDateTime(city.getTOL(),null, 0); + city.getTOL().setName(newOwner.getGuild().getName()); + + // Send updated cityZone to players + CityZoneMsg czm = new CityZoneMsg(2, city.getTOL().getLoc().x, city.getTOL().getLoc().y, city.getTOL().getLoc().z, city.getTOL().getName(), city.getTOL().getParentZone(), 0f, 0f); + + DispatchMessage.dispatchMsgToAll(czm); + + // Reset city timer for map update + + City.lastCityUpdate = System.currentTimeMillis(); + + Logger.info("uuid:" + city.getObjectUUID() + "transferred from " + formerGuild.getName() + + " to " + newOwner.getGuild().getName()); + } +} diff --git a/src/engine/workthreads/WarehousePushThread.java b/src/engine/workthreads/WarehousePushThread.java new file mode 100644 index 00000000..39757da5 --- /dev/null +++ b/src/engine/workthreads/WarehousePushThread.java @@ -0,0 +1,450 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.workthreads; + +/* + * This thread pushes cumulative warehouse data to + * a remote database. + * + */ + +import engine.Enum; +import engine.db.archive.*; +import engine.gameManager.ConfigManager; +import org.pmw.tinylog.Logger; + +import java.sql.*; + +public class WarehousePushThread implements Runnable { + + // Used to track last push. These are read + // at thread startup and written back out + // when we're done + + public static int charIndex, charDelta; + public static int cityIndex, cityDelta; + public static int guildIndex, guildDelta; + public static int realmIndex, realmDelta; + public static int baneIndex, baneDelta; + public static int pvpIndex, pvpDelta; + public static int mineIndex, mineDelta; + + public WarehousePushThread() { + + } + + public void run() { + + int recordCount = 0; + boolean writeSuccess = true; + + if ( ConfigManager.MB_WORLD_WAREHOUSE_PUSH.getValue().equals("false")) { + Logger.info("WAREHOUSEPUSH DISABLED: EARLY EXIT"); + return; + } + + // Cache where we left off from the last push + // for each of the warehouse tables + + if (readWarehouseIndex() == false) + return; + + // Log run to console + + Logger.info( "Pushing records to remote..."); + + // Push records to remote database + + for (Enum.DataRecordType recordType : Enum.DataRecordType.values()) { + + switch (recordType) { + case PVP: + if (pushPvpRecords() == true) { + recordCount = Math.max(0, pvpDelta - pvpIndex); + pvpIndex += recordCount; + } else + writeSuccess = false; + break; + case CHARACTER: + if (pushCharacterRecords() == true) { + recordCount = Math.max(0, charDelta - charIndex); + charIndex += recordCount; + } else + writeSuccess = false; + break; + case REALM: + if (pushRealmRecords() == true) { + recordCount = Math.max(0, realmDelta - realmIndex); + realmIndex += recordCount; + } + else + writeSuccess = false; + break; + case GUILD: + if (pushGuildRecords() == true) { + recordCount = Math.max(0, guildDelta - guildIndex); + guildIndex += recordCount; + } + else + writeSuccess = false; + break; + case BANE: + if (pushBaneRecords() == true) { + recordCount = Math.max(0, baneDelta - baneIndex); + baneIndex += recordCount; + } + else + writeSuccess = false; + break; + case CITY: + if (pushCityRecords() == true) { + recordCount = Math.max(0, cityDelta - cityIndex); + cityIndex += recordCount; + } else + writeSuccess = false; + break; + case MINE: + if (pushMineRecords() == true) { + recordCount = Math.max(0, mineDelta - mineIndex); + mineIndex += recordCount; + } else + writeSuccess = false; + break; + default: + recordCount = 0; + writeSuccess = false; + break; // unhandled type + } + + if (writeSuccess == true) + Logger.info( recordCount + " " + recordType.name() + " records sent to remote"); + else + Logger.info( recordCount + " returning failed success"); + + } // Iterate switch + + // Update indices + + updateWarehouseIndex(); + + // Update dirty records + + Logger.info( "Pushing updates of dirty warehouse records"); + CharacterRecord.updateDirtyRecords(); + + if (charDelta > 0) + Logger.info( charDelta + " dirty character records were sent"); + ; + BaneRecord.updateDirtyRecords(); + + if (baneDelta > 0) + Logger.info( baneDelta + " dirty bane records were sent"); + + Logger.info( "Process has completed"); + + } + + public static boolean pushMineRecords() { + + try (Connection localConnection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = MineRecord.buildMineQueryStatement(localConnection); + ResultSet rs = statement.executeQuery()) { + + while (rs.next()) { + pushMineRecord(rs); + mineDelta = rs.getInt("event_number"); + } + + return true; + } catch (SQLException e) { + Logger.error( "Error with local DB connection: " + e.toString()); + e.printStackTrace(); + return false; + } + } + + public static boolean pushCharacterRecords() { + + try (Connection localConnection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = CharacterRecord.buildCharacterQueryStatement(localConnection); + ResultSet rs = statement.executeQuery()) { + + while (rs.next()) { + pushCharacterRecord(rs); + charDelta = rs.getInt("event_number"); + } + + return true; + } catch (SQLException e) { + Logger.error( "Error with local DB connection: " + e.toString()); + e.printStackTrace(); + return false; + } + } + + private static boolean pushGuildRecords() { + + try (Connection localConnection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = GuildRecord.buildGuildQueryStatement(localConnection); + ResultSet rs = statement.executeQuery()) { + + while (rs.next()) { + pushGuildRecord(rs); + guildDelta = rs.getInt("event_number"); + } + + return true; + } catch (SQLException e) { + Logger.error("Error with local DB connection: " + e.toString()); + e.printStackTrace(); + return false; + } + } + + private static boolean pushMineRecord(ResultSet rs) { + + try (Connection remoteConnection = DataWarehouse.remoteConnectionPool.getConnection(); + PreparedStatement statement = MineRecord.buildMinePushStatement(remoteConnection, rs)) { + + statement.execute(); + return true; + + } catch (SQLException e) { + Logger.error( e.toString()); + return false; + } + } + + private static boolean pushGuildRecord(ResultSet rs) { + + try (Connection remoteConnection = DataWarehouse.remoteConnectionPool.getConnection(); + PreparedStatement statement = GuildRecord.buildGuildPushStatement(remoteConnection, rs)) { + + statement.execute(); + return true; + + } catch (SQLException e) { + Logger.error(e.toString()); + return false; + } + } + + private static boolean pushBaneRecords() { + + try (Connection localConnection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = BaneRecord.buildBaneQueryStatement(localConnection); + ResultSet rs = statement.executeQuery()) { + + while (rs.next()) { + pushBaneRecord(rs); + baneDelta = rs.getInt("event_number"); + } + + return true; + } catch (SQLException e) { + Logger.error("Error with local DB connection: " + e.toString()); + e.printStackTrace(); + return false; + } + } + + private static boolean pushBaneRecord(ResultSet rs) { + + try (Connection remoteConnection = DataWarehouse.remoteConnectionPool.getConnection(); + PreparedStatement statement = BaneRecord.buildBanePushStatement(remoteConnection, rs)) { + + statement.execute(); + return true; + + } catch (SQLException e) { + Logger.error(e.toString()); + return false; + } + } + + private static boolean pushCityRecords() { + + try (Connection localConnection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = CityRecord.buildCityQueryStatement(localConnection); + ResultSet rs = statement.executeQuery()) { + + while (rs.next()) { + pushCityRecord(rs); + cityDelta = rs.getInt("event_number"); + } + + return true; + } catch (SQLException e) { + Logger.error( "Error with local DB connection: " + e.toString()); + e.printStackTrace(); + return false; + } + } + + private static boolean pushPvpRecords() { + + try (Connection localConnection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = PvpRecord.buildPvpQueryStatement(localConnection); + ResultSet rs = statement.executeQuery()) { + + while (rs.next()) { + + if (pushPvpRecord(rs) == true) + pvpDelta = rs.getInt("event_number"); + } + + return true; + } catch (SQLException e) { + Logger.error("Error with local DB connection: " + e.toString()); + return false; + } + } + + private static boolean pushPvpRecord(ResultSet rs) { + + try (Connection remoteConnection = DataWarehouse.remoteConnectionPool.getConnection(); + PreparedStatement statement = PvpRecord.buildPvpPushStatement(remoteConnection, rs)) { + + statement.execute(); + return true; + } catch (SQLException e) { + Logger.error(e.toString()); + return false; + } + + } + + private static boolean pushRealmRecords() { + + try (Connection localConnection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = RealmRecord.buildRealmQueryStatement(localConnection); + ResultSet rs = statement.executeQuery()) { + + while (rs.next()) { + + if (pushRealmRecord(rs) == true) + realmDelta = rs.getInt("event_number"); + } + + return true; + } catch (SQLException e) { + Logger.error( "Error with local DB connection: " + e.toString()); + e.printStackTrace(); + return false; + } + } + + private static boolean pushRealmRecord(ResultSet rs) { + + try (Connection remoteConnection = DataWarehouse.remoteConnectionPool.getConnection(); + PreparedStatement statement = RealmRecord.buildRealmPushStatement(remoteConnection, rs)) { + statement.execute(); + return true; + + } catch (SQLException e) { + Logger.error( e.toString()); + return false; + } + + } + + private static boolean pushCharacterRecord(ResultSet rs) { + + try (Connection remoteConnection = DataWarehouse.remoteConnectionPool.getConnection(); + PreparedStatement statement = CharacterRecord.buildCharacterPushStatement(remoteConnection, rs)) { + + statement.execute(); + return true; + + } catch (SQLException e) { + Logger.error(e.toString()); + return false; + } + + } + + private static boolean pushCityRecord(ResultSet rs) { + + try (Connection remoteConnection = DataWarehouse.remoteConnectionPool.getConnection(); + PreparedStatement statement = CityRecord.buildCityPushStatement(remoteConnection, rs)) { + + statement.execute(); + return true; + + } catch (SQLException e) { + Logger.error( e.toString()); + return false; + } + } + + private static boolean readWarehouseIndex() { + + // Member variable declaration + + String queryString; + + queryString = "SELECT * FROM `warehouse_index`"; + + try (Connection localConnection = DataWarehouse.connectionPool.getConnection(); + CallableStatement statement = localConnection.prepareCall(queryString); + ResultSet rs = statement.executeQuery()) { + + while (rs.next()) { + charIndex = rs.getInt("charIndex"); + cityIndex = rs.getInt("cityIndex"); + guildIndex = rs.getInt("guildIndex"); + realmIndex = rs.getInt("realmIndex"); + baneIndex = rs.getInt("baneIndex"); + pvpIndex = rs.getInt("pvpIndex"); + mineIndex = rs.getInt("mineIndex"); + } + + return true; + + } catch (SQLException e) { + Logger.error( "Error reading warehouse index" + e.toString()); + e.printStackTrace(); + return false; + } + } + + private static boolean updateWarehouseIndex() { + + try (Connection connection = DataWarehouse.connectionPool.getConnection(); + PreparedStatement statement = WarehousePushThread.buildIndexUpdateStatement(connection)) { + + statement.execute(); + return true; + + } catch (SQLException e) { + Logger.error( e.toString()); + return false; + } + } + + private static PreparedStatement buildIndexUpdateStatement(Connection connection) throws SQLException { + + PreparedStatement outStatement = null; + String queryString = "UPDATE `warehouse_index` SET `charIndex` = ?, `cityIndex` = ?, `guildIndex` = ?, `realmIndex` = ?, `baneIndex` = ?, `pvpIndex` = ?, `mineIndex` = ?"; + outStatement = connection.prepareStatement(queryString); + + // Bind record data + + outStatement.setInt(1, charIndex); + outStatement.setInt(2, cityIndex); + outStatement.setInt(3, guildIndex); + outStatement.setInt(4, realmIndex); + outStatement.setInt(5, baneIndex); + outStatement.setInt(6, pvpIndex); + outStatement.setInt(7, mineIndex); + return outStatement; + } +} +