From 59cf22287a5a8dbd853b13fbd98aed32ce7bd540 Mon Sep 17 00:00:00 2001 From: BuildTools Date: Sun, 28 Jul 2019 07:17:06 -0400 Subject: [PATCH] Initial Commit --- GauntletConfig.java | 88 +++++++++ GauntletOverlay.java | 228 +++++++++++++++++++++ GauntletPlugin.java | 459 +++++++++++++++++++++++++++++++++++++++++++ README.md | 48 +++++ 4 files changed, 823 insertions(+) create mode 100644 GauntletConfig.java create mode 100644 GauntletOverlay.java create mode 100644 GauntletPlugin.java create mode 100644 README.md diff --git a/GauntletConfig.java b/GauntletConfig.java new file mode 100644 index 0000000..7a27801 --- /dev/null +++ b/GauntletConfig.java @@ -0,0 +1,88 @@ +/* + * THIS SOFTWARE WRITTEN BY A KEYBOARD-WIELDING MONKEY BOI + * No rights reserved. Use, redistribute, and modify at your own discretion, + * and in accordance with Yagex and RuneLite guidelines. + * However, aforementioned monkey would prefer if you don't sell this plugin for profit. + * Good luck on your raids! + */ + +package net.runelite.client.plugins.gauntlet; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("Theatre") + +public interface GauntletConfig extends Config { + + @ConfigItem( + position = 0, + keyName = "highlightResources", + name = "Highlight Resources", + description = "Highlights all the resources in each room with a color." + ) + default boolean highlightResources() { + return true; + } + + @ConfigItem( + position = 1, + keyName = "highlightResourcesIcons", + name = "Highlight Icons", + description = "Highlights all the icons in each room with an icon." + ) + default boolean highlightResourcesIcons() { + return true; + } + + @ConfigItem( + position = 2, + keyName = "countPlayerAttacks", + name = "Count Player Attacks", + description = "Count the player attacks until the boss switches their prayer." + ) + default boolean countPlayerAttacks() { + return true; + } + + @ConfigItem( + position = 3, + keyName = "countBossAttacks", + name = "Count Boss Attacks", + description = "Count the attacks until the boss switches their style." + ) + default boolean countBossAttacks() { + return true; + } + + @ConfigItem( + position = 4, + keyName = "showAttackStyle", + name = "Show Attack Styles", + description = "Mark orb with an individual attack style." + ) + default boolean showAttackStyle() { + return true; + } + + @ConfigItem( + position = 5, + keyName = "overlayBoss", + name = "Overlay the Boss", + description = "Overlay the boss with the color attack style." + ) + default boolean overlayBoss() { + return false; + } + + @ConfigItem( + position = 6, + keyName = "alertPrayerDrain", + name = "Play Tune on Prayer Attack", + description = "Plays a sound whenever the boss is about to shut down your prayer." + ) + default boolean alertPrayerDrain() { + return true; + } +} diff --git a/GauntletOverlay.java b/GauntletOverlay.java new file mode 100644 index 0000000..04e18ab --- /dev/null +++ b/GauntletOverlay.java @@ -0,0 +1,228 @@ +/* + * THIS SOFTWARE WRITTEN BY A KEYBOARD-WIELDING MONKEY BOI + * No rights reserved. Use, redistribute, and modify at your own discretion, + * and in accordance with Yagex and RuneLite guidelines. + * However, aforementioned monkey would prefer if you don't sell this plugin for profit. + * Good luck on your raids! + */ + +package net.runelite.client.plugins.gauntlet; + +import net.runelite.api.Client; +import net.runelite.api.NPC; +import net.runelite.api.Perspective; +import net.runelite.api.Point; +import net.runelite.api.Projectile; +import net.runelite.api.Tile; +import net.runelite.api.TileObject; +import net.runelite.api.coords.LocalPoint; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayPriority; +import net.runelite.client.ui.overlay.OverlayUtil; + +import javax.inject.Inject; +import java.awt.*; +import java.awt.image.BufferedImage; + +public class GauntletOverlay extends Overlay { + + private final Client client; + private final GauntletPlugin plugin; + private final GauntletConfig config; + + private static final int MAX_DISTANCE = 2350; + + @Inject + private GauntletOverlay(Client client, GauntletPlugin plugin, GauntletConfig config) { + this.client = client; + this.plugin = plugin; + this.config = config; + + setPosition(OverlayPosition.DYNAMIC); + setPriority(OverlayPriority.HIGH); + setLayer(OverlayLayer.ABOVE_SCENE); + } + + @Override + public Dimension render(Graphics2D graphics) { + if (this.plugin.inBoss()) { + if (config.showAttackStyle()) { + for (Projectile projectile : this.client.getProjectiles()) { + BufferedImage icon; + switch (projectile.getId()) { + case 1707: + case 1708: + icon = plugin.getATTACK_MAGE(); + break; + case 1711: + case 1712: + icon = plugin.getATTACK_RANGE(); + break; + case 1713: + case 1714: + icon = plugin.getATTACK_PRAYER(); + break; + default: + icon = null; + break; + } + + if (icon == null) + continue; + + int x = (int) projectile.getX(); + int y = (int) projectile.getY(); + + LocalPoint point = new LocalPoint(x, y); + Point loc = Perspective.getCanvasImageLocation(client, point, icon, 0); + + if (loc == null) + continue; + + graphics.drawImage(icon, loc.getX(), loc.getY(), null); + } + } + + BufferedImage attackIcon = null; + if (config.countBossAttacks()) { + switch (plugin.getCurrentStyle()) { + case MAGIC: + attackIcon = plugin.getATTACK_MAGE(); + break; + case RANGE: + attackIcon = plugin.getATTACK_RANGE(); + break; + default: + attackIcon = plugin.getATTACK_PRAYER(); + break; + } + } + + for (NPC npc : this.client.getNpcs()) { + String name = npc.getName(); + if (name == null || !npc.getName().matches("(Crystalline|Corrupted) Hunllef")) + continue; + + if (config.overlayBoss()) { + Polygon polygon = npc.getConvexHull(); + + if (polygon != null) { + Color color; + switch (plugin.getCurrentStyle()) { + case MAGIC: + color = Color.CYAN; + break; + case RANGE: + color = Color.GREEN; + break; + default: + color = Color.WHITE; + break; + } + + graphics.draw(polygon); + graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 50)); + graphics.fill(polygon); + } + } + + if (attackIcon != null) { + LocalPoint point = npc.getLocalLocation(); + Point imageLoc = Perspective.getCanvasImageLocation(client, point, attackIcon, npc.getLogicalHeight() / 2); + + if (imageLoc == null) + continue; + + graphics.drawImage(attackIcon, imageLoc.getX(), imageLoc.getY(), null); + + String message = Integer.toString(plugin.getAttacksLeft()); + if (config.countPlayerAttacks()) { + message += " | " + plugin.getPlayerCounter(); + } + + Point textLoc = Perspective.getCanvasTextLocation(client, graphics, point, message, npc.getLogicalHeight() / 2); + + if (textLoc == null) + continue; + + textLoc = new Point(textLoc.getX(), textLoc.getY() + 35); + + Font oldFont = graphics.getFont(); + + graphics.setFont(new Font("Arial", Font.BOLD, 20)); + Point pointShadow = new Point(textLoc.getX() + 1, textLoc.getY() + 1); + + OverlayUtil.renderTextLocation(graphics, pointShadow, message, Color.BLACK); + OverlayUtil.renderTextLocation(graphics, textLoc, message, Color.CYAN); + + graphics.setFont(oldFont); + } + } + } else { + if (config.highlightResources()) { + LocalPoint playerLocation = client.getLocalPlayer().getLocalLocation(); + Point mousePosition = client.getMouseCanvasPosition(); + + for (TileObject object : plugin.getResources().keySet()) { + Tile tile = plugin.getResources().get(object); + if (tile.getPlane() == client.getPlane() + && object.getLocalLocation().distanceTo(playerLocation) < MAX_DISTANCE) { +// Area objectClickbox = object.getClickbox(); // Don't use Convex Hull; you'll lag. + + Polygon polygon = object.getCanvasTilePoly(); + if (polygon != null) { + Color color = SystemColor.YELLOW; + BufferedImage icon; + + if (config.highlightResourcesIcons()) + switch (object.getId()) { + case 36064: // Crystal Deposit + case 35967: + icon = plugin.getCRYSTAL_DEPOSIT(); + break; + case 36066: // Phren Roots + case 35969: + icon = plugin.getPHREN_ROOTS(); + break; + case 36068: // Fishing Spot + case 35971: + icon = plugin.getFISHING_SPOT(); + break; + case 36070: // Grym Root + case 35973: + icon = plugin.getGRYM_ROOT(); + break; + case 36072: // Linum Tirinum + case 35975: + icon = plugin.getLINUM_TIRINUM(); + break; + default: + icon = null; + break; + } + else + icon = null; + + graphics.setColor(color); + + graphics.draw(polygon); + graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 50)); + graphics.fill(polygon); + + if (icon != null) { + Rectangle bounds = polygon.getBounds(); + int startX = (int) bounds.getCenterX() - (icon.getWidth() / 2); + int startY = (int) bounds.getCenterY() - (icon.getHeight() / 2); + graphics.drawImage(icon, startX, startY, null); + } + } + } + } + } + } + + return null; + } +} diff --git a/GauntletPlugin.java b/GauntletPlugin.java new file mode 100644 index 0000000..848b6ba --- /dev/null +++ b/GauntletPlugin.java @@ -0,0 +1,459 @@ +/* + * THIS SOFTWARE WRITTEN BY A KEYBOARD-WIELDING MONKEY BOI + * No rights reserved. Use, redistribute, and modify at your own discretion, + * and in accordance with Yagex and RuneLite guidelines. + * However, aforementioned monkey would prefer if you don't sell this plugin for profit. + * Good luck on your raids! + */ + +package net.runelite.client.plugins.gauntlet; + +import com.google.inject.Provides; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import net.runelite.api.Actor; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.HeadIcon; +import net.runelite.api.NPC; +import net.runelite.api.NPCComposition; +import net.runelite.api.Player; +import net.runelite.api.Projectile; +import net.runelite.api.SoundEffectID; +import net.runelite.api.SoundEffectVolume; +import net.runelite.api.Tile; +import net.runelite.api.TileObject; +import net.runelite.api.events.AnimationChanged; +import net.runelite.api.events.DecorativeObjectChanged; +import net.runelite.api.events.DecorativeObjectDespawned; +import net.runelite.api.events.DecorativeObjectSpawned; +import net.runelite.api.events.GameObjectChanged; +import net.runelite.api.events.GameObjectDespawned; +import net.runelite.api.events.GameObjectSpawned; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.GroundObjectChanged; +import net.runelite.api.events.GroundObjectDespawned; +import net.runelite.api.events.GroundObjectSpawned; +import net.runelite.api.events.NpcDespawned; +import net.runelite.api.events.NpcSpawned; +import net.runelite.api.events.WallObjectChanged; +import net.runelite.api.events.WallObjectDespawned; +import net.runelite.api.events.WallObjectSpawned; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.util.ImageUtil; + +import javax.inject.Inject; +import java.awt.image.BufferedImage; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +@PluginDescriptor( + name = "Gauntlet", + description = "All-in-one plugin for the Gauntlet.", + tags = {"Gauntlet"}, + enabledByDefault = false +) + +public class GauntletPlugin extends Plugin { + + @Getter + private BufferedImage CRYSTAL_DEPOSIT; + + @Getter + private BufferedImage PHREN_ROOTS; + + @Getter + private BufferedImage FISHING_SPOT; + + @Getter + private BufferedImage GRYM_ROOT; + + @Getter + private BufferedImage LINUM_TIRINUM; + + @Getter + private BufferedImage ATTACK_RANGE; + + @Getter + private BufferedImage ATTACK_MAGE; + + @Getter + private BufferedImage ATTACK_PRAYER; + + @Inject + private Client client; + + @Getter(AccessLevel.PUBLIC) + @Inject + private OverlayManager overlayManager; + + @Inject + private GauntletOverlay overlay; + + @Inject + private GauntletConfig config; + + @Getter + private final Map resources = new HashMap<>(); + + @Getter + private Set projectiles = new HashSet<>(); + + @Provides + GauntletConfig getConfig(ConfigManager configManager) { + return configManager.getConfig(GauntletConfig.class); + } + + @Getter + @Setter + private int attacksLeft; + + @Getter + @Setter + private int playerCounter; + + @Getter + @Setter + private Style currentStyle; + + public static enum Style { + MAGIC, RANGE, UNKNOWN; + } + + public static enum Attack { + MAGIC, RANGE, PRAYER, LIGHTNING; + } + + @Override + protected void startUp() { + CRYSTAL_DEPOSIT = ImageUtil.getResourceStreamFromClass(getClass(), "/skill_icons/mining.png"); + PHREN_ROOTS = ImageUtil.getResourceStreamFromClass(getClass(), "/skill_icons/woodcutting.png"); + FISHING_SPOT = ImageUtil.getResourceStreamFromClass(getClass(), "/skill_icons/fishing.png"); + GRYM_ROOT = ImageUtil.getResourceStreamFromClass(getClass(), "/skill_icons/herblore.png"); + LINUM_TIRINUM = ImageUtil.getResourceStreamFromClass(getClass(), "/skill_icons/farming.png"); + + ATTACK_MAGE = ImageUtil.getResourceStreamFromClass(getClass(), "/skill_icons/magic.png"); + ATTACK_RANGE = ImageUtil.getResourceStreamFromClass(getClass(), "/skill_icons/ranged.png"); + ATTACK_PRAYER = ImageUtil.getResourceStreamFromClass(getClass(), "/skill_icons/prayer.png"); + + playerCounter = 6; + attacksLeft = 0; + currentStyle = Style.UNKNOWN; + projectiles.clear(); + + overlayManager.add(overlay); + } + + @Override + protected void shutDown() { + playerCounter = 6; + attacksLeft = 0; + currentStyle = Style.UNKNOWN; + projectiles.clear(); + + overlayManager.remove(overlay); + + CRYSTAL_DEPOSIT = null; + PHREN_ROOTS = null; + FISHING_SPOT = null; + GRYM_ROOT = null; + LINUM_TIRINUM = null; + + ATTACK_MAGE = null; + ATTACK_RANGE = null; + ATTACK_PRAYER = null; + } + + @Subscribe + public void onNpcSpawned(NpcSpawned event) { + NPC npc = event.getNpc(); + if (npc == null || npc.getName() == null || !npc.getName().matches("(Crystalline|Corrupted) Hunllef")) + return; + + playerCounter = 6; + attacksLeft = 0; + currentStyle = Style.UNKNOWN; + projectiles.clear(); + } + + @Subscribe + public void onNpcDespawned(NpcDespawned event) { + NPC npc = event.getNpc(); + if (npc == null || npc.getName() == null || !npc.getName().matches("(Crystalline|Corrupted) Hunllef")) + return; + + playerCounter = 6; + attacksLeft = 0; + currentStyle = Style.UNKNOWN; + projectiles.clear(); + } + + public void doAttack(Attack style) { + if (style == Attack.PRAYER) { + if (config.alertPrayerDrain()) { + client.playSoundEffect(SoundEffectID.MAGIC_SPLASH_BOING, SoundEffectVolume.MEDIUM_HIGH); + } + style = Attack.MAGIC; + } + + if (style == Attack.LIGHTNING) { + attacksLeft--; + } else if (style == Attack.RANGE) { + if (currentStyle != Style.RANGE) { + currentStyle = Style.RANGE; + attacksLeft = 3; + } else { + attacksLeft--; + } + } else if (style == Attack.MAGIC) { + if (currentStyle != Style.MAGIC) { + currentStyle = Style.MAGIC; + attacksLeft = 3; + } else { + attacksLeft--; + } + } + + if (attacksLeft <= 0) { + Style newStyle; + + switch (currentStyle) { + case MAGIC: + attacksLeft = 4; + newStyle = Style.RANGE; + break; + case RANGE: + attacksLeft = 4; + newStyle = Style.MAGIC; + break; + default: + attacksLeft = 0; + newStyle = Style.UNKNOWN; + break; + } + + currentStyle = newStyle; + } + } + + @Subscribe + public void onAnimationChanged(AnimationChanged event) { + Actor actor = event.getActor(); + + if (actor instanceof Player && this.inBoss()) { + Player p = (Player) actor; + if (p.getName().equals(client.getLocalPlayer().getName())) { + int id = p.getAnimation(); + if (id != -1) { + int[] all_styles = new int[]{395, 401, 400, 401, 386, 390, 422, 423, 401, 428, 440, 426, 1167}; + int[] wrong_style = new int[]{}; + + for (NPC npc : this.client.getNpcs()) { + if (npc != null && npc.getName() != null && npc.getName().matches("(Crystalline|Corrupted) Hunllef")) { + NPCComposition comp = npc.getComposition(); + if (comp != null) { + HeadIcon prayer = comp.getOverheadIcon(); + if (prayer != null) { + switch (prayer) { + case MELEE: + wrong_style = new int[]{ + 395, // Axe Slash + 401, // Axe Crush + + 400, // Pick Crush + 401, // Pick Stab + + 386, // Harpoon Stab + 390, // Harpoon Slash + + 422, // Unarmed Punch + 423, // Unarmed Kick + + 401, // Crystal Scepter + 428, // Crystal Halberd Jab & Fend + 440 // Crystal Halberd Swipe + }; + break; + case RANGED: + wrong_style = new int[]{ + 426 // Crystal Bow + }; + break; + case MAGIC: + wrong_style = new int[]{ + 1167 // Crystal Staff + }; + break; + default: + wrong_style = new int[]{}; + break; + } + + break; + } + } + } + } + + outerloop: + for (int action : all_styles) { + if (action == id) { + for (int wrong_action : wrong_style) { + if (action == wrong_action) + break outerloop; + } + + playerCounter--; + if (playerCounter <= 0) { + playerCounter = 6; + } + + break outerloop; + } + } + } + } + } + + if (actor instanceof NPC) { + NPC npc = (NPC) actor; + if (npc != null && npc.getName() != null && npc.getName().matches("(Crystalline|Corrupted) Hunllef")) { + int id = npc.getAnimation(); + if (id == 8418) { + this.doAttack(Attack.LIGHTNING); + } + } + } + } + + @Subscribe + public void onGameTick(GameTick event) { + Set newProjectiles = new HashSet<>(); + for (Projectile projectile : client.getProjectiles()) { + newProjectiles.add(projectile); + + if (!projectiles.contains(projectile)) { + int id = projectile.getId(); + if (id == 1707 || id == 1708) { + this.doAttack(Attack.MAGIC); + } else if (id == 1713 || id == 1714) { + this.doAttack(Attack.PRAYER); + } else if (id == 1711 || id == 1712) { + this.doAttack(Attack.RANGE); + } + } + } + + projectiles.clear(); + projectiles = newProjectiles; + } + + @Subscribe + public void onGameObjectSpawned(GameObjectSpawned event) { + onTileObject(event.getTile(), null, event.getGameObject()); + } + + @Subscribe + public void onGameObjectChanged(GameObjectChanged event) { + onTileObject(event.getTile(), event.getPrevious(), event.getGameObject()); + } + + @Subscribe + public void onGameObjectDespawned(GameObjectDespawned event) { + onTileObject(event.getTile(), event.getGameObject(), null); + } + + @Subscribe + public void onGroundObjectSpawned(GroundObjectSpawned event) { + onTileObject(event.getTile(), null, event.getGroundObject()); + } + + @Subscribe + public void onGroundObjectChanged(GroundObjectChanged event) { + onTileObject(event.getTile(), event.getPrevious(), event.getGroundObject()); + } + + @Subscribe + public void onGroundObjectDespawned(GroundObjectDespawned event) { + onTileObject(event.getTile(), event.getGroundObject(), null); + } + + @Subscribe + public void onWallObjectSpawned(WallObjectSpawned event) { + onTileObject(event.getTile(), null, event.getWallObject()); + } + + @Subscribe + public void onWallObjectChanged(WallObjectChanged event) { + onTileObject(event.getTile(), event.getPrevious(), event.getWallObject()); + } + + @Subscribe + public void onWallObjectDespawned(WallObjectDespawned event) { + onTileObject(event.getTile(), event.getWallObject(), null); + } + + @Subscribe + public void onDecorativeObjectSpawned(DecorativeObjectSpawned event) { + onTileObject(event.getTile(), null, event.getDecorativeObject()); + } + + @Subscribe + public void onDecorativeObjectChanged(DecorativeObjectChanged event) { + onTileObject(event.getTile(), event.getPrevious(), event.getDecorativeObject()); + } + + @Subscribe + public void onDecorativeObjectDespawned(DecorativeObjectDespawned event) { + onTileObject(event.getTile(), event.getDecorativeObject(), null); + } + + + @Subscribe + public void onGameStateChanged(GameStateChanged event) { + if (event.getGameState() == GameState.LOADING) { + resources.clear(); + } + } + + private void onTileObject(Tile tile, TileObject oldObject, TileObject newObject) { + resources.remove(oldObject); + + if (newObject == null) { + return; + } + + int id = newObject.getId(); + + int[] ids = { + 36068, // Fishing Spot (Harpoon) + 35967, + 36066, // Phren Roots (Axe) + 35969, + 36070, // Grym Root + 35971, + 36072, // Linum Tirinum + 35973, + 36064, // Crystal Deposit + 35975 + }; + + for (int i : ids) { + if (i == id) { + resources.put(newObject, tile); + } + } + } + + public boolean inBoss() { + return client.getVarbitValue(client.getVarps(), 9177) == 1; + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..32d898f --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# RuneLite Gauntlet Plugin (Last Updated: 2019 July 27) +##### Made by: Yuri-chan (Nagi#0001) +# +**Disclaimer:** +I'm only 5 hours into this boss with 2 normal kc and 6 corrupted kc. +More features will be added as they become relevant. + +## Settings and Features: +![alt text](https://www.kthisiscvpv.com/jmeE91564311006SAwWl.png) + +## Highlight Resources: +Resource deposits in each room will be highlighted in yellow. + +![alt text](https://www.kthisiscvpv.com/BvhGO1564311286FFSvx.jpg) + +## Highlight Icons: +Resource deposits in each room will be highlighted in yellow. +**Warning:** This setting depends on __Highlight Resources__ being turned __ON__. + +![alt text](https://www.kthisiscvpv.com/tYCOE1564311216ylXbk.png) + +## Count Player Attacks: +Count the amount of player attacks left until the boss changes their prayer. +**Warning:** This setting depends on __Boss Attacks__ being turned __ON__. + +## Count Boss Attacks: +Count the amount of attacks left until the boss changes their attack style. + +![alt text](https://www.kthisiscvpv.com/xCD8R1564311933UX6Fl.png) + +## Show Attack Styles: +Overlays each projectile shot by the boss with an icon. + +![alt text](https://www.kthisiscvpv.com/YlKVu1564312230N1WO2.png) + +## Overlay the Boss: +Overlay the boss entirely with a color coded style reference. + +![alt text](https://www.kthisiscvpv.com/oBZp01564312305iuqmm.png) +![alt text](https://www.kthisiscvpv.com/vJjSJ1564312314gMpm5.png) +![alt text](https://www.kthisiscvpv.com/T1hDw1564312358bZc3h.png) +![alt text](https://www.kthisiscvpv.com/mSw8X1564312347BshE3.png) + +## Play Tune on Prayer Attack: +Play a quick tune when the prayer attack is being sent. +**Warning:** This setting will override your in-game sound settings. + +``client.playSoundEffect(SoundEffectID.MAGIC_SPLASH_BOING, SoundEffectVolume.MEDIUM_HIGH);`` \ No newline at end of file