Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add helper for the Crack The Clue II generic quest and Add Emote Sequences #1949

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
/*
* Copyright (c) 2024, Zoinkwiz <https://github.com/Zoinkwiz>
* 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 com.questhelper.helpers.mischelpers.cracktheclueii;

import com.questhelper.panel.PanelDetails;
import com.questhelper.questhelpers.BasicQuestHelper;
import com.questhelper.requirements.widget.WidgetTextRequirement;
import com.questhelper.requirements.ChatMessageRequirement;
import com.questhelper.requirements.zone.ZoneRequirement;
import com.questhelper.requirements.item.ItemRequirement;
import com.questhelper.requirements.zone.Zone;
import com.questhelper.rewards.ItemReward;
import com.questhelper.steps.DetailedQuestStep;
import com.questhelper.steps.emote.QuestEmote;
import com.questhelper.steps.ConditionalStep;
import com.questhelper.steps.EmoteStep;
import com.questhelper.steps.QuestStep;
import com.questhelper.steps.DigStep;
import java.util.Collections;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import net.runelite.api.ItemID;
import net.runelite.api.SpriteID;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.widgets.ComponentID;
import static com.questhelper.requirements.util.LogicHelper.nand;

public class CrackTheClueII extends BasicQuestHelper
{
ItemRequirement spade, pieDish, rawHerring, goblinMail, plainPizza, woodenShield, cheese;

DetailedQuestStep week1Dig, week2Dig, week3Emotes, week4Dig, finalEmotes;

ConditionalStep week1Steps, week2Steps, week3Steps, week4Steps, finalSteps;

WidgetTextRequirement week1Message, week2Message, week4Message, finalMessage;

ChatMessageRequirement week3Message;

Zone week1Zone, week2Zone, week3Zone, week4Zone, finalZone;

ZoneRequirement inWeek1Zone, inWeek2Zone, inWeek3Zone, inWeek4Zone, inFinalZone;

@Override
public Map<Integer, QuestStep> loadSteps()
{
setupRequirements();
setupZones();
setupConditions();
setupSteps();

Map<Integer, QuestStep> steps = new HashMap<>();

week1Steps = new ConditionalStep(this, week1Dig);
week1Steps.addStep(week1Message, week1Dig);
week1Steps.setLockingCondition(week1Message);
week2Steps = new ConditionalStep(this, week2Dig);
week2Steps.addStep(week2Message, week2Dig);
week2Steps.setLockingCondition(week2Message);
week3Steps = new ConditionalStep(this, week3Emotes);
week3Steps.addStep(week3Message, week3Emotes);
week3Steps.setLockingCondition(week3Message);
week4Steps = new ConditionalStep(this, week4Dig);
week4Steps.addStep(week4Message, week4Dig);
week4Steps.setLockingCondition(week4Message);
finalSteps = new ConditionalStep(this, finalEmotes);
finalSteps.addStep(finalMessage, finalEmotes);
finalSteps.setLockingCondition(finalMessage);

ConditionalStep allSteps = new ConditionalStep(this, week1Steps);
allSteps.addStep(nand(inWeek1Zone,week1Message),week1Steps);
allSteps.addStep(nand(inWeek2Zone, week2Message), week2Steps);
allSteps.addStep(nand(inWeek3Zone, week3Message), week3Steps);
allSteps.addStep(nand(inWeek4Zone, week4Message), week4Steps);
allSteps.addStep(nand(inFinalZone, finalMessage), finalSteps);

steps.put(0, allSteps);

return steps;
}

public void setupConditions()
{
inWeek1Zone = new ZoneRequirement(week1Zone);
inWeek2Zone = new ZoneRequirement(week2Zone);
inWeek3Zone = new ZoneRequirement(week3Zone);
inWeek4Zone = new ZoneRequirement(week4Zone);
inFinalZone = new ZoneRequirement(finalZone);
}

@Override
public void setupRequirements()
{
spade = new ItemRequirement("Spade", ItemID.SPADE).isNotConsumed();
pieDish = new ItemRequirement("Pie dish", ItemID.PIE_DISH).isNotConsumed();
rawHerring = new ItemRequirement("Raw herring", ItemID.RAW_HERRING).isNotConsumed();
goblinMail = new ItemRequirement("Goblin mail", ItemID.GOBLIN_MAIL).isNotConsumed();
plainPizza = new ItemRequirement("Plain pizza", ItemID.PLAIN_PIZZA).isNotConsumed();
woodenShield = new ItemRequirement("Wooden shield", ItemID.WOODEN_SHIELD).isNotConsumed();
cheese = new ItemRequirement("Cheese", ItemID.CHEESE).isNotConsumed();
}

public void setupZones()
{
week1Zone = new Zone(new WorldPoint(2977, 3193, 0), new WorldPoint(2979, 3195, 0));
week2Zone = new Zone(new WorldPoint(2990, 3294, 0), new WorldPoint(2992, 3296, 0));
week3Zone = new Zone(new WorldPoint(3034, 3517, 0), new WorldPoint(3036, 3519, 0));
week4Zone = new Zone(new WorldPoint(3234, 3630, 0), new WorldPoint(3236, 3632, 0));
finalZone = new Zone(new WorldPoint(3245, 3361, 0), new WorldPoint(3247, 3363, 0));
}

public void setupSteps()
{
week1Dig = new DigStep(this, new WorldPoint(2978, 3194, 0),
"Dig south-east of Rimmington and north-west of the chapel.", pieDish);
week1Message = new WidgetTextRequirement(ComponentID.DIALOG_SPRITE_TEXT, true, "You find some beautifully ornate gloves and boots.");

week2Dig = new DigStep(this, new WorldPoint(2991, 3295, 0),
"Dig by the entrance to the Air Altar south of Falador.", rawHerring);
week2Message = new WidgetTextRequirement(ComponentID.DIALOG_SPRITE_TEXT, true, "You find some beautifully ornate leg armour.");

List<QuestEmote> week3Steps = Arrays.asList(QuestEmote.SHRUG, QuestEmote.CHEER);
week3Emotes = new EmoteStep(this, week3Steps, new WorldPoint(3035, 3518, 0),
"Perform the shrug emote followed by cheer emote east of the Black Knights' Fortress.");
week3Emotes.addTileMarker(new WorldPoint(3035, 3518, 0), SpriteID.TAB_EMOTES);
week3Message = new ChatMessageRequirement("Some beautifully ornate armour mysteriously appears.");

week4Dig = new DigStep(this, new WorldPoint(3235, 3631, 0),
"Dig outside the Chaos Temple in the Wilderness.", goblinMail);
week4Message = new WidgetTextRequirement(ComponentID.DIALOG_SPRITE_TEXT, true, "You find a beautifully ornate cape.");

List<QuestEmote> finalSteps = Arrays.asList(QuestEmote.BOW, QuestEmote.YES, QuestEmote.CLAP);
finalEmotes = new EmoteStep(this, finalSteps, new WorldPoint(3246, 3362, 0),
"Perform the bow emote, then yes emote, then clap emote between the trees south of Varrock, east of the stone circle. " +
"Have only the required items in your inventory/equipped.",
plainPizza, woodenShield, cheese);
finalEmotes.addTileMarker(new WorldPoint(3246, 3362, 0), SpriteID.TAB_EMOTES);
finalMessage = new WidgetTextRequirement(ComponentID.DIALOG_NPC_TEXT, true, "Here, take this. But tell no one I was here.");
}

@Override
public List<ItemRequirement> getItemRequirements()
{
return Arrays.asList(spade, pieDish, rawHerring, goblinMail, plainPizza, woodenShield, cheese);
}

@Override
public List<ItemReward> getItemRewards()
{
return Arrays.asList(
new ItemReward("Ornate gloves", ItemID.ORNATE_GLOVES, 1),
new ItemReward("Ornate boots", ItemID.ORNATE_BOOTS, 1),
new ItemReward("Ornate legs", ItemID.ORNATE_LEGS, 1),
new ItemReward("Ornate top", ItemID.ORNATE_TOP, 1),
new ItemReward("Ornate cape", ItemID.ORNATE_CAPE, 1),
new ItemReward("Ornate helm", ItemID.ORNATE_HELM, 1)
);
}

@Override
public List<PanelDetails> getPanels()
{
List<PanelDetails> allSteps = new ArrayList<>();

PanelDetails week1Panel = new PanelDetails("Week 1", Collections.singletonList(week1Dig),
Arrays.asList(spade, pieDish));
week1Panel.setLockingStep(week1Steps);
allSteps.add(week1Panel);

PanelDetails week2Panel = new PanelDetails("Week 2", Collections.singletonList(week2Dig),
Arrays.asList(spade, rawHerring));
week2Panel.setLockingStep(week2Steps);
allSteps.add(week2Panel);

PanelDetails week3Panel = new PanelDetails("Week 3", Collections.singletonList(week3Emotes));
week3Panel.setLockingStep(week3Steps);
allSteps.add(week3Panel);

PanelDetails week4Panel = new PanelDetails("Week 4", Collections.singletonList(week4Dig),
Arrays.asList(spade, goblinMail));
week4Panel.setLockingStep(week4Steps);
allSteps.add(week4Panel);

PanelDetails finalPanel = new PanelDetails("Final Clue",
Collections.singletonList(finalEmotes),
Arrays.asList(plainPizza, woodenShield, cheese));
finalPanel.setLockingStep(finalSteps);
allSteps.add(finalPanel);

return allSteps;
}
}
3 changes: 3 additions & 0 deletions src/main/java/com/questhelper/questinfo/QuestHelperQuest.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
import com.questhelper.helpers.quests.coldwar.ColdWar;
import com.questhelper.helpers.quests.contact.Contact;
import com.questhelper.helpers.quests.cooksassistant.CooksAssistant;
import com.questhelper.helpers.mischelpers.cracktheclueii.CrackTheClueII;
import com.questhelper.helpers.quests.creatureoffenkenstrain.CreatureOfFenkenstrain;
import com.questhelper.helpers.miniquests.curseoftheemptylord.CurseOfTheEmptyLord;
import com.questhelper.helpers.miniquests.daddyshome.DaddysHome;
Expand Down Expand Up @@ -654,6 +655,8 @@ public enum QuestHelperQuest
BARROWS_HELPER(new BarrowsHelper(), "Barrows helper", QuestVarbits.CUTSCENE, -1, QuestDetails.Type.GENERIC, QuestDetails.Difficulty.GENERIC),
STRONGHOLD_OF_SECURITY(new StrongholdOfSecurity(), "Stronghold of Security", QuestVarbits.STRONGHOLD_OF_SECURITY, 1,
QuestDetails.Type.GENERIC, QuestDetails.Difficulty.GENERIC),
CRACK_THE_CLUE_II(new CrackTheClueII(), "Crack The Clue II", QuestVarbits.CRACK_THE_CLUE_II, -1, QuestDetails.Type.GENERIC, QuestDetails.Difficulty.GENERIC),

// Skill
AGILITY(new Agility(), "Agility", Skill.AGILITY, 99, QuestDetails.Type.SKILL_P2P, QuestDetails.Difficulty.SKILL),
WOODCUTTING_MEMBER(new WoodcuttingMember(), "Woodcutting - Member", Skill.WOODCUTTING, 99, QuestDetails.Type.SKILL_P2P, QuestDetails.Difficulty.SKILL),
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/questhelper/questinfo/QuestVarbits.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ public enum QuestVarbits
BALLOON_TRANSPORT_CASTLE_WARS(2869),
BALLOON_TRANSPORT_GRAND_TREE(2870),
STRONGHOLD_OF_SECURITY(2312),
/**
* fake mini-quest varbits, these don't hold the completion value.
*/
CRACK_THE_CLUE_II(0),

// Achievement Diaries

Expand Down
107 changes: 99 additions & 8 deletions src/main/java/com/questhelper/steps/EmoteStep.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,36 +33,126 @@
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import net.runelite.api.Player;
import net.runelite.api.ScriptID;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.AnimationChanged;
import net.runelite.api.widgets.ComponentID;
import net.runelite.api.widgets.Widget;
import net.runelite.client.eventbus.Subscribe;

public class EmoteStep extends DetailedQuestStep
{
private boolean hasScrolled;
private final QuestEmote emote;
private List<QuestEmote> emoteSequence;
private int currentEmoteIndex;
private final WorldPoint requiredLocation;
private static final int LOCATION_TOLERANCE = 1;

public EmoteStep(QuestHelper questHelper, QuestEmote emote, String text, Requirement... requirements)
{
super(questHelper, text, requirements);
this.emote = emote;
this.emoteSequence = new ArrayList<>();
this.emoteSequence.add(emote);
this.currentEmoteIndex = 0;
this.requiredLocation = null;
}

public EmoteStep(QuestHelper questHelper, QuestEmote emote, WorldPoint worldPoint, String text, Requirement... requirements)
{
super(questHelper, worldPoint, text, requirements);
this.emote = emote;
this.emoteSequence = new ArrayList<>();
this.emoteSequence.add(emote);
this.currentEmoteIndex = 0;
this.requiredLocation = worldPoint;
}

public EmoteStep(QuestHelper questHelper, QuestEmote emote, String text, Requirement... requirements)
public EmoteStep(QuestHelper questHelper, List<QuestEmote> emoteSequence, WorldPoint worldPoint, String text, Requirement... requirements)
{
super(questHelper, text, requirements);
this.emote = emote;
super(questHelper, worldPoint, text, requirements);
this.emote = emoteSequence.get(0);
this.emoteSequence = emoteSequence;
this.currentEmoteIndex = 0;
this.requiredLocation = worldPoint;
}

@Subscribe
public void onAnimationChanged(AnimationChanged event)
{
if (!(event.getActor() instanceof Player))
{
return;
}

Player player = (Player) event.getActor();
if (player != client.getLocalPlayer())
{
return;
}

QuestEmote currentEmote = getCurrentEmote();
if (currentEmote != null && player.getAnimation() == currentEmote.getAnimationId())
{
if (isPlayerInCorrectLocation(player))
{
nextEmote();
}
}
}

private boolean isPlayerInCorrectLocation(Player player)
{
if (requiredLocation == null)
{
return true;
}

WorldPoint playerLocation = player.getWorldLocation();
return playerLocation.distanceTo(requiredLocation) <= LOCATION_TOLERANCE;
}

public QuestEmote getCurrentEmote()
{
if (currentEmoteIndex >= emoteSequence.size())
{
return null;
}
return emoteSequence.get(currentEmoteIndex);
}

public void nextEmote()
{
if (currentEmoteIndex < emoteSequence.size() - 1)
{
currentEmoteIndex++;
hasScrolled = false;
}
}

public void resetEmotes()
{
currentEmoteIndex = 0;
hasScrolled = false;
}

@Override
public void makeWidgetOverlayHint(Graphics2D graphics, QuestHelperPlugin plugin)
{
super.makeWidgetOverlayHint(graphics, plugin);

Widget emoteContainer = client.getWidget(ComponentID.EMOTES_EMOTE_CONTAINER);
QuestEmote currentEmote = getCurrentEmote();
if (currentEmote == null)
{
return;
}

if (emoteContainer == null || emoteContainer.isHidden())
Widget emoteContainer = client.getWidget(ComponentID.EMOTES_EMOTE_CONTAINER);
if (emoteContainer == null || emoteContainer.isHidden())
{
return;
}
Expand All @@ -78,7 +168,7 @@ public void makeWidgetOverlayHint(Graphics2D graphics, QuestHelperPlugin plugin)

for (Widget emoteWidget : emoteContainer.getDynamicChildren())
{
if (emoteWidget.getSpriteId() == emote.getSpriteId())
if (emoteWidget.getSpriteId() == currentEmote.getSpriteId())
{
finalEmoteWidget = emoteWidget;
graphics.setColor(new Color(questHelper.getConfig().targetOverlayColor().getRed(),
Expand Down Expand Up @@ -119,9 +209,10 @@ void scrollToWidget(Widget widget)
@Override
protected void setupIcon()
{
if (emote.getSpriteId() != -1 && icon == null)
QuestEmote currentEmote = getCurrentEmote();
if (currentEmote != null && currentEmote.getSpriteId() != -1 && icon == null)
{
BufferedImage emoteImage = spriteManager.getSprite(emote.getSpriteId(), 0);
BufferedImage emoteImage = spriteManager.getSprite(currentEmote.getSpriteId(), 0);
if (emoteImage != null)
{
icon = IconOverlay.createIconImage(emoteImage);
Expand Down
Loading