last compatibility and general things for injector

This commit is contained in:
Lucwousin
2019-11-02 00:57:41 +01:00
parent 1071e43b04
commit 0c1fa761f5
5 changed files with 1489 additions and 1792 deletions

View File

@@ -3,4 +3,4 @@ org.gradle.warning.mode=all
org.gradle.parallel=true org.gradle.parallel=true
org.gradle.console=rich org.gradle.console=rich
org.gradle.configureondemand=true org.gradle.configureondemand=true
org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 org.gradle.jvmargs=-XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

View File

@@ -1,34 +0,0 @@
/*
* Copyright (c) 2019 ThatGamerBlue
* 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.
*/
/**
* @author ThatGamerBlue
*
* This file exists to force gradle to execute the compileJava task
* so we can hijack it and run the injector-plugin
*/
public class Placeholder
{
}

View File

@@ -1,268 +0,0 @@
/*
* Copyright (c) 2018, Lucas <https://github.com/Lucwousin>
* 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.injector.raw;
import com.google.common.collect.Lists;
import net.runelite.asm.ClassFile;
import net.runelite.asm.Field;
import net.runelite.asm.Method;
import net.runelite.asm.attributes.Code;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.InstructionType;
import net.runelite.asm.attributes.code.Instructions;
import net.runelite.asm.attributes.code.instruction.types.FieldInstruction;
import net.runelite.asm.attributes.code.instruction.types.GetFieldInstruction;
import net.runelite.asm.attributes.code.instruction.types.LVTInstruction;
import net.runelite.asm.attributes.code.instruction.types.PushConstantInstruction;
import net.runelite.asm.attributes.code.instructions.GetStatic;
import net.runelite.asm.attributes.code.instructions.IALoad;
import net.runelite.asm.attributes.code.instructions.IAStore;
import net.runelite.asm.attributes.code.instructions.IAdd;
import net.runelite.asm.attributes.code.instructions.ILoad;
import net.runelite.asm.attributes.code.instructions.IOr;
import net.runelite.asm.attributes.code.instructions.IShR;
import net.runelite.asm.attributes.code.instructions.ISub;
import net.runelite.asm.attributes.code.instructions.IUShR;
import net.runelite.asm.attributes.code.instructions.InvokeStatic;
import net.runelite.asm.attributes.code.instructions.LDC;
import net.runelite.asm.attributes.code.instructions.SiPush;
import net.runelite.asm.execution.Execution;
import net.runelite.asm.execution.InstructionContext;
import net.runelite.asm.execution.MethodContext;
import net.runelite.asm.execution.StackContext;
import net.runelite.asm.execution.VariableContext;
import net.runelite.asm.pool.Class;
import net.runelite.asm.signature.Signature;
import net.runelite.injector.Inject;
import net.runelite.injector.InjectUtil;
import net.runelite.injector.InjectionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RasterizerAlpha
{
private static final Logger logger = LoggerFactory.getLogger(RasterizerAlpha.class);
private static final net.runelite.asm.pool.Method DRAWALPHA = new net.runelite.asm.pool.Method(
new Class("client"),
"drawAlpha",
new Signature("([IIII)V")
);
private static final int ALPHA = 0xff000000;
private final Inject inject;
public RasterizerAlpha(Inject inject)
{
this.inject = inject;
}
/*
* This class exists cause of removing colour banding.
*
* Push array on stack
* Push array index on stack
* Push colour on stack -> we're interested in where the colour comes from
* Put colour in array, popping array, index and colour
*
*
*
*/
public void inject() throws InjectionException
{
final Field r2dPx = InjectUtil.findDeobField(inject, "Rasterizer2D_pixels", "Rasterizer2D");
final Method draw = InjectUtil.findMethod(inject, "draw", "Client");
final ClassFile rasterizer2D = r2dPx.getClassFile();
final Execution ex = new Execution(rasterizer2D.getGroup());
ex.staticStep = false;
ex.step = false;
ex.addMethod(draw);
int[] counts = new int[2];
ex.addMethodContextVisitor((MethodContext mc) ->
{
Instructions instrs = getInstrs(mc);
if (instrs == null)
return;
int count = 0;
int orCount = 0;
for (InstructionContext ic : mc.getInstructionContexts())
{
Instruction instruction = ic.getInstruction();
if (!(instruction instanceof IAStore))
continue;
// Field field = astore.getMyField(ic);
// doesn't track into methods so doing it here
StackContext array = ic.getPops().get(2);
if (!isSameField(r2dPx, array))
continue;
// This is the colour that's being set
StackContext colour = ic.getPops().get(0);
// resolve gets the original value pusher
InstructionContext colPusher = colour.getPushed().resolve(colour);
Instruction colPushI = colPusher.getInstruction();
// If it's not a >> or a | we're not interested
if (colPushI instanceof LVTInstruction // when called from a method we didn't execute
|| colPushI instanceof PushConstantInstruction &&
!((PushConstantInstruction) colPushI).getConstant().equals(0)
|| colPushI instanceof IALoad)
{
// OR with 0xFF000000, unless 0
int storeIdx = instrs.getInstructions().indexOf(instruction);
instrs.addInstruction(storeIdx++, new LDC(instrs, ALPHA));
instrs.addInstruction(storeIdx, new IOr(instrs, InstructionType.IOR));
++orCount;
continue;
}
else if (!(
colPushI instanceof IShR ||
colPushI instanceof IUShR ||
colPushI instanceof IAdd))
{
continue;
}
// So we know we're dealing with alpha here, now we need the alpha value
// earlier on in the method there's been a 256 - XXX, where xxx is alpha
for (InstructionContext ins : mc.getInstructionContexts())
{
if (!(ins.getInstruction() instanceof SiPush))
continue;
SiPush pci = (SiPush) ins.getInstruction();
if ((short) pci.getConstant() != (short) 256)
continue;
InstructionContext isub = ins.getPushes().get(0).getPopped().get(0);
if (!(isub.getInstruction() instanceof ISub))
continue;
StackContext alphaPop = isub.getPops().get(0);
InstructionContext alphaPusher = alphaPop.getPushed().resolve(alphaPop);
InstructionContext isubResult = isub.getPushes().get(0).getPopped().get(0);
if (pushesToSameField(isubResult, alphaPusher))
{
alphaPusher = resolveFieldThroughInvokes(alphaPop);
if (alphaPusher == null)
throw new RuntimeException("Alpha var is overwritten and we don't know what pushed it"); // cheeky unchecked
}
int storeIdx = instrs.getInstructions().indexOf(instruction);
Instruction alphaPushI = alphaPusher.getInstruction();
if (alphaPushI instanceof GetStatic)
{
instrs.addInstruction(storeIdx++, new LDC(instrs, 255));
instrs.addInstruction(storeIdx++, new GetStatic(instrs, ((GetStatic) alphaPushI).getField()));
instrs.addInstruction(storeIdx++, new ISub(instrs, InstructionType.ISUB));
}
else if (alphaPushI instanceof LVTInstruction)
{
instrs.addInstruction(storeIdx++, new ILoad(instrs, ((LVTInstruction) alphaPushI).getVariableIndex()));
}
instrs.getInstructions().set(storeIdx, new InvokeStatic(instrs, DRAWALPHA));
++count;
break;
}
}
if (orCount != 0)
{
counts[0] += orCount;
logger.info("Added {} OR's into {}", orCount, mc.getMethod());
}
if (count != 0)
{
counts[1] += count;
logger.info("Injected {} DrawAlpha invokes into {}", count, mc.getMethod());
}
});
ex.run();
logger.info("Injected {} DrawAlpha invokes and {} ors", counts[1], counts[0]);
}
private static boolean pushesToSameField(InstructionContext cA, InstructionContext cB)
{
if (cA.getInstruction() instanceof FieldInstruction && cB instanceof FieldInstruction)
{
Field a = ((FieldInstruction) cA.getInstruction()).getMyField();
Field b = ((FieldInstruction) cB.getInstruction()).getMyField();
return a == b;
}
return false;
}
private static Instructions getInstrs(MethodContext mc)
{
Code c = mc.getMethod().getCode();
if (c == null)
return null;
return c.getInstructions();
}
private static InstructionContext resolveFieldThroughInvokes(StackContext stackContext)
{
InstructionContext pusher = stackContext.getPushed().resolve(stackContext);
if (pusher.getInstruction() instanceof GetFieldInstruction)
return pusher;
// No field I wanna trace, rn at least
if (!(pusher.getInstruction() instanceof LVTInstruction))
return null;
int vidx = ((LVTInstruction) pusher.getInstruction()).getVariableIndex();
VariableContext vc = pusher.getVariables().get(vidx);
stackContext = Lists.reverse(vc.getInstructionWhichStored().getPops()).get(vidx);
return resolveFieldThroughInvokes(stackContext);
}
private static boolean isSameField(Field f, StackContext array)
{
InstructionContext ic = resolveFieldThroughInvokes(array);
if (ic == null)
return false;
return ((GetFieldInstruction) ic.getInstruction()).getMyField() == f;
}
}

View File

@@ -1,341 +1,340 @@
/* /*
* Copyright (c) 2018, Adam <Adam@sigterm.info> * Copyright (c) 2018, Adam <Adam@sigterm.info>
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* 1. Redistributions of source code must retain the above copyright notice, this * 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, * 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * 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 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * 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 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package net.runelite.client.game; package net.runelite.client.game;
import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap; import com.google.common.collect.ListMultimap;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.api.AnimationID; import net.runelite.api.AnimationID;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.TileItem; import net.runelite.api.TileItem;
import net.runelite.api.ItemID; import net.runelite.api.ItemID;
import net.runelite.api.NPC; import net.runelite.api.NPC;
import net.runelite.api.NpcID; import net.runelite.api.NpcID;
import net.runelite.api.Player; import net.runelite.api.Player;
import net.runelite.api.Tile; import net.runelite.api.Tile;
import net.runelite.api.coords.LocalPoint; import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint; import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.AnimationChanged; import net.runelite.api.events.AnimationChanged;
import net.runelite.api.events.GameTick; import net.runelite.api.events.GameTick;
import net.runelite.api.events.ItemDespawned; import net.runelite.api.events.ItemDespawned;
import net.runelite.api.events.ItemQuantityChanged; import net.runelite.api.events.ItemQuantityChanged;
import net.runelite.api.events.ItemSpawned; import net.runelite.api.events.ItemSpawned;
import net.runelite.api.events.NpcDespawned; import net.runelite.api.events.NpcDespawned;
import net.runelite.api.events.PlayerDespawned; import net.runelite.api.events.PlayerDespawned;
import net.runelite.client.eventbus.EventBus; import net.runelite.client.eventbus.EventBus;
import net.runelite.client.events.NpcLootReceived; import net.runelite.client.events.NpcLootReceived;
import net.runelite.client.events.PlayerLootReceived; import net.runelite.client.events.PlayerLootReceived;
@Singleton @Singleton
@Slf4j @Slf4j
public class LootManager public class LootManager
{ {
private static final Map<Integer, Integer> NPC_DEATH_ANIMATIONS = ImmutableMap.<Integer, Integer>builder() private static final Map<Integer, Integer> NPC_DEATH_ANIMATIONS = ImmutableMap.<Integer, Integer>builder()
.put(NpcID.CAVE_KRAKEN, AnimationID.CAVE_KRAKEN_DEATH) .put(NpcID.CAVE_KRAKEN, AnimationID.CAVE_KRAKEN_DEATH)
.put(NpcID.CRYSTALLINE_BAT, AnimationID.CRYSTALLINE_BAT_DEATH) .put(NpcID.CRYSTALLINE_BAT, AnimationID.CRYSTALLINE_BAT_DEATH)
.put(NpcID.CRYSTALLINE_RAT, AnimationID.CRYSTALLINE_RAT_DEATH) .put(NpcID.CRYSTALLINE_RAT, AnimationID.CRYSTALLINE_RAT_DEATH)
.put(NpcID.CRYSTALLINE_SPIDER, AnimationID.CRYSTALLINE_SPIDER_DEATH) .put(NpcID.CRYSTALLINE_SPIDER, AnimationID.CRYSTALLINE_SPIDER_DEATH)
.put(NpcID.CRYSTALLINE_WOLF, AnimationID.CRYSTALLINE_WOLF_DEATH) .put(NpcID.CRYSTALLINE_WOLF, AnimationID.CRYSTALLINE_WOLF_DEATH)
.put(NpcID.CRYSTALLINE_UNICORN, AnimationID.CRYSTALLINE_UNICORN_DEATH) .put(NpcID.CRYSTALLINE_UNICORN, AnimationID.CRYSTALLINE_UNICORN_DEATH)
.put(NpcID.CRYSTALLINE_SCORPION, AnimationID.CORRUPTED_SCORPION_DEATH) .put(NpcID.CRYSTALLINE_SCORPION, AnimationID.CORRUPTED_SCORPION_DEATH)
.put(NpcID.CRYSTALLINE_DRAGON, AnimationID.CRYSTALLINE_DRAGON_DEATH) .put(NpcID.CRYSTALLINE_DRAGON, AnimationID.CRYSTALLINE_DRAGON_DEATH)
.put(NpcID.CRYSTALLINE_DARK_BEAST, AnimationID.CRYSTALLINE_DARK_BEAST_DEATH) .put(NpcID.CRYSTALLINE_DARK_BEAST, AnimationID.CRYSTALLINE_DARK_BEAST_DEATH)
.put(NpcID.CRYSTALLINE_BEAR, AnimationID.CRYSTALLINE_BEAR_DEATH) .put(NpcID.CRYSTALLINE_BEAR, AnimationID.CRYSTALLINE_BEAR_DEATH)
.put(NpcID.CORRUPTED_BAT, AnimationID.CRYSTALLINE_BAT_DEATH) .put(NpcID.CORRUPTED_BAT, AnimationID.CRYSTALLINE_BAT_DEATH)
.put(NpcID.CORRUPTED_RAT, AnimationID.CRYSTALLINE_RAT_DEATH) .put(NpcID.CORRUPTED_RAT, AnimationID.CRYSTALLINE_RAT_DEATH)
.put(NpcID.CORRUPTED_SPIDER, AnimationID.CRYSTALLINE_SPIDER_DEATH) .put(NpcID.CORRUPTED_SPIDER, AnimationID.CRYSTALLINE_SPIDER_DEATH)
.put(NpcID.CORRUPTED_WOLF, AnimationID.CRYSTALLINE_WOLF_DEATH) .put(NpcID.CORRUPTED_WOLF, AnimationID.CRYSTALLINE_WOLF_DEATH)
.put(NpcID.CORRUPTED_UNICORN, AnimationID.CRYSTALLINE_UNICORN_DEATH) .put(NpcID.CORRUPTED_UNICORN, AnimationID.CRYSTALLINE_UNICORN_DEATH)
.put(NpcID.CORRUPTED_SCORPION, AnimationID.CORRUPTED_SCORPION_DEATH) .put(NpcID.CORRUPTED_SCORPION, AnimationID.CORRUPTED_SCORPION_DEATH)
.put(NpcID.CORRUPTED_DRAGON, AnimationID.CRYSTALLINE_DRAGON_DEATH) .put(NpcID.CORRUPTED_DRAGON, AnimationID.CRYSTALLINE_DRAGON_DEATH)
.put(NpcID.CORRUPTED_DARK_BEAST, AnimationID.CRYSTALLINE_DARK_BEAST_DEATH) .put(NpcID.CORRUPTED_DARK_BEAST, AnimationID.CRYSTALLINE_DARK_BEAST_DEATH)
.put(NpcID.CORRUPTED_BEAR, AnimationID.CRYSTALLINE_BEAR_DEATH) .put(NpcID.CORRUPTED_BEAR, AnimationID.CRYSTALLINE_BEAR_DEATH)
.build(); .build();
private final EventBus eventBus; private final EventBus eventBus;
private final Client client; private final Client client;
private final ListMultimap<Integer, ItemStack> itemSpawns = ArrayListMultimap.create(); private final ListMultimap<Integer, ItemStack> itemSpawns = ArrayListMultimap.create();
private final Set<LocalPoint> killPoints = new HashSet<>(); private final Set<LocalPoint> killPoints = new HashSet<>();
private WorldPoint playerLocationLastTick; private WorldPoint playerLocationLastTick;
private WorldPoint krakenPlayerLocation; private WorldPoint krakenPlayerLocation;
@Inject @Inject
private LootManager( private LootManager(
final EventBus eventBus, final EventBus eventBus,
final Client client final Client client
) )
{ {
this.eventBus = eventBus; this.eventBus = eventBus;
this.client = client; this.client = client;
eventBus.subscribe(GameTick.class, this, this::onGameTick); eventBus.subscribe(GameTick.class, this, this::onGameTick);
eventBus.subscribe(NpcDespawned.class, this, this::onNpcDespawned); eventBus.subscribe(NpcDespawned.class, this, this::onNpcDespawned);
eventBus.subscribe(PlayerDespawned.class, this, this::onPlayerDespawned); eventBus.subscribe(PlayerDespawned.class, this, this::onPlayerDespawned);
eventBus.subscribe(ItemSpawned.class, this, this::onItemSpawned); eventBus.subscribe(ItemSpawned.class, this, this::onItemSpawned);
eventBus.subscribe(ItemDespawned.class, this, this::onItemDespawned); eventBus.subscribe(ItemDespawned.class, this, this::onItemDespawned);
eventBus.subscribe(ItemQuantityChanged.class, this, this::onItemQuantityChanged); eventBus.subscribe(ItemQuantityChanged.class, this, this::onItemQuantityChanged);
eventBus.subscribe(AnimationChanged.class, this, this::onAnimationChanged); eventBus.subscribe(AnimationChanged.class, this, this::onAnimationChanged);
} }
private void onNpcDespawned(NpcDespawned npcDespawned) private void onNpcDespawned(NpcDespawned npcDespawned)
{ {
final NPC npc = npcDespawned.getNpc(); final NPC npc = npcDespawned.getNpc();
if (!npc.isDead()) if (!npc.isDead())
{ {
int id = npc.getId(); int id = npc.getId();
switch (id) switch (id)
{ {
case NpcID.GARGOYLE: case NpcID.GARGOYLE:
case NpcID.GARGOYLE_413: case NpcID.GARGOYLE_413:
case NpcID.GARGOYLE_1543: case NpcID.GARGOYLE_1543:
case NpcID.MARBLE_GARGOYLE: case NpcID.MARBLE_GARGOYLE:
case NpcID.MARBLE_GARGOYLE_7408: case NpcID.MARBLE_GARGOYLE_7408:
case NpcID.DUSK_7888: case NpcID.DUSK_7888:
case NpcID.DUSK_7889: case NpcID.DUSK_7889:
case NpcID.ROCKSLUG: case NpcID.ROCKSLUG:
case NpcID.ROCKSLUG_422: case NpcID.ROCKSLUG_422:
case NpcID.GIANT_ROCKSLUG: case NpcID.GIANT_ROCKSLUG:
case NpcID.SMALL_LIZARD: case NpcID.SMALL_LIZARD:
case NpcID.SMALL_LIZARD_463: case NpcID.SMALL_LIZARD_463:
case NpcID.DESERT_LIZARD: case NpcID.DESERT_LIZARD:
case NpcID.DESERT_LIZARD_460: case NpcID.DESERT_LIZARD_460:
case NpcID.DESERT_LIZARD_461: case NpcID.DESERT_LIZARD_461:
case NpcID.LIZARD: case NpcID.LIZARD:
case NpcID.ZYGOMITE: case NpcID.ZYGOMITE:
case NpcID.ZYGOMITE_1024: case NpcID.ZYGOMITE_1024:
case NpcID.ANCIENT_ZYGOMITE: case NpcID.ANCIENT_ZYGOMITE:
// these monsters die with >0 hp, so we just look for coincident // these monsters die with >0 hp, so we just look for coincident
// item spawn with despawn // item spawn with despawn
break; break;
default: default:
return; return;
} }
} }
processNpcLoot(npc); processNpcLoot(npc);
} }
private void onPlayerDespawned(PlayerDespawned playerDespawned) private void onPlayerDespawned(PlayerDespawned playerDespawned)
{ {
final Player player = playerDespawned.getPlayer(); final Player player = playerDespawned.getPlayer();
// Only care about dead Players // Only care about dead Players
if (player.getHealthRatio() != 0) if (player.getHealthRatio() != 0)
{ {
return; return;
} }
final LocalPoint location = LocalPoint.fromWorld(client, player.getWorldLocation()); final LocalPoint location = LocalPoint.fromWorld(client, player.getWorldLocation());
if (location == null || killPoints.contains(location)) if (location == null || killPoints.contains(location))
{ {
return; return;
} }
final int x = location.getSceneX(); final int x = location.getSceneX();
final int y = location.getSceneY(); final int y = location.getSceneY();
final int packed = x << 8 | y; final int packed = x << 8 | y;
final Collection<ItemStack> items = itemSpawns.get(packed); final Collection<ItemStack> items = itemSpawns.get(packed);
if (items.isEmpty()) if (items.isEmpty())
{ {
return; return;
} }
killPoints.add(location); killPoints.add(location);
eventBus.post(PlayerLootReceived.class, new PlayerLootReceived(player, items)); eventBus.post(PlayerLootReceived.class, new PlayerLootReceived(player, items));
} }
private void onItemSpawned(ItemSpawned itemSpawned) private void onItemSpawned(ItemSpawned itemSpawned)
{ {
final TileItem item = itemSpawned.getItem(); final TileItem item = itemSpawned.getItem();
final Tile tile = itemSpawned.getTile(); final Tile tile = itemSpawned.getTile();
final LocalPoint location = tile.getLocalLocation(); final LocalPoint location = tile.getLocalLocation();
final int packed = location.getSceneX() << 8 | location.getSceneY(); final int packed = location.getSceneX() << 8 | location.getSceneY();
log.debug("storing items in {}", packed); itemSpawns.put(packed, new ItemStack(item.getId(), item.getQuantity(), location));
itemSpawns.put(packed, new ItemStack(item.getId(), item.getQuantity(), location)); log.debug("Item spawn {} ({}) location {}", item.getId(), item.getQuantity(), location);
log.debug("Item spawn {} ({}) location {}", item.getId(), item.getQuantity(), location); }
}
private void onItemDespawned(ItemDespawned itemDespawned)
private void onItemDespawned(ItemDespawned itemDespawned) {
{ final TileItem item = itemDespawned.getItem();
final TileItem item = itemDespawned.getItem(); final LocalPoint location = itemDespawned.getTile().getLocalLocation();
final LocalPoint location = itemDespawned.getTile().getLocalLocation(); log.debug("Item despawn {} ({}) location {}", item.getId(), item.getQuantity(), location);
log.debug("Item despawn {} ({}) location {}", item.getId(), item.getQuantity(), location); }
}
private void onItemQuantityChanged(ItemQuantityChanged itemQuantityChanged)
private void onItemQuantityChanged(ItemQuantityChanged itemQuantityChanged) {
{ final TileItem item = itemQuantityChanged.getItem();
final TileItem item = itemQuantityChanged.getItem(); final Tile tile = itemQuantityChanged.getTile();
final Tile tile = itemQuantityChanged.getTile(); final LocalPoint location = tile.getLocalLocation();
final LocalPoint location = tile.getLocalLocation(); final int packed = location.getSceneX() << 8 | location.getSceneY();
final int packed = location.getSceneX() << 8 | location.getSceneY(); final int diff = itemQuantityChanged.getNewQuantity() - itemQuantityChanged.getOldQuantity();
final int diff = itemQuantityChanged.getNewQuantity() - itemQuantityChanged.getOldQuantity();
if (diff <= 0)
if (diff <= 0) {
{ return;
return; }
}
itemSpawns.put(packed, new ItemStack(item.getId(), diff, location));
itemSpawns.put(packed, new ItemStack(item.getId(), diff, location)); }
}
private void onAnimationChanged(AnimationChanged e)
private void onAnimationChanged(AnimationChanged e) {
{ if (!(e.getActor() instanceof NPC))
if (!(e.getActor() instanceof NPC)) {
{ return;
return; }
}
final NPC npc = (NPC) e.getActor();
final NPC npc = (NPC) e.getActor(); int id = npc.getId();
int id = npc.getId();
// We only care about certain NPCs
// We only care about certain NPCs final Integer deathAnim = NPC_DEATH_ANIMATIONS.get(id);
final Integer deathAnim = NPC_DEATH_ANIMATIONS.get(id);
// Current animation is death animation?
// Current animation is death animation? if (deathAnim != null && deathAnim == npc.getAnimation())
if (deathAnim != null && deathAnim == npc.getAnimation()) {
{ if (id == NpcID.CAVE_KRAKEN)
if (id == NpcID.CAVE_KRAKEN) {
{ // Big Kraken drops loot wherever player is standing when animation starts.
// Big Kraken drops loot wherever player is standing when animation starts. krakenPlayerLocation = client.getLocalPlayer().getWorldLocation();
krakenPlayerLocation = client.getLocalPlayer().getWorldLocation(); }
} else
else {
{ // These NPCs drop loot on death animation, which is right now.
// These NPCs drop loot on death animation, which is right now. processNpcLoot(npc);
processNpcLoot(npc); }
} }
} }
}
private void onGameTick(GameTick gameTick)
private void onGameTick(GameTick gameTick) {
{ playerLocationLastTick = client.getLocalPlayer().getWorldLocation();
playerLocationLastTick = client.getLocalPlayer().getWorldLocation(); itemSpawns.clear();
itemSpawns.clear(); killPoints.clear();
killPoints.clear(); }
}
private void processNpcLoot(NPC npc)
private void processNpcLoot(NPC npc) {
{ final LocalPoint location = LocalPoint.fromWorld(client, getDropLocation(npc, npc.getWorldLocation()));
final LocalPoint location = LocalPoint.fromWorld(client, getDropLocation(npc, npc.getWorldLocation())); if (location == null || killPoints.contains(location))
if (location == null || killPoints.contains(location)) {
{ return;
return; }
}
final int x = location.getSceneX();
final int x = location.getSceneX(); final int y = location.getSceneY();
final int y = location.getSceneY(); final int size = npc.getDefinition().getSize();
final int size = npc.getDefinition().getSize();
// Some NPCs drop items onto multiple tiles
// Some NPCs drop items onto multiple tiles final List<ItemStack> allItems = new ArrayList<>();
final List<ItemStack> allItems = new ArrayList<>(); for (int i = 0; i < size; ++i)
for (int i = 0; i < size; ++i) {
{ for (int j = 0; j < size; ++j)
for (int j = 0; j < size; ++j) {
{ final int packed = (x + i) << 8 | (y + j);
final int packed = (x + i) << 8 | (y + j); final Collection<ItemStack> items = itemSpawns.get(packed);
final Collection<ItemStack> items = itemSpawns.get(packed); allItems.addAll(items);
allItems.addAll(items); }
} }
}
if (allItems.isEmpty())
if (allItems.isEmpty()) {
{ return;
return; }
}
killPoints.add(location);
killPoints.add(location); eventBus.post(NpcLootReceived.class, new NpcLootReceived(npc, allItems));
eventBus.post(NpcLootReceived.class, new NpcLootReceived(npc, allItems)); }
}
private WorldPoint getDropLocation(NPC npc, WorldPoint worldLocation)
private WorldPoint getDropLocation(NPC npc, WorldPoint worldLocation) {
{ switch (npc.getId())
switch (npc.getId()) {
{ case NpcID.KRAKEN:
case NpcID.KRAKEN: case NpcID.KRAKEN_6640:
case NpcID.KRAKEN_6640: case NpcID.KRAKEN_6656:
case NpcID.KRAKEN_6656: worldLocation = playerLocationLastTick;
worldLocation = playerLocationLastTick; break;
break; case NpcID.CAVE_KRAKEN:
case NpcID.CAVE_KRAKEN: worldLocation = krakenPlayerLocation;
worldLocation = krakenPlayerLocation; break;
break; case NpcID.ZULRAH: // Green
case NpcID.ZULRAH: // Green case NpcID.ZULRAH_2043: // Red
case NpcID.ZULRAH_2043: // Red case NpcID.ZULRAH_2044: // Blue
case NpcID.ZULRAH_2044: // Blue for (Map.Entry<Integer, ItemStack> entry : itemSpawns.entries())
for (Map.Entry<Integer, ItemStack> entry : itemSpawns.entries()) {
{ if (entry.getValue().getId() == ItemID.ZULRAHS_SCALES)
if (entry.getValue().getId() == ItemID.ZULRAHS_SCALES) {
{ int packed = entry.getKey();
int packed = entry.getKey(); int unpackedX = packed >> 8;
int unpackedX = packed >> 8; int unpackedY = packed & 0xFF;
int unpackedY = packed & 0xFF; worldLocation = WorldPoint.fromScene(client, unpackedX, unpackedY, worldLocation.getPlane());
worldLocation = WorldPoint.fromScene(client, unpackedX, unpackedY, worldLocation.getPlane()); break;
break; }
} }
} break;
break; case NpcID.VORKATH:
case NpcID.VORKATH: case NpcID.VORKATH_8058:
case NpcID.VORKATH_8058: case NpcID.VORKATH_8059:
case NpcID.VORKATH_8059: case NpcID.VORKATH_8060:
case NpcID.VORKATH_8060: case NpcID.VORKATH_8061:
case NpcID.VORKATH_8061: int x = worldLocation.getX() + 3;
int x = worldLocation.getX() + 3; int y = worldLocation.getY() + 3;
int y = worldLocation.getY() + 3; if (playerLocationLastTick.getX() < x)
if (playerLocationLastTick.getX() < x) {
{ x -= 4;
x -= 4; }
} else if (playerLocationLastTick.getX() > x)
else if (playerLocationLastTick.getX() > x) {
{ x += 4;
x += 4; }
} if (playerLocationLastTick.getY() < y)
if (playerLocationLastTick.getY() < y) {
{ y -= 4;
y -= 4; }
} else if (playerLocationLastTick.getY() > y)
else if (playerLocationLastTick.getY() > y) {
{ y += 4;
y += 4; }
} worldLocation = new WorldPoint(x, y, worldLocation.getPlane());
worldLocation = new WorldPoint(x, y, worldLocation.getPlane()); break;
break; }
}
return worldLocation;
return worldLocation; }
} }
}

File diff suppressed because it is too large Load Diff