Add crowdsourcing plugin

This commit is contained in:
leejt
2020-09-25 18:46:17 -07:00
committed by Adam
parent a8846cfef8
commit deb46aedd6
20 changed files with 1500 additions and 0 deletions

View File

@@ -0,0 +1,99 @@
/*
* Copyright (c) 2019, Weird Gloop <admin@weirdgloop.org>
* 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.crowdsourcing;
import com.google.gson.Gson;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.http.api.RuneLiteAPI;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
@Slf4j
@Singleton
public class CrowdsourcingManager
{
private static final String CROWDSOURCING_BASE = "https://crowdsource.runescape.wiki/runelite";
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
private static final Gson GSON = RuneLiteAPI.GSON;
@Inject
private OkHttpClient okHttpClient;
private List<Object> data = new ArrayList<>();
public void storeEvent(Object event)
{
synchronized (this)
{
data.add(event);
}
}
protected void submitToAPI()
{
List<Object> temp;
synchronized (this)
{
if (data.isEmpty())
{
return;
}
temp = data;
data = new ArrayList<>();
}
Request r = new Request.Builder()
.url(CROWDSOURCING_BASE)
.post(RequestBody.create(JSON, GSON.toJson(temp)))
.build();
okHttpClient.newCall(r).enqueue(new Callback()
{
@Override
public void onFailure(Call call, IOException e)
{
log.debug("Error sending crowdsourcing data", e);
}
@Override
public void onResponse(Call call, Response response)
{
log.debug("Successfully sent crowdsourcing data");
response.close();
}
});
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (c) 2019, Weird Gloop <admin@weirdgloop.org>
* 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.crowdsourcing;
import java.time.temporal.ChronoUnit;
import javax.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.crowdsourcing.cooking.CrowdsourcingCooking;
import net.runelite.client.plugins.crowdsourcing.dialogue.CrowdsourcingDialogue;
import net.runelite.client.plugins.crowdsourcing.movement.CrowdsourcingMovement;
import net.runelite.client.plugins.crowdsourcing.music.CrowdsourcingMusic;
import net.runelite.client.plugins.crowdsourcing.thieving.CrowdsourcingThieving;
import net.runelite.client.plugins.crowdsourcing.woodcutting.CrowdsourcingWoodcutting;
import net.runelite.client.plugins.crowdsourcing.zmi.CrowdsourcingZMI;
import net.runelite.client.task.Schedule;
@Slf4j
@PluginDescriptor(
name = "OSRS Wiki Crowdsourcing",
description = "Send data to the wiki to help figure out skilling success rates, burn rates, more. See osrs.wiki/RS:CROWD"
)
public class CrowdsourcingPlugin extends Plugin
{
// Number of seconds to wait between trying to send data to the wiki.
private static final int SECONDS_BETWEEN_UPLOADS = 300;
@Inject
private EventBus eventBus;
@Inject
private CrowdsourcingManager manager;
@Inject
private CrowdsourcingCooking cooking;
@Inject
private CrowdsourcingDialogue dialogue;
@Inject
private CrowdsourcingMovement movement;
@Inject
private CrowdsourcingMusic music;
@Inject
private CrowdsourcingThieving thieving;
@Inject
private CrowdsourcingWoodcutting woodcutting;
@Inject
private CrowdsourcingZMI zmi;
@Override
protected void startUp() throws Exception
{
eventBus.register(cooking);
eventBus.register(dialogue);
eventBus.register(movement);
eventBus.register(music);
eventBus.register(thieving);
eventBus.register(woodcutting);
eventBus.register(zmi);
}
@Override
protected void shutDown() throws Exception
{
eventBus.unregister(cooking);
eventBus.unregister(dialogue);
eventBus.unregister(movement);
eventBus.unregister(music);
eventBus.unregister(thieving);
eventBus.unregister(woodcutting);
eventBus.unregister(zmi);
}
@Schedule(
period = SECONDS_BETWEEN_UPLOADS,
unit = ChronoUnit.SECONDS,
asynchronous = true
)
public void submitToAPI()
{
manager.submitToAPI();
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2019, Weird Gloop <admin@weirdgloop.org>
* 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.crowdsourcing.cooking;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class CookingData
{
private final String message;
private final boolean hasCookingGauntlets;
private final boolean inHosidiusKitchen;
private final boolean kourendElite;
private final int lastGameObjectClicked;
private final int level;
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (c) 2019, Weird Gloop <admin@weirdgloop.org>
* 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.crowdsourcing.cooking;
import javax.inject.Inject;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.InventoryID;
import net.runelite.api.ItemContainer;
import net.runelite.api.ItemID;
import net.runelite.api.MenuAction;
import net.runelite.api.Player;
import net.runelite.api.Skill;
import net.runelite.api.Varbits;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.plugins.crowdsourcing.CrowdsourcingManager;
public class CrowdsourcingCooking
{
private static final int HOSIDIUS_KITCHEN_REGION = 6712;
@Inject
private CrowdsourcingManager manager;
@Inject
private Client client;
private int lastGameObjectClicked;
private boolean hasCookingGauntlets()
{
ItemContainer equipmentContainer = client.getItemContainer(InventoryID.EQUIPMENT);
if (equipmentContainer == null)
{
return false;
}
return equipmentContainer.contains(ItemID.COOKING_GAUNTLETS);
}
@Subscribe
public void onChatMessage(ChatMessage event)
{
if (event.getType() != ChatMessageType.SPAM)
{
return;
}
final String message = event.getMessage();
// Message prefixes taken from CookingPlugin
if (message.startsWith("You successfully cook")
|| message.startsWith("You successfully bake")
|| message.startsWith("You manage to cook")
|| message.startsWith("You roast a")
|| message.startsWith("You cook")
|| message.startsWith("You accidentally burn")
|| message.startsWith("You accidentally spoil"))
{
boolean inHosidiusKitchen = false;
Player local = client.getLocalPlayer();
if (local != null && local.getWorldLocation().getRegionID() == HOSIDIUS_KITCHEN_REGION)
{
inHosidiusKitchen = true;
}
int cookingLevel = client.getBoostedSkillLevel(Skill.COOKING);
boolean hasCookingGauntlets = hasCookingGauntlets();
boolean kourendElite = client.getVar(Varbits.DIARY_KOUREND_ELITE) == 1;
CookingData data = new CookingData(message, hasCookingGauntlets, inHosidiusKitchen, kourendElite, lastGameObjectClicked, cookingLevel);
manager.storeEvent(data);
}
}
@Subscribe
public void onMenuOptionClicked(MenuOptionClicked menuOptionClicked)
{
MenuAction action = menuOptionClicked.getMenuAction();
if (action == MenuAction.ITEM_USE_ON_GAME_OBJECT
|| action == MenuAction.GAME_OBJECT_FIRST_OPTION
|| action == MenuAction.GAME_OBJECT_SECOND_OPTION
|| action == MenuAction.GAME_OBJECT_THIRD_OPTION
|| action == MenuAction.GAME_OBJECT_FOURTH_OPTION
|| action == MenuAction.GAME_OBJECT_FIFTH_OPTION)
{
lastGameObjectClicked = menuOptionClicked.getId();
}
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (c) 2019, Weird Gloop <admin@weirdgloop.org>
* 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.crowdsourcing.dialogue;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.events.GameTick;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetID;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.plugins.crowdsourcing.CrowdsourcingManager;
public class CrowdsourcingDialogue
{
private static final String USERNAME_TOKEN = "%USERNAME%";
@Inject
private Client client;
@Inject
private CrowdsourcingManager manager;
private String lastNpcDialogueText = null;
private String lastPlayerDialogueText = null;
private Widget[] dialogueOptions;
private String sanitize(String dialogue)
{
String username = client.getLocalPlayer().getName();
return dialogue.replaceAll(username, USERNAME_TOKEN);
}
@Subscribe
public void onGameTick(GameTick tick)
{
Widget npcDialogueTextWidget = client.getWidget(WidgetInfo.DIALOG_NPC_TEXT);
if (npcDialogueTextWidget != null && !npcDialogueTextWidget.getText().equals(lastNpcDialogueText))
{
lastNpcDialogueText = npcDialogueTextWidget.getText();
String npcName = client.getWidget(WidgetInfo.DIALOG_NPC_NAME).getText();
NpcDialogueData data = new NpcDialogueData(sanitize(lastNpcDialogueText), npcName);
manager.storeEvent(data);
}
Widget playerDialogueTextWidget = client.getWidget(WidgetID.DIALOG_PLAYER_GROUP_ID, 4);
if (playerDialogueTextWidget != null && !playerDialogueTextWidget.getText().equals(lastPlayerDialogueText))
{
lastPlayerDialogueText = playerDialogueTextWidget.getText();
PlayerDialogueData data = new PlayerDialogueData(sanitize(lastPlayerDialogueText));
manager.storeEvent(data);
}
Widget playerDialogueOptionsWidget = client.getWidget(WidgetID.DIALOG_OPTION_GROUP_ID, 1);
if (playerDialogueOptionsWidget != null && playerDialogueOptionsWidget.getChildren() != dialogueOptions)
{
dialogueOptions = playerDialogueOptionsWidget.getChildren();
String[] optionsText = new String[dialogueOptions.length];
for (int i = 0; i < dialogueOptions.length; i++)
{
optionsText[i] = sanitize(dialogueOptions[i].getText());
}
DialogueOptionsData data = new DialogueOptionsData(optionsText);
manager.storeEvent(data);
}
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2019, Weird Gloop <admin@weirdgloop.org>
* 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.crowdsourcing.dialogue;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class DialogueOptionsData
{
private final String[] options;
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2019, Weird Gloop <admin@weirdgloop.org>
* 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.crowdsourcing.dialogue;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class NpcDialogueData
{
private final String message;
private final String name;
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2019, Weird Gloop <admin@weirdgloop.org>
* 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.crowdsourcing.dialogue;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class PlayerDialogueData
{
private final String message;
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright (c) 2019, Weird Gloop <admin@weirdgloop.org>
* 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.crowdsourcing.movement;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.MenuAction;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.plugins.crowdsourcing.CrowdsourcingManager;
public class CrowdsourcingMovement
{
@Inject
private Client client;
@Inject
private CrowdsourcingManager manager;
private WorldPoint lastPoint;
private int ticksStill;
private boolean lastIsInInstance;
private MenuOptionClicked lastClick;
@Subscribe
public void onMenuOptionClicked(MenuOptionClicked menuOptionClicked)
{
if (menuOptionClicked.getMenuAction() != MenuAction.WALK
&& !menuOptionClicked.getMenuOption().equals("Message"))
{
lastClick = menuOptionClicked;
}
}
@Subscribe
public void onGameTick(GameTick tick)
{
LocalPoint local = LocalPoint.fromWorld(client, client.getLocalPlayer().getWorldLocation());
WorldPoint nextPoint = WorldPoint.fromLocalInstance(client, local);
boolean nextIsInInstance = client.isInInstancedRegion();
if (lastPoint != null)
{
int distance = nextPoint.distanceTo(lastPoint);
if (distance > 2 || nextIsInInstance != lastIsInInstance)
{
MovementData data = new MovementData(lastPoint, nextPoint, lastIsInInstance, nextIsInInstance, ticksStill, lastClick);
manager.storeEvent(data);
}
if (distance > 0)
{
ticksStill = 0;
}
}
ticksStill++;
lastPoint = nextPoint;
lastIsInInstance = nextIsInInstance;
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2019, Weird Gloop <admin@weirdgloop.org>
* 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.crowdsourcing.movement;
import lombok.AllArgsConstructor;
import lombok.Data;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.MenuOptionClicked;
@Data
@AllArgsConstructor
public class MovementData
{
private final WorldPoint start;
private final WorldPoint end;
private final boolean fromInstance;
private final boolean toInstance;
private final int ticks;
private MenuOptionClicked lastClick;
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) 2019, Weird Gloop <admin@weirdgloop.org>
* 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.crowdsourcing.music;
import javax.inject.Inject;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.ChatMessage;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.plugins.crowdsourcing.CrowdsourcingManager;
public class CrowdsourcingMusic
{
private static final String MUSIC_UNLOCK_MESSAGE = "You have unlocked a new music track:";
@Inject
private Client client;
@Inject
private ClientThread clientThread;
@Inject
private CrowdsourcingManager manager;
@Subscribe
public void onChatMessage(ChatMessage event)
{
if (event.getType() == ChatMessageType.GAMEMESSAGE)
{
String message = event.getMessage();
if (message.contains(MUSIC_UNLOCK_MESSAGE))
{
// Need to delay this by a tick because the location is not set until after the message
clientThread.invokeLater(() ->
{
LocalPoint local = LocalPoint.fromWorld(client, client.getLocalPlayer().getWorldLocation());
WorldPoint location = WorldPoint.fromLocalInstance(client, local);
boolean isInInstance = client.isInInstancedRegion();
MusicUnlockData data = new MusicUnlockData(location, isInInstance, message);
manager.storeEvent(data);
});
}
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2019, Weird Gloop <admin@weirdgloop.org>
* 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.crowdsourcing.music;
import lombok.AllArgsConstructor;
import lombok.Data;
import net.runelite.api.coords.WorldPoint;
@Data
@AllArgsConstructor
public class MusicUnlockData
{
private final WorldPoint location;
private final boolean isInInstance;
private final String message;
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2019, Weird Gloop <admin@weirdgloop.org>
* 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.crowdsourcing.skilling;
public enum SkillingEndReason
{
DEPLETED,
INVENTORY_FULL,
INTERRUPTED
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2019, Weird Gloop <admin@weirdgloop.org>
* 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.crowdsourcing.skilling;
public enum SkillingState
{
READY,
CLICKED,
CUTTING,
RECOVERING
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (c) 2019, Weird Gloop <admin@weirdgloop.org>
* 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.crowdsourcing.thieving;
import java.util.regex.Pattern;
import javax.inject.Inject;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.InventoryID;
import net.runelite.api.ItemContainer;
import net.runelite.api.ItemID;
import net.runelite.api.NPC;
import net.runelite.api.Skill;
import net.runelite.api.Varbits;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.plugins.crowdsourcing.CrowdsourcingManager;
public class CrowdsourcingThieving
{
private static final String BLACKJACK_SUCCESS = "You smack the bandit over the head and render them unconscious.";
private static final String BLACKJACK_FAIL = "Your blow only glances off the bandit's head.";
private static final Pattern PICKPOCKET_SUCCESS = Pattern.compile("You pick .*'s pocket\\.");
private static final Pattern PICKPOCKET_FAIL = Pattern.compile("You fail to pick .*'s pocket\\.");
@Inject
private Client client;
@Inject
private CrowdsourcingManager manager;
private int lastPickpocketTarget;
private boolean hasGlovesOfSilence()
{
ItemContainer equipmentContainer = client.getItemContainer(InventoryID.EQUIPMENT);
if (equipmentContainer == null)
{
return false;
}
return equipmentContainer.contains(ItemID.GLOVES_OF_SILENCE);
}
private boolean hasThievingCape()
{
ItemContainer equipmentContainer = client.getItemContainer(InventoryID.EQUIPMENT);
if (equipmentContainer == null)
{
return false;
}
return equipmentContainer.contains(ItemID.THIEVING_CAPE) ||
equipmentContainer.contains(ItemID.THIEVING_CAPET) ||
equipmentContainer.contains(ItemID.MAX_CAPE);
}
private int getArdougneDiary()
{
int easy = client.getVar(Varbits.DIARY_ARDOUGNE_EASY);
int medium = client.getVar(Varbits.DIARY_ARDOUGNE_MEDIUM);
int hard = client.getVar(Varbits.DIARY_ARDOUGNE_HARD);
int elite = client.getVar(Varbits.DIARY_ARDOUGNE_ELITE);
return easy + 2 * medium + 4 * hard + 8 * elite;
}
@Subscribe
public void onChatMessage(ChatMessage event)
{
if (event.getType() != ChatMessageType.SPAM)
{
return;
}
String message = event.getMessage();
if (BLACKJACK_SUCCESS.equals(message)
|| BLACKJACK_FAIL.equals(message)
|| PICKPOCKET_FAIL.matcher(message).matches()
|| PICKPOCKET_SUCCESS.matcher(message).matches())
{
WorldPoint location = client.getLocalPlayer().getWorldLocation();
int ardougneDiary = getArdougneDiary();
boolean silence = hasGlovesOfSilence();
boolean thievingCape = hasThievingCape();
int thievingLevel = client.getBoostedSkillLevel(Skill.THIEVING);
PickpocketData data = new PickpocketData(thievingLevel, lastPickpocketTarget, message, location, silence, thievingCape, ardougneDiary);
manager.storeEvent(data);
}
}
@Subscribe
public void onMenuOptionClicked(MenuOptionClicked event)
{
if (event.getMenuOption().equals("Pickpocket") || event.getMenuOption().equals("Knock-Out"))
{
NPC[] cachedNPCs = client.getCachedNPCs();
NPC npc = cachedNPCs[event.getId()];
lastPickpocketTarget = npc.getId();
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2019, Weird Gloop <admin@weirdgloop.org>
* 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.crowdsourcing.thieving;
import lombok.AllArgsConstructor;
import lombok.Data;
import net.runelite.api.coords.WorldPoint;
@Data
@AllArgsConstructor
public class PickpocketData
{
private final int level;
private final int target;
private final String message;
private final WorldPoint location;
private final boolean silence;
private final boolean thievingCape;
private final int ardougneDiary;
}

View File

@@ -0,0 +1,232 @@
/*
* Copyright (c) 2019, Weird Gloop <admin@weirdgloop.org>
* 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.crowdsourcing.woodcutting;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import net.runelite.api.AnimationID;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.ItemID;
import net.runelite.api.MenuAction;
import net.runelite.api.ObjectID;
import net.runelite.api.Skill;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.GameObjectDespawned;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.plugins.crowdsourcing.CrowdsourcingManager;
import net.runelite.client.plugins.crowdsourcing.skilling.SkillingEndReason;
import net.runelite.client.plugins.crowdsourcing.skilling.SkillingState;
public class CrowdsourcingWoodcutting
{
private static final String CHOPPING_MESSAGE = "You swing your axe at the tree.";
private static final String INVENTORY_FULL_MESSAGE = "Your inventory is too full to hold any more logs.";
private static final String NEST_MESSAGE = "A bird's nest falls out of the tree";
private static final Set<Integer> TREE_OBJECTS = new ImmutableSet.Builder<Integer>().
add(ObjectID.OAK_10820).
add(ObjectID.YEW).
add(ObjectID.TREE).
add(ObjectID.TREE_1278).
add(ObjectID.WILLOW).
add(ObjectID.WILLOW_10829).
add(ObjectID.WILLOW_10831).
add(ObjectID.WILLOW_10833).
add(ObjectID.SCRAPEY_TREE).
add(ObjectID.JUNGLE_TREE_15951).
add(ObjectID.JUNGLE_TREE_15954).
add(ObjectID.JUNGLE_TREE_15948).
add(ObjectID.MAPLE_TREE_10832).
add(ObjectID.MAHOGANY).
add(ObjectID.TEAK).
add(ObjectID.MAGIC_TREE_10834).
add(ObjectID.HOLLOW_TREE_10821).
add(ObjectID.HOLLOW_TREE_10830).
add(ObjectID.ACHEY_TREE).
build();
private static final Map<Integer, Integer> AXE_ANIMS = new ImmutableMap.Builder<Integer, Integer>().
put(AnimationID.WOODCUTTING_BRONZE, ItemID.BRONZE_AXE).
put(AnimationID.WOODCUTTING_IRON, ItemID.IRON_AXE).
put(AnimationID.WOODCUTTING_STEEL, ItemID.STEEL_AXE).
put(AnimationID.WOODCUTTING_BLACK, ItemID.BLACK_AXE).
put(AnimationID.WOODCUTTING_MITHRIL, ItemID.MITHRIL_AXE).
put(AnimationID.WOODCUTTING_ADAMANT, ItemID.ADAMANT_AXE).
put(AnimationID.WOODCUTTING_RUNE, ItemID.RUNE_AXE).
put(AnimationID.WOODCUTTING_DRAGON, ItemID.DRAGON_AXE).
put(AnimationID.WOODCUTTING_INFERNAL, ItemID.INFERNAL_AXE).
put(AnimationID.WOODCUTTING_3A_AXE, ItemID._3RD_AGE_AXE).
put(AnimationID.WOODCUTTING_CRYSTAL, ItemID.CRYSTAL_AXE).build();
private static final Set<String> SUCCESS_MESSAGES = new ImmutableSet.Builder<String>().
add("You get some logs.").
add("You get some oak logs.").
add("You get some willow logs.").
add("You get some teak logs.").
add("You get some teak logs and give them to Carpenter Kjallak.").
add("You get some maple logs.").
add("You get some maple logs and give them to Lumberjack Leif.").
add("You get some mahogany logs.").
add("You get some mahogany logs and give them to Carpenter Kjallak.").
add("You get some yew logs.").
add("You get some magic logs.").
add("You get some scrapey tree logs.").
add("You get some bark.").
build();
@Inject
private CrowdsourcingManager manager;
@Inject
private Client client;
private SkillingState state = SkillingState.RECOVERING;
private int lastExperimentEnd = 0;
private WorldPoint treeLocation;
private int treeId;
private int startTick;
private int axe;
private List<Integer> chopTicks = new ArrayList<>();
private List<Integer> nestTicks = new ArrayList<>();
private void endExperiment(SkillingEndReason reason)
{
if (state == SkillingState.CUTTING)
{
int endTick = client.getTickCount();
int woodcuttingLevel = client.getBoostedSkillLevel(Skill.WOODCUTTING);
WoodcuttingData data = new WoodcuttingData(
woodcuttingLevel,
startTick,
endTick,
chopTicks,
nestTicks,
axe,
treeId,
treeLocation,
reason
);
manager.storeEvent(data);
chopTicks = new ArrayList<>();
nestTicks = new ArrayList<>();
}
state = SkillingState.RECOVERING;
lastExperimentEnd = client.getTickCount();
}
@Subscribe
public void onChatMessage(ChatMessage event)
{
final String message = event.getMessage();
final ChatMessageType type = event.getType();
if (state == SkillingState.CLICKED && type == ChatMessageType.SPAM && message.equals(CHOPPING_MESSAGE))
{
startTick = client.getTickCount();
state = SkillingState.CUTTING;
}
else if (state == SkillingState.CUTTING && type == ChatMessageType.SPAM && SUCCESS_MESSAGES.contains(message))
{
chopTicks.add(client.getTickCount());
}
else if (state == SkillingState.CUTTING && type == ChatMessageType.GAMEMESSAGE && message.equals(INVENTORY_FULL_MESSAGE))
{
endExperiment(SkillingEndReason.INVENTORY_FULL);
}
else if (state == SkillingState.CUTTING && type == ChatMessageType.GAMEMESSAGE && message.contains(NEST_MESSAGE))
{
nestTicks.add(client.getTickCount());
}
}
@Subscribe
public void onGameTick(GameTick tick)
{
int animId = client.getLocalPlayer().getAnimation();
if (state == SkillingState.CUTTING)
{
if (AXE_ANIMS.containsKey(animId))
{
axe = AXE_ANIMS.get(animId);
}
else
{
endExperiment(SkillingEndReason.INTERRUPTED);
}
}
else if (animId != -1)
{
endExperiment(SkillingEndReason.INTERRUPTED);
}
else if (state == SkillingState.RECOVERING && client.getTickCount() - lastExperimentEnd >= 4)
{
state = SkillingState.READY;
}
else if (state == SkillingState.CLICKED && client.getTickCount() - lastExperimentEnd >= 20)
{
state = SkillingState.READY;
}
}
@Subscribe
public void onMenuOptionClicked(MenuOptionClicked menuOptionClicked)
{
MenuAction menuAction = menuOptionClicked.getMenuAction();
int id = menuOptionClicked.getId();
if (state == SkillingState.READY && menuAction == MenuAction.GAME_OBJECT_FIRST_OPTION && TREE_OBJECTS.contains(id))
{
state = SkillingState.CLICKED;
lastExperimentEnd = client.getTickCount();
treeId = id;
treeLocation = WorldPoint.fromScene(client, menuOptionClicked.getActionParam(), menuOptionClicked.getWidgetId(), client.getPlane());
}
else
{
endExperiment(SkillingEndReason.INTERRUPTED);
}
}
@Subscribe
public void onGameObjectDespawned(GameObjectDespawned event)
{
if (state != SkillingState.CUTTING)
{
return;
}
if (treeId == event.getGameObject().getId() && treeLocation.equals(event.getTile().getWorldLocation()))
{
endExperiment(SkillingEndReason.DEPLETED);
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2019, Weird Gloop <admin@weirdgloop.org>
* 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.crowdsourcing.woodcutting;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.plugins.crowdsourcing.skilling.SkillingEndReason;
@Data
@AllArgsConstructor
public class WoodcuttingData
{
private final int level;
private final int startTick;
private final int endTick;
private final List<Integer> chopTicks;
private final List<Integer> nestTicks;
private final int axe;
private final int treeId;
private final WorldPoint treeLocation;
private final SkillingEndReason reason;
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright (c) 2019, Weird Gloop <admin@weirdgloop.org>
* 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.crowdsourcing.zmi;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import com.google.common.collect.Multisets;
import java.util.Arrays;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.InventoryID;
import net.runelite.api.ItemContainer;
import net.runelite.api.MenuAction;
import net.runelite.api.Skill;
import net.runelite.api.Varbits;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.ItemContainerChanged;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.events.StatChanged;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.plugins.crowdsourcing.CrowdsourcingManager;
public class CrowdsourcingZMI
{
private static final String CHAT_MESSAGE_ZMI = "You bind the temple's power into runes.";
@Inject
private CrowdsourcingManager manager;
@Inject
private Client client;
private int gameTickZMI = -1;
private int illegalActionTick = -1;
private int previousRunecraftXp = 0;
private int runecraftXpGained = 0;
private Multiset<Integer> previousInventorySnapshot;
private Multiset<Integer> getInventorySnapshot()
{
final ItemContainer inventory = client.getItemContainer(InventoryID.INVENTORY);
Multiset<Integer> inventorySnapshot = HashMultiset.create();
if (inventory != null)
{
Arrays.stream(inventory.getItems())
.forEach(item -> inventorySnapshot.add(item.getId(), item.getQuantity()));
}
return inventorySnapshot;
}
@Subscribe
public void onMenuOptionClicked(MenuOptionClicked menuOptionClicked)
{
MenuAction action = menuOptionClicked.getMenuAction();
switch (action)
{
case ITEM_FIRST_OPTION:
case ITEM_SECOND_OPTION:
case ITEM_THIRD_OPTION:
case ITEM_FOURTH_OPTION:
case ITEM_FIFTH_OPTION:
case GROUND_ITEM_FIRST_OPTION:
case GROUND_ITEM_SECOND_OPTION:
case GROUND_ITEM_THIRD_OPTION:
case GROUND_ITEM_FOURTH_OPTION:
case GROUND_ITEM_FIFTH_OPTION:
case ITEM_DROP:
illegalActionTick = client.getTickCount();
break;
}
}
@Subscribe
public void onChatMessage(ChatMessage chatMessage)
{
if (chatMessage.getMessage().equals(CHAT_MESSAGE_ZMI))
{
gameTickZMI = client.getTickCount();
previousRunecraftXp = client.getSkillExperience(Skill.RUNECRAFT);
previousInventorySnapshot = getInventorySnapshot();
}
}
@Subscribe
public void onStatChanged(StatChanged statChanged)
{
if (gameTickZMI == client.getTickCount())
{
int currentRunecraftXp = statChanged.getXp();
runecraftXpGained = currentRunecraftXp - previousRunecraftXp;
}
}
@Subscribe
public void onItemContainerChanged(ItemContainerChanged event)
{
int itemContainerChangedTick = client.getTickCount();
if (event.getItemContainer() != client.getItemContainer(InventoryID.INVENTORY) || gameTickZMI != itemContainerChangedTick)
{
return;
}
int tickDelta = itemContainerChangedTick - illegalActionTick;
boolean ardougneMedium = client.getVar(Varbits.DIARY_ARDOUGNE_MEDIUM) == 1;
int runecraftBoostedLevel = client.getBoostedSkillLevel(Skill.RUNECRAFT);
Multiset<Integer> currentInventorySnapshot = getInventorySnapshot();
final Multiset<Integer> itemsReceived = Multisets.difference(currentInventorySnapshot, previousInventorySnapshot);
final Multiset<Integer> itemsRemoved = Multisets.difference(previousInventorySnapshot, currentInventorySnapshot);
ZMIData data = new ZMIData(tickDelta, ardougneMedium, runecraftBoostedLevel, runecraftXpGained, itemsReceived, itemsRemoved);
manager.storeEvent(data);
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2019, Weird Gloop <admin@weirdgloop.org>
* 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.crowdsourcing.zmi;
import com.google.common.collect.Multiset;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class ZMIData
{
private final int tickDelta;
private final boolean ardougneMedium;
private final int level;
private final int xpGained;
private final Multiset<Integer> itemsReceived;
private final Multiset<Integer> itemsRemoved;
}