From cc698c98a3c625865191e121ee8c9868657b88ea Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Thu, 9 Aug 2018 02:36:36 +0100 Subject: [PATCH] Add run energy plugin --- .../main/java/net/runelite/api/Client.java | 7 + .../main/java/net/runelite/api/Varbits.java | 5 + .../net/runelite/api/widgets/WidgetID.java | 2 + .../net/runelite/api/widgets/WidgetInfo.java | 2 + .../plugins/runenergy/RunEnergyConfig.java | 43 +++ .../plugins/runenergy/RunEnergyOverlay.java | 97 +++++++ .../plugins/runenergy/RunEnergyPlugin.java | 246 ++++++++++++++++++ .../java/net/runelite/rs/api/RSClient.java | 1 + 8 files changed, 403 insertions(+) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyPlugin.java diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java index 85dceb6c18..3775ff77c7 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -456,6 +456,13 @@ public interface Client extends GameEngine */ int getEnergy(); + /** + * Gets the current weight of the logged in player. + * + * @return the weight + */ + int getWeight(); + /** * Gets an array of options that can currently be used on other players. *

diff --git a/runelite-api/src/main/java/net/runelite/api/Varbits.java b/runelite-api/src/main/java/net/runelite/api/Varbits.java index 0e932b6b85..b1e50160e6 100644 --- a/runelite-api/src/main/java/net/runelite/api/Varbits.java +++ b/runelite-api/src/main/java/net/runelite/api/Varbits.java @@ -39,6 +39,11 @@ public enum Varbits */ TRANSPARENT_CHATBOX(4608), + /* + * If the player has an active stamina potion effect or not + */ + RUN_SLOWED_DEPLETION_ACTIVE(25), + /** * If scrollbar in resizable mode chat is on the left */ diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java index 69d5d99657..683482092a 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java @@ -254,6 +254,8 @@ public class WidgetID static final int PRAYER_ORB = 12; static final int QUICK_PRAYER_ORB = 14; // Has the "Quick-prayers" name static final int RUN_ORB = 20; + static final int TOGGLE_RUN_ORB = 22; // Has the "Toggle run" name + static final int RUN_ORB_TEXT = 23; static final int SPEC_ORB = 28; } diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java index 951e92c2c7..70d684fceb 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java @@ -141,6 +141,8 @@ public enum WidgetInfo MINIMAP_PRAYER_ORB(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.PRAYER_ORB), MINIMAP_QUICK_PRAYER_ORB(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.QUICK_PRAYER_ORB), MINIMAP_RUN_ORB(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.RUN_ORB), + MINIMAP_TOGGLE_RUN_ORB(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.TOGGLE_RUN_ORB), + MINIMAP_RUN_ORB_TEXT(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.RUN_ORB_TEXT), MINIMAP_HEALTH_ORB(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.HEALTH_ORB), MINIMAP_SPEC_ORB(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.SPEC_ORB), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyConfig.java new file mode 100644 index 0000000000..3c1418411a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyConfig.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018, Sean Dewar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.runenergy; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("runenergy") +public interface RunEnergyConfig extends Config +{ + @ConfigItem( + keyName = "replaceOrbText", + name = "Replace orb text with run time left", + description = "Show the remaining run time (in seconds) next in the energy orb." + ) + default boolean replaceOrbText() + { + return false; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyOverlay.java new file mode 100644 index 0000000000..1d501c968b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyOverlay.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2018, Sean Dewar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.runenergy; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.Point; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +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.tooltip.Tooltip; +import net.runelite.client.ui.overlay.tooltip.TooltipManager; +import org.apache.commons.lang3.StringUtils; + +class RunEnergyOverlay extends Overlay +{ + private final RunEnergyPlugin plugin; + private final Client client; + private final TooltipManager tooltipManager; + + @Inject + private RunEnergyOverlay(final RunEnergyPlugin plugin, final Client client, final TooltipManager tooltipManager) + { + this.plugin = plugin; + this.client = client; + this.tooltipManager = tooltipManager; + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_WIDGETS); + } + + @Override + public Dimension render(Graphics2D graphics) + { + final Widget runOrb = client.getWidget(WidgetInfo.MINIMAP_TOGGLE_RUN_ORB); + + if (runOrb == null) + { + return null; + } + + final Rectangle bounds = runOrb.getBounds(); + + if (bounds.getX() <= 0) + { + return null; + } + + final Point mousePosition = client.getMouseCanvasPosition(); + + if (bounds.contains(mousePosition.getX(), mousePosition.getY())) + { + StringBuilder sb = new StringBuilder(); + sb.append("Weight: ").append(client.getWeight()).append(" kg
") + .append("Run Time Remaining: ").append(plugin.getEstimatedRunTimeRemaining(false)); + + int secondsUntil100 = plugin.getEstimatedRecoverTimeRemaining(); + if (secondsUntil100 > 0) + { + final int minutes = (int) Math.floor(secondsUntil100 / 60.0); + final int seconds = (int) Math.floor(secondsUntil100 - (minutes * 60.0)); + + sb.append("
").append("100% Energy In: ").append(minutes).append(':').append(StringUtils.leftPad(Integer.toString(seconds), 2, "0")); + } + + tooltipManager.add(new Tooltip(sb.toString())); + } + + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyPlugin.java new file mode 100644 index 0000000000..1260df244a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyPlugin.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2018, Sean Dewar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.runenergy; + +import com.google.common.collect.ImmutableSet; +import com.google.common.eventbus.Subscribe; +import com.google.inject.Provides; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.EquipmentInventorySlot; +import net.runelite.api.InventoryID; +import net.runelite.api.Item; +import net.runelite.api.ItemContainer; +import static net.runelite.api.ItemID.*; +import net.runelite.api.Skill; +import net.runelite.api.Varbits; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.ConfigChanged; +import net.runelite.api.events.GameTick; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; +import org.apache.commons.lang3.StringUtils; + +@PluginDescriptor( + name = "Run Energy", + description = "Show various information related to run energy", + tags = {"overlay", "stamina"} +) +public class RunEnergyPlugin extends Plugin +{ + // TODO It would be nice if we have the IDs for just the equipped variants of the Graceful set items. + private static final ImmutableSet ALL_GRACEFUL_HOODS = ImmutableSet.of( + GRACEFUL_HOOD_11851, GRACEFUL_HOOD_13579, GRACEFUL_HOOD_13580, GRACEFUL_HOOD_13591, GRACEFUL_HOOD_13592, + GRACEFUL_HOOD_13603, GRACEFUL_HOOD_13604, GRACEFUL_HOOD_13615, GRACEFUL_HOOD_13616, GRACEFUL_HOOD_13627, + GRACEFUL_HOOD_13628, GRACEFUL_HOOD_13667, GRACEFUL_HOOD_13668, GRACEFUL_HOOD_21061, GRACEFUL_HOOD_21063 + ); + + private static final ImmutableSet ALL_GRACEFUL_TOPS = ImmutableSet.of( + GRACEFUL_TOP_11855, GRACEFUL_TOP_13583, GRACEFUL_TOP_13584, GRACEFUL_TOP_13595, GRACEFUL_TOP_13596, + GRACEFUL_TOP_13607, GRACEFUL_TOP_13608, GRACEFUL_TOP_13619, GRACEFUL_TOP_13620, GRACEFUL_TOP_13631, + GRACEFUL_TOP_13632, GRACEFUL_TOP_13671, GRACEFUL_TOP_13672, GRACEFUL_TOP_21067, GRACEFUL_TOP_21069 + ); + + private static final ImmutableSet ALL_GRACEFUL_LEGS = ImmutableSet.of( + GRACEFUL_LEGS_11857, GRACEFUL_LEGS_13585, GRACEFUL_LEGS_13586, GRACEFUL_LEGS_13597, GRACEFUL_LEGS_13598, + GRACEFUL_LEGS_13609, GRACEFUL_LEGS_13610, GRACEFUL_LEGS_13621, GRACEFUL_LEGS_13622, GRACEFUL_LEGS_13633, + GRACEFUL_LEGS_13634, GRACEFUL_LEGS_13673, GRACEFUL_LEGS_13674, GRACEFUL_LEGS_21070, GRACEFUL_LEGS_21072 + ); + + private static final ImmutableSet ALL_GRACEFUL_GLOVES = ImmutableSet.of( + GRACEFUL_GLOVES_11859, GRACEFUL_GLOVES_13587, GRACEFUL_GLOVES_13588, GRACEFUL_GLOVES_13599, GRACEFUL_GLOVES_13600, + GRACEFUL_GLOVES_13611, GRACEFUL_GLOVES_13612, GRACEFUL_GLOVES_13623, GRACEFUL_GLOVES_13624, GRACEFUL_GLOVES_13635, + GRACEFUL_GLOVES_13636, GRACEFUL_GLOVES_13675, GRACEFUL_GLOVES_13676, GRACEFUL_GLOVES_21073, GRACEFUL_GLOVES_21075 + ); + + private static final ImmutableSet ALL_GRACEFUL_BOOTS = ImmutableSet.of( + GRACEFUL_BOOTS_11861, GRACEFUL_BOOTS_13589, GRACEFUL_BOOTS_13590, GRACEFUL_BOOTS_13601, GRACEFUL_BOOTS_13602, + GRACEFUL_BOOTS_13613, GRACEFUL_BOOTS_13614, GRACEFUL_BOOTS_13625, GRACEFUL_BOOTS_13626, GRACEFUL_BOOTS_13637, + GRACEFUL_BOOTS_13638, GRACEFUL_BOOTS_13677, GRACEFUL_BOOTS_13678, GRACEFUL_BOOTS_21076, GRACEFUL_BOOTS_21078 + ); + + // Agility skill capes and the non-cosmetic Max capes also count for the Graceful set effect + private static final ImmutableSet ALL_GRACEFUL_CAPES = ImmutableSet.of( + GRACEFUL_CAPE_11853, GRACEFUL_CAPE_13581, GRACEFUL_CAPE_13582, GRACEFUL_CAPE_13593, GRACEFUL_CAPE_13594, + GRACEFUL_CAPE_13605, GRACEFUL_CAPE_13606, GRACEFUL_CAPE_13617, GRACEFUL_CAPE_13618, GRACEFUL_CAPE_13629, + GRACEFUL_CAPE_13630, GRACEFUL_CAPE_13669, GRACEFUL_CAPE_13670, GRACEFUL_CAPE_21064, GRACEFUL_CAPE_21066, + AGILITY_CAPE, AGILITY_CAPET, MAX_CAPE + ); + + @Inject + private Client client; + + @Inject + private OverlayManager overlayManager; + + @Inject + private RunEnergyOverlay energyOverlay; + + @Inject + private RunEnergyConfig energyConfig; + + private boolean localPlayerRunningToDestination; + private WorldPoint prevLocalPlayerLocation; + + @Provides + RunEnergyConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(RunEnergyConfig.class); + } + + @Override + protected void startUp() throws Exception + { + overlayManager.add(energyOverlay); + } + + @Override + protected void shutDown() throws Exception + { + overlayManager.remove(energyOverlay); + localPlayerRunningToDestination = false; + prevLocalPlayerLocation = null; + resetRunOrbText(); + } + + @Subscribe + public void onGameTick(GameTick event) + { + localPlayerRunningToDestination = + prevLocalPlayerLocation != null && + client.getLocalDestinationLocation() != null && + prevLocalPlayerLocation.distanceTo(client.getLocalPlayer().getWorldLocation()) > 1; + + prevLocalPlayerLocation = client.getLocalPlayer().getWorldLocation(); + + if (energyConfig.replaceOrbText()) + { + setRunOrbText(getEstimatedRunTimeRemaining(true)); + } + } + + @Subscribe + public void onConfigChanged(ConfigChanged event) + { + if (event.getGroup().equals("runenergy") && !energyConfig.replaceOrbText()) + { + resetRunOrbText(); + } + } + + private void setRunOrbText(String text) + { + Widget runOrbText = client.getWidget(WidgetInfo.MINIMAP_RUN_ORB_TEXT); + + if (runOrbText != null) + { + runOrbText.setText(text); + } + } + + private void resetRunOrbText() + { + setRunOrbText(Integer.toString(client.getEnergy())); + } + + String getEstimatedRunTimeRemaining(boolean inSeconds) + { + // Calculate the amount of energy lost every 2 ticks (0.6 seconds). + // Negative weight has the same depletion effect as 0 kg. + final int effectiveWeight = Math.max(client.getWeight(), 0); + double lossRate = (Math.min(effectiveWeight, 64) / 100.0) + 0.64; + + if (client.getVar(Varbits.RUN_SLOWED_DEPLETION_ACTIVE) != 0) + { + lossRate *= 0.3; // Stamina effect reduces energy depletion to 30% + } + + // Calculate the number of seconds left + final double secondsLeft = (client.getEnergy() * 0.6) / lossRate; + + // Return the text + if (inSeconds) + { + return Integer.toString((int) Math.floor(secondsLeft)) + "s"; + } + else + { + final int minutes = (int) Math.floor(secondsLeft / 60.0); + final int seconds = (int) Math.floor(secondsLeft - (minutes * 60.0)); + + return Integer.toString(minutes) + ":" + StringUtils.leftPad(Integer.toString(seconds), 2, "0"); + } + } + + private boolean isLocalPlayerWearingFullGraceful() + { + final ItemContainer equipment = client.getItemContainer(InventoryID.EQUIPMENT); + + if (equipment == null) + { + return false; + } + + final Item[] items = equipment.getItems(); + + // Check that the local player is wearing enough items to be using full Graceful + // (the Graceful boots will have the highest slot index in the worn set). + if (items == null || items.length <= EquipmentInventorySlot.BOOTS.getSlotIdx()) + { + return false; + } + + return (ALL_GRACEFUL_HOODS.contains(items[EquipmentInventorySlot.HEAD.getSlotIdx()].getId()) && + ALL_GRACEFUL_TOPS.contains(items[EquipmentInventorySlot.BODY.getSlotIdx()].getId()) && + ALL_GRACEFUL_LEGS.contains(items[EquipmentInventorySlot.LEGS.getSlotIdx()].getId()) && + ALL_GRACEFUL_GLOVES.contains(items[EquipmentInventorySlot.GLOVES.getSlotIdx()].getId()) && + ALL_GRACEFUL_BOOTS.contains(items[EquipmentInventorySlot.BOOTS.getSlotIdx()].getId()) && + ALL_GRACEFUL_CAPES.contains(items[EquipmentInventorySlot.CAPE.getSlotIdx()].getId())); + } + + int getEstimatedRecoverTimeRemaining() + { + if (localPlayerRunningToDestination) + { + return -1; + } + + // Calculate the amount of energy recovered every second + double recoverRate = (48 + client.getBoostedSkillLevel(Skill.AGILITY)) / 360.0; + + if (isLocalPlayerWearingFullGraceful()) + { + recoverRate *= 1.3; // 30% recover rate increase from Graceful set effect + } + + // Calculate the number of seconds left + final double secondsLeft = (100 - client.getEnergy()) / recoverRate; + return (int) secondsLeft; + } +} diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java b/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java index 4a03a94858..20d4336892 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java @@ -111,6 +111,7 @@ public interface RSClient extends RSGameEngine, Client int getEnergy(); @Import("weight") + @Override int getWeight(); @Import("baseX")