Browse Source

Added health display for mobs during combat.

During combat, players will now see a healthbar appear over the head of
hostile mobs when they are damaged. This healthbar will have two display
options - HEARTS and BAR - which can be changed via the /mobhealth
command.

New Permissions:
mcmmo.mobhealthdisplay - Allows viewing of mob health display
mcmmo.commands.mobhealth - Allows access to the /mobhealth command

New Config Options (config.yml):
Mob_Healthbar.Display_Type - the default health display type
Mob_Healthbar.Display_Time - the amount of time to show health display
GJ 12 năm trước cách đây
mục cha
commit
da29185b7d

+ 1 - 0
Changelog.txt

@@ -8,6 +8,7 @@ Key:
   - Removal
   
 Version 1.4.06-dev
+ + Added health display for mobs during combat.
  + Added new API method to McMMOPlayerLevelUpEvent to set levels gained
  + Added new permission node for /ptp; mcmmo.commands.ptp.send (enabled by default)
  = Fixed displaying partial names when trying to use /ptp

+ 39 - 0
src/main/java/com/gmail/nossr50/commands/MobhealthCommand.java

@@ -0,0 +1,39 @@
+package com.gmail.nossr50.commands;
+
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+
+import com.gmail.nossr50.datatypes.MobHealthbarType;
+import com.gmail.nossr50.datatypes.player.PlayerProfile;
+import com.gmail.nossr50.util.commands.CommandUtils;
+import com.gmail.nossr50.util.player.UserManager;
+
+public class MobhealthCommand implements CommandExecutor {
+
+    @Override
+    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+        if (CommandUtils.noConsoleUsage(sender)) {
+            return true;
+        }
+
+        switch (args.length) {
+            case 1:
+                PlayerProfile playerProfile = UserManager.getPlayer(sender.getName()).getProfile();
+
+                try {
+                    MobHealthbarType type = MobHealthbarType.valueOf(args[0].toUpperCase().trim());
+                    playerProfile.setMobHealthbarType(type);
+                    sender.sendMessage("Display type changed to: " + type); //TODO: Localize
+                    return true;
+                }
+                catch (IllegalArgumentException ex) {
+                    sender.sendMessage("Invalid type!"); //TODO: Localize
+                    return true;
+                }
+
+            default:
+                return false;
+        }
+    }
+}

+ 13 - 0
src/main/java/com/gmail/nossr50/config/Config.java

@@ -6,6 +6,7 @@ import org.bukkit.Material;
 import org.bukkit.configuration.ConfigurationSection;
 import org.bukkit.entity.EntityType;
 
+import com.gmail.nossr50.datatypes.MobHealthbarType;
 import com.gmail.nossr50.datatypes.skills.AbilityType;
 import com.gmail.nossr50.datatypes.skills.SkillType;
 import com.gmail.nossr50.util.StringUtils;
@@ -48,6 +49,18 @@ public class Config extends AutoUpdateConfigLoader {
     public boolean getPartyDisplayNames() { return config.getBoolean("Commands.p.Use_Display_Names", true); }
     public boolean getAdminDisplayNames() { return config.getBoolean("Commands.a.Use_Display_Names", true); }
 
+    /* Mob Healthbar */
+    public MobHealthbarType getMobHealthbarDefault() {
+        try {
+            return MobHealthbarType.valueOf(config.getString("Mob_Healthbar.Display_Type", "HEARTS").toUpperCase().trim());
+        }
+        catch (IllegalArgumentException ex) {
+            return MobHealthbarType.HEARTS;
+        }
+    }
+
+    public int getMobHealthbarTime() { return config.getInt("Mob_Healthbar.Display_Time", 3); }
+
     /* Database Purging */
     public int getPurgeInterval() { return config.getInt("Database_Purging.Purge_Interval", -1); }
     public int getOldUsersCutoff() { return config.getInt("Database_Purging.Old_User_Cutoff", 6); }

+ 11 - 0
src/main/java/com/gmail/nossr50/database/DatabaseManager.java

@@ -96,6 +96,7 @@ public final class DatabaseManager {
         write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "huds` ("
                 + "`user_id` int(10) unsigned NOT NULL,"
                 + "`hudtype` varchar(50) NOT NULL DEFAULT 'STANDARD',"
+                + "`mobhealthbar` varchar(50) NOT NULL DEFAULT 'HEARTS',"
                 + "PRIMARY KEY (`user_id`),"
                 + "FOREIGN KEY (`user_id`) REFERENCES `" + tablePrefix + "users` (`id`) "
                 + "ON DELETE CASCADE) ENGINE=MyISAM DEFAULT CHARSET=latin1;");
@@ -153,6 +154,7 @@ public final class DatabaseManager {
         checkDatabaseStructure(DatabaseUpdateType.BLAST_MINING);
         checkDatabaseStructure(DatabaseUpdateType.CASCADE_DELETE);
         checkDatabaseStructure(DatabaseUpdateType.INDEX);
+        checkDatabaseStructure(DatabaseUpdateType.MOB_HEALTHBARS);
     }
 
     /**
@@ -574,6 +576,10 @@ public final class DatabaseManager {
                 }
                 break;
 
+            case MOB_HEALTHBARS:
+                sql = "SELECT * FROM  `" + tablePrefix + "huds` ORDER BY  `" + tablePrefix + "huds`.`mobhealthbar` ASC LIMIT 0 , 30";
+                break;
+
             default:
                 break;
         }
@@ -610,6 +616,11 @@ public final class DatabaseManager {
                     write("ALTER TABLE `"+tablePrefix + "experience` ADD `fishing` int(10) NOT NULL DEFAULT '0' ;");
                     break;
 
+                case MOB_HEALTHBARS:
+                    mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for mob healthbars...");
+                    write("ALTER TABLE `" + tablePrefix + "huds` ADD `mobhealthbar` varchar(50) NOT NULL DEFAULT 'HEARTS' ;");
+                    break;
+
                 default:
                     break;
             }

+ 7 - 0
src/main/java/com/gmail/nossr50/datatypes/MobHealthbarType.java

@@ -0,0 +1,7 @@
+package com.gmail.nossr50.datatypes;
+
+public enum MobHealthbarType {
+    HEARTS,
+    BAR,
+    DISABLED;
+}

+ 2 - 1
src/main/java/com/gmail/nossr50/datatypes/database/DatabaseUpdateType.java

@@ -4,5 +4,6 @@ public enum DatabaseUpdateType {
     FISHING,
     BLAST_MINING,
     CASCADE_DELETE,
-    INDEX;
+    INDEX,
+    MOB_HEALTHBARS;
 }

+ 24 - 0
src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java

@@ -13,6 +13,7 @@ import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.config.Config;
 import com.gmail.nossr50.config.spout.SpoutConfig;
 import com.gmail.nossr50.database.DatabaseManager;
+import com.gmail.nossr50.datatypes.MobHealthbarType;
 import com.gmail.nossr50.datatypes.skills.AbilityType;
 import com.gmail.nossr50.datatypes.skills.SkillType;
 import com.gmail.nossr50.datatypes.spout.huds.HudType;
@@ -27,6 +28,7 @@ public class PlayerProfile {
     // HUD
     private McMMOHud spoutHud;
     private HudType  hudType;
+    private MobHealthbarType mobHealthbarType;
 
     // mySQL Stuff
     private int userId;
@@ -41,6 +43,7 @@ public class PlayerProfile {
 
     public PlayerProfile(String playerName, boolean addNew) {
         this.playerName = playerName;
+        mobHealthbarType = Config.getInstance().getMobHealthbarDefault();
 
         if (mcMMO.spoutEnabled) {
             hudType = SpoutConfig.getInstance().defaultHudType;
@@ -98,6 +101,8 @@ public class PlayerProfile {
             }
         }
 
+        mobHealthbarType = MobHealthbarType.valueOf(DatabaseManager.read("SELECT mobhealthbar FROM " + tablePrefix + "huds WHERE user_id = " + userId).get(1).get(0));
+
         HashMap<Integer, ArrayList<String>> cooldowns = DatabaseManager.read("SELECT mining, woodcutting, unarmed, herbalism, excavation, swords, axes, blast_mining FROM " + tablePrefix + "cooldowns WHERE user_id = " + userId);
         ArrayList<String> cooldownValues = cooldowns.get(1);
 
@@ -326,6 +331,10 @@ public class PlayerProfile {
                     skillsDATS.put(AbilityType.BLAST_MINING, Integer.valueOf(character[36]));
                 }
 
+                if (character.length > 38) {
+                    mobHealthbarType = MobHealthbarType.valueOf(character[38]);
+                }
+
                 loaded = true;
 
                 in.close();
@@ -348,6 +357,7 @@ public class PlayerProfile {
             String tablePrefix = Config.getInstance().getMySQLTablePrefix();
 
             DatabaseManager.write("UPDATE " + tablePrefix + "huds SET hudtype = '" + hudType.toString() + "' WHERE user_id = " + userId);
+            DatabaseManager.write("UPDATE " + tablePrefix + "huds SET mobhealthbar = '" + mobHealthbarType.toString() + "' WHERE user_id = " + userId);
             DatabaseManager.write("UPDATE " + tablePrefix + "users SET lastlogin = " + ((int) (timestamp / Misc.TIME_CONVERSION_FACTOR)) + " WHERE id = " + userId);
             DatabaseManager.write("UPDATE " + tablePrefix + "cooldowns SET "
                     + " mining = " + skillsDATS.get(AbilityType.SUPER_BREAKER)
@@ -443,6 +453,7 @@ public class PlayerProfile {
                         writer.append(skillsXp.get(SkillType.FISHING)).append(":");
                         writer.append(skillsDATS.get(AbilityType.BLAST_MINING)).append(":");
                         writer.append(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR).append(":");
+                        writer.append(mobHealthbarType.toString()).append(":");
                         writer.append("\r\n");
                     }
                 }
@@ -506,6 +517,7 @@ public class PlayerProfile {
             out.append("0:"); // FishingXp
             out.append("0:"); // Blast Mining
             out.append(String.valueOf(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR)).append(":"); // LastLogin
+            out.append(mobHealthbarType.toString()).append(":"); // Mob Healthbar HUD
 
             // Add more in the same format as the line above
 
@@ -549,6 +561,18 @@ public class PlayerProfile {
         this.hudType = hudType;
     }
 
+    /*
+     * Mob Healthbars
+     */
+
+    public MobHealthbarType getMobHealthbarType() {
+        return mobHealthbarType;
+    }
+
+    public void setMobHealthbarType(MobHealthbarType mobHealthbarType) {
+        this.mobHealthbarType = mobHealthbarType;
+    }
+
     /*
      * Cooldowns
      */

+ 3 - 0
src/main/java/com/gmail/nossr50/mcMMO.java

@@ -91,6 +91,8 @@ public class mcMMO extends JavaPlugin {
     public final static String blockMetadataKey    = "mcMMO: Piston Tracking";
     public final static String furnaceMetadataKey  = "mcMMO: Tracked Furnace";
     public final static String tntMetadataKey      = "mcMMO: Tracked TNT";
+    public final static String customNameKey       = "mcMMO: Custom Name";
+    public final static String customVisibleKey    = "mcMMO: Name Visibility";
 
     public static FixedMetadataValue metadataValue;
 
@@ -355,6 +357,7 @@ public class mcMMO extends JavaPlugin {
         CommandRegistrationManager.registerHardcoreCommand();
         CommandRegistrationManager.registerVampirismCommand();
         CommandRegistrationManager.registerMcnotifyCommand();
+        CommandRegistrationManager.registerMobhealthCommand();
 
         // Spout commands
         CommandRegistrationManager.registerXplockCommand();

+ 24 - 0
src/main/java/com/gmail/nossr50/runnables/MobHealthDisplayUpdaterTask.java

@@ -0,0 +1,24 @@
+package com.gmail.nossr50.runnables;
+
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.scheduler.BukkitRunnable;
+
+import com.gmail.nossr50.mcMMO;
+
+public class MobHealthDisplayUpdaterTask extends BukkitRunnable {
+    private LivingEntity target;
+    private String oldName;
+    private boolean oldNameVisible;
+
+    public MobHealthDisplayUpdaterTask(LivingEntity target) {
+        this.target = target;
+        this.oldName = target.getMetadata(mcMMO.customNameKey).get(0).asString();
+        this.oldNameVisible = target.getMetadata(mcMMO.customVisibleKey).get(0).asBoolean();
+    }
+
+    @Override
+    public void run() {
+        target.setCustomNameVisible(oldNameVisible);
+        target.setCustomName(oldName);
+    }
+}

+ 1 - 0
src/main/java/com/gmail/nossr50/util/Permissions.java

@@ -20,6 +20,7 @@ public final class Permissions {
      */
 
     public static boolean motd(Permissible permissible) { return permissible.hasPermission("mcmmo.motd"); }
+    public static boolean mobHealthDisplay(Permissible permissible) { return permissible.hasPermission("mcmmo.mobhealthdisplay"); }
     public static boolean updateNotifications(Permissible permissible) {return permissible.hasPermission("mcmmo.tools.updatecheck"); }
     public static boolean chimaeraWing(Permissible permissible) { return permissible.hasPermission("mcmmo.item.chimaerawing"); }
 

+ 10 - 0
src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java

@@ -11,6 +11,7 @@ import com.gmail.nossr50.commands.McgodCommand;
 import com.gmail.nossr50.commands.McmmoCommand;
 import com.gmail.nossr50.commands.McnotifyCommand;
 import com.gmail.nossr50.commands.McrefreshCommand;
+import com.gmail.nossr50.commands.MobhealthCommand;
 import com.gmail.nossr50.commands.XprateCommand;
 import com.gmail.nossr50.commands.chat.AdminChatCommand;
 import com.gmail.nossr50.commands.chat.PartyChatCommand;
@@ -366,4 +367,13 @@ public final class CommandRegistrationManager {
         command.setUsage(LocaleLoader.getString("Commands.Usage.0", "mcnotify"));
         command.setExecutor(new McnotifyCommand());
     }
+
+    public static void registerMobhealthCommand() {
+        PluginCommand command = mcMMO.p.getCommand("mobhealth");
+        command.setDescription("Change the style of the mob healthbar"); //TODO: Localize
+        command.setPermission("mcmmo.commands.mobhealth");
+        command.setPermissionMessage(permissionsMessage);
+        command.setUsage(LocaleLoader.getString("Commands.Usage.1", "mobhealth", "<DISABLED | HEARTS | BAR>"));
+        command.setExecutor(new MobhealthCommand());
+    }
 }

+ 96 - 0
src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java

@@ -1,5 +1,6 @@
 package com.gmail.nossr50.util.skills;
 
+import org.bukkit.ChatColor;
 import org.bukkit.Material;
 import org.bukkit.entity.AnimalTamer;
 import org.bukkit.entity.Animals;
@@ -17,15 +18,19 @@ import org.bukkit.event.entity.EntityDamageEvent;
 import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
 import org.bukkit.event.player.PlayerAnimationEvent;
 import org.bukkit.inventory.ItemStack;
+import org.bukkit.metadata.FixedMetadataValue;
 
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.config.Config;
+import com.gmail.nossr50.datatypes.MobHealthbarType;
 import com.gmail.nossr50.datatypes.player.McMMOPlayer;
+import com.gmail.nossr50.datatypes.player.PlayerProfile;
 import com.gmail.nossr50.datatypes.skills.SkillType;
 import com.gmail.nossr50.events.fake.FakeEntityDamageByEntityEvent;
 import com.gmail.nossr50.events.fake.FakeEntityDamageEvent;
 import com.gmail.nossr50.locale.LocaleLoader;
 import com.gmail.nossr50.party.PartyManager;
+import com.gmail.nossr50.runnables.MobHealthDisplayUpdaterTask;
 import com.gmail.nossr50.runnables.skills.AwardCombatXpTask;
 import com.gmail.nossr50.runnables.skills.BleedTimerTask;
 import com.gmail.nossr50.skills.acrobatics.AcrobaticsManager;
@@ -284,6 +289,34 @@ public final class CombatUtils {
                 }
             }
         }
+        else if (attacker instanceof Player) {
+            Player player = (Player) attacker;
+            PlayerProfile profile = UserManager.getPlayer(player).getProfile();
+
+            if (Permissions.mobHealthDisplay(player) && profile.getMobHealthbarType() != MobHealthbarType.DISABLED) {
+                String oldName = target.getCustomName();
+                boolean oldNameVisible = target.isCustomNameVisible();
+                String newName = createHealthDisplay(profile, target, event.getDamage());
+
+                target.setCustomName(newName);
+                target.setCustomNameVisible(true);
+
+                int displayTime = Config.getInstance().getMobHealthbarTime();
+
+                if (displayTime != -1) {
+                    if (oldName == null) {
+                        oldName = "";
+                    }
+
+                    if (!ChatColor.stripColor(oldName).equalsIgnoreCase(ChatColor.stripColor(newName))) {
+                        target.setMetadata(mcMMO.customNameKey, new FixedMetadataValue(mcMMO.p, oldName));
+                        target.setMetadata(mcMMO.customVisibleKey, new FixedMetadataValue(mcMMO.p, oldNameVisible));
+                    }
+
+                    new MobHealthDisplayUpdaterTask(target).runTaskLater(mcMMO.p, displayTime * 20); // Clear health display after 3 seconds
+                }
+            }
+        }
     }
 
     /**
@@ -583,4 +616,67 @@ public final class CombatUtils {
 
         return process;
     }
+
+    private static String createHealthDisplay(PlayerProfile profile, LivingEntity entity, int damage) {
+        int maxHealth = entity.getMaxHealth();
+        int currentHealth = Math.max(entity.getHealth() - damage, 0);
+        double healthPercentage = (currentHealth / (double) maxHealth) * 100.0D;
+
+        int fullDisplay = 0;
+        ChatColor color = ChatColor.BLACK;
+        String symbol = "";
+
+        switch (profile.getMobHealthbarType()) {
+            case HEARTS:
+                fullDisplay = Math.min(maxHealth / 2, 10);
+                color = ChatColor.DARK_RED;
+                symbol = "❤";
+                break;
+
+            case BAR:
+                fullDisplay = 10;
+
+                if (healthPercentage >= 85) {
+                    color = ChatColor.DARK_GREEN;
+                }
+                else if (healthPercentage >= 70) {
+                    color = ChatColor.GREEN;
+                }
+                else if (healthPercentage >= 55) {
+                    color = ChatColor.GOLD;
+                }
+                else if (healthPercentage >= 40) {
+                    color = ChatColor.YELLOW;
+                }
+                else if (healthPercentage >= 25) {
+                    color = ChatColor.RED;
+                }
+                else if (healthPercentage >= 0) {
+                    color = ChatColor.DARK_RED;
+                }
+
+                symbol = "■";
+                break;
+
+            default:
+                return null;
+        }
+
+        int coloredDisplay = (int) (fullDisplay * (healthPercentage / 100.0D));
+        int grayDisplay = fullDisplay - coloredDisplay;
+
+        String healthbar = color + "";
+
+        for (int i = 0; i < coloredDisplay; i++) {
+            healthbar += symbol;
+        }
+
+        healthbar += ChatColor.GRAY;
+
+        for (int i = 0; i < grayDisplay; i++) {
+            healthbar += symbol;
+        }
+
+        return healthbar;
+    }
 }

+ 6 - 0
src/main/resources/config.yml

@@ -27,6 +27,12 @@ General:
     # Should mcMMO over-write configs to update, or make new ones ending in .new?
     Config_Update_Overwrite: true
 
+Mob_Healthbar
+    # Default display for mob health bars - HEARTS, BAR, or DISABLED
+    Display_Type: HEARTS
+    # Amount of time (in seconds) to display. To display permanently, set to -1
+    Display_Time: 3
+
 Database_Purging:
     # Amount of time (in hours) to wait between database purging
     # To only run at server start, set to 0

+ 10 - 0
src/main/resources/plugin.yml

@@ -104,6 +104,9 @@ commands:
     mcnotify:
         aliases: [notify]
         description: Toggle mcMMO abilities chat display notifications on/off
+    mobhealth:
+        aliases: [mcmobhealth]
+        description: Change the style of the mob healthbar
 permissions:
     mcmmo.*:
         default: false
@@ -659,6 +662,7 @@ permissions:
             mcmmo.commands.mcstats: true
             mcmmo.commands.mctop.all: true
             mcmmo.commands.mining: true
+            mcmmo.commands.mobhealth: true
             mcmmo.commands.party.all: true
             mcmmo.commands.ptp: true
             mcmmo.commands.ptp.accept: true
@@ -875,6 +879,9 @@ permissions:
     mcmmo.commands.mmoupdate:
         default: false
         description: Allows access to the mmoupdate command
+    mcmmo.commands.mobhealth:
+         default: true
+         description: Allows access to the mobhealth command
     mcmmo.commands.party.*:
         default: false
         description: Implies access to all mcmmo.commands.party permissions.
@@ -1179,6 +1186,7 @@ permissions:
         children:
             mcmmo.chat.partychat: true
             mcmmo.commands.defaults: true
+            mcmmo.mobhealthdisplay: true
             mcmmo.motd: true 
             mcmmo.skills.all: true
     mcmmo.defaultsop:
@@ -1200,6 +1208,8 @@ permissions:
             mcmmo.item.chimaerawing: true
     mcmmo.item.chimaerawing:
         description: Allows use of Chimaera Wing item
+    mcmmo.mobhealthdisplay:
+         description: Allows viewing of mob health display during combat
     mcmmo.motd:
         description: Allows access to the motd
     mcmmo.party.*: