Skip to content
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
20 changes: 20 additions & 0 deletions JG-example-plugin/JG-example-plugin.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
version = "0.0.1"

project.extra["PluginName"] = "JG Looped Plugin"
project.extra["PluginDescription"] = "First attempt at a torm 2 example"

tasks {
jar {
manifest {
attributes(
mapOf(
"Plugin-Version" to project.version,
"Plugin-Id" to nameToId(project.extra["PluginName"] as String),
"Plugin-Provider" to project.extra["PluginProvider"],
"Plugin-Description" to project.extra["PluginDescription"],
"Plugin-License" to project.extra["PluginLicense"]
)
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package net.storm.plugins.examples.plugin;


import net.storm.api.plugins.SoxExclude;
import net.storm.api.plugins.config.Config;
import net.storm.api.plugins.config.ConfigGroup;
import net.storm.api.plugins.config.ConfigItem;

@ConfigGroup(JGExampleConfig.GROUP)
@SoxExclude // Exclude from obfuscation
public interface JGExampleConfig extends Config {
String GROUP = "JG-example-plugin";

@ConfigItem(
keyName = "npcName",
name = "NPC Name",
description = "Name of the NPC to attack"
)
default String npcName() {
return "Goblin";
}

@ConfigItem(
keyName = "eatFood",
name = "Eat Food?",
description = "Eat food when low hp?"
)
default boolean eatFood() {
return true;
}

@ConfigItem(
keyName = "foodName",
name = "Food Name",
description = "Name of the food to eat"
)
default String foodName() {
return "Lobster";
}

@ConfigItem(
keyName = "foodHp",
name = "Food HP %",
description = "HP percentage to eat food at"
)
default int foodHp() {
return 50;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package net.storm.plugins.examples.plugin;

import net.storm.plugins.examples.plugin.JGExampleConfig;
import com.google.inject.Inject;
import com.google.inject.Provides;
import net.storm.api.plugins.PluginDescriptor;
import net.storm.api.plugins.config.ConfigManager;
import net.storm.sdk.entities.NPCs;
import net.storm.sdk.entities.Players;
import net.storm.sdk.game.Combat;
import net.storm.sdk.items.Inventory;
import net.storm.sdk.plugins.LoopedPlugin;
import org.pf4j.Extension;

/*
* A very basic example of a looped plugin.
*
* Important notes: look at the imports! The class names are similar to RuneLite's API, but they are not the same.
* Always use the Storm SDK's classes when developing plugins.
*
* Ensure that your package names start with net.storm.plugins, or your plugin will not be compatible with the SDN.
*/
@PluginDescriptor(
name = "Jimmy Example Plugin",
description = "A simple demonstration plugin.",
enabledByDefault = false
)
@Extension
public class JGExamplePlugin extends LoopedPlugin {
@Inject
private JGExampleConfig config;

@Override
protected int loop() {
if (config.eatFood() && Combat.getHealthPercent() < config.foodHp()) {
var food = Inventory.getFirst(config.foodName());
if (food != null) {
food.interact("Eat");
return 1200; // Eat, do not execute any other actions if eating, and wait for 1200 milliseconds
}
}

var localPlayer = Players.getLocal();
var npc = NPCs.query()
.names(config.npcName())
.results()
.nearest(localPlayer);
if (npc != null && !localPlayer.isInteracting() && npc.isInteractable()) {
npc.interact("Attack");
}

return 1000; // Sleep for 1000 milliseconds
}

@Provides
JGExampleConfig provideConfig(ConfigManager configManager) {
return configManager.getConfig(JGExampleConfig.class);
}
}
20 changes: 20 additions & 0 deletions JG-tutorial-island/JG-tutorial-island.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
version = "0.0.1"

project.extra["PluginName"] = "Tutorial Island Bot"
project.extra["PluginDescription"] = "Automates Tutorial Island from scratch."

tasks {
jar {
manifest {
attributes(
mapOf(
"Plugin-Version" to project.version,
"Plugin-Id" to nameToId(project.extra["PluginName"] as String),
"Plugin-Provider" to project.extra["PluginProvider"],
"Plugin-Description" to project.extra["PluginDescription"],
"Plugin-License" to project.extra["PluginLicense"]
)
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package net.storm.plugins.tutorialisland;

import net.runelite.client.ui.overlay.OverlayPanel;
import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPriority;
import net.runelite.client.ui.overlay.components.LineComponent;
import net.runelite.client.ui.overlay.components.PanelComponent;
import net.storm.api.plugins.Task;
import net.storm.sdk.game.Combat;
import steps.*;

import javax.inject.Inject;
import java.awt.*;

public class JGTutorialIslandOverlay extends OverlayPanel
{
private final JGTutorialIslandPlugin plugin;

@Inject
public JGTutorialIslandOverlay(JGTutorialIslandPlugin plugin)
{
this.plugin = plugin;
setLayer(OverlayLayer.ABOVE_WIDGETS); // or ALWAYS_ON_TOP
setPriority(OverlayPriority.LOW);
}

@Override
public Dimension render(Graphics2D graphics)
{
// Basic box, but you can style as you like
panelComponent.getChildren().clear();
panelComponent.setPreferredSize(new Dimension(220, 0));

panelComponent.getChildren().add(LineComponent.builder()
.left("Tutorial Island Bot")
.right("Debug")
.build());

panelComponent.getChildren().add(LineComponent.builder()
.left("Current Step")
.right(plugin.getCurrentState().toString())
.build());

panelComponent.getChildren().add(LineComponent.builder()
.left("Tick")
.right(Integer.toString(plugin.getTickCount()))
.build());

for (Task task : plugin.getTasks()) {
if (plugin.getCurrentState() == JGTutorialIslandState.SURVIVAL_EXPERT && task instanceof SurvivalExpertStep) {
SurvivalExpertStep s = (SurvivalExpertStep) task;
panelComponent.getChildren().add(LineComponent.builder()
.left("Substate")
.right(String.valueOf(s.getSubState()))
.build());
}
if (plugin.getCurrentState() == JGTutorialIslandState.GIELINOR_GUIDE && task instanceof GielinorGuideStep) {
GielinorGuideStep g = (GielinorGuideStep) task;
panelComponent.getChildren().add(LineComponent.builder()
.left("Substate")
.right(String.valueOf(g.getSubState()))
.build());
}
if (plugin.getCurrentState() == JGTutorialIslandState.MASTER_CHEF && task instanceof MasterChefStep) {
MasterChefStep c = (MasterChefStep) task;
panelComponent.getChildren().add(LineComponent.builder()
.left("Substate")
.right(String.valueOf(c.getSubState()))
.build());
}
if (plugin.getCurrentState() == JGTutorialIslandState.QUEST_GUIDE && task instanceof QuestGuideStep) {
QuestGuideStep q = (QuestGuideStep) task;
panelComponent.getChildren().add(LineComponent.builder()
.left("Substate")
.right(String.valueOf(q.getSubState()))
.build());
}
if (plugin.getCurrentState() == JGTutorialIslandState.MINING_INSTRUCTOR && task instanceof MiningGuideStep) {
MiningGuideStep m = (MiningGuideStep) task;
panelComponent.getChildren().add(LineComponent.builder()
.left("Substate")
.right(String.valueOf(m.getSubState()))
.build());
}
if (plugin.getCurrentState() == JGTutorialIslandState.COMBAT_INSTRUCTOR && task instanceof CombatInstructorStep) {
CombatInstructorStep cb = (CombatInstructorStep) task;
panelComponent.getChildren().add(LineComponent.builder()
.left("Substate")
.right(String.valueOf(cb.getSubState()))
.build());
}
if (plugin.getCurrentState() == JGTutorialIslandState.BANKER && task instanceof BankerStep) {
BankerStep b = (BankerStep) task;
panelComponent.getChildren().add(LineComponent.builder()
.left("Substate")
.right(String.valueOf(b.getSubState()))
.build());
}
if (plugin.getCurrentState() == JGTutorialIslandState.PRAYER_INSTRUCTOR && task instanceof PrayerInstructorStep) {
PrayerInstructorStep p = (PrayerInstructorStep) task;
panelComponent.getChildren().add(LineComponent.builder()
.left("Substate")
.right(String.valueOf(p.getSubState()))
.build());
}
if (plugin.getCurrentState() == JGTutorialIslandState.MAGIC_INSTRUCTOR && task instanceof MagicInstructorStep) {
MagicInstructorStep m = (MagicInstructorStep) task;
panelComponent.getChildren().add(LineComponent.builder()
.left("Substate")
.right(String.valueOf(m.getSubState()))
.build());
}
}

return super.render(graphics);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package net.storm.plugins.tutorialisland;

import com.google.inject.Inject;
import lombok.Getter;
import net.storm.api.plugins.PluginDescriptor;
import net.storm.api.plugins.Task;
import net.storm.sdk.plugins.TaskPlugin;
import net.storm.sdk.script.paint.DefaultPaint;
import net.runelite.client.ui.overlay.OverlayManager;
import org.pf4j.Extension;
import steps.*;

@PluginDescriptor(
name = "Tutorial Island Bot",
description = "Automates Tutorial Island from scratch",
enabledByDefault = false
)
@Extension
public class JGTutorialIslandPlugin extends TaskPlugin {
@Getter
private JGTutorialIslandState currentState = JGTutorialIslandState.GIELINOR_GUIDE;
@Getter
private int tickCount = 0;

@Inject
private JGTutorialIslandOverlay overlay;

@Inject
private OverlayManager overlayManager;

// --- PERSISTENT STEP INSTANCES ---
private final GielinorGuideStep gielinorGuideStep = new GielinorGuideStep(this);
private final SurvivalExpertStep survivalExpertStep = new SurvivalExpertStep(this);
private final MasterChefStep masterChefStep = new MasterChefStep(this);
private final QuestGuideStep questGuideStep = new QuestGuideStep(this);
private final MiningGuideStep miningGuideStep = new MiningGuideStep(this);
private final CombatInstructorStep combatInstructorStep = new CombatInstructorStep(this);
private final BankerStep bankerStep = new BankerStep(this);
private final PrayerInstructorStep prayerInstructorStep = new PrayerInstructorStep(this);
private final MagicInstructorStep magicInstructorStep = new MagicInstructorStep(this);
@Override
public void startUp() {
overlayManager.add(overlay);
setCurrentState(JGTutorialIslandState.GIELINOR_GUIDE);
}

@Override
public void shutDown() {
overlayManager.remove(overlay);
}

// Return the steps only ONCE; but you will only run the ACTIVE one
@Override
public Task[] getTasks() {
return new Task[] {
gielinorGuideStep,
survivalExpertStep,
masterChefStep,
questGuideStep,
miningGuideStep,
combatInstructorStep,
bankerStep,
prayerInstructorStep,
magicInstructorStep,


};
}

public void incrementTick() {
tickCount++;
}

public void resetTickCount() {
tickCount = 0;
}

public void setCurrentState(JGTutorialIslandState newState) {
if (this.currentState != newState) {
System.out.println("Tutorial Island State changed: " + this.currentState + " → " + newState);
this.currentState = newState;
resetTickCount();
// Optionally: update overlay, log, trigger events, etc.
}
}

// --- Only run the active step ---
@Override
protected int loop() {
// Only execute the step where validate() is true (should only be one)
for (Task task : getTasks()) {
if (task instanceof steps.TutorialStep && ((steps.TutorialStep) task).validate()) {
return task.execute();
}
}
// If no step matched, just sleep
return 600;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package net.storm.plugins.tutorialisland;

public enum JGTutorialIslandState {
GIELINOR_GUIDE,
SURVIVAL_EXPERT,
MASTER_CHEF,
QUEST_GUIDE,
MINING_INSTRUCTOR,
COMBAT_INSTRUCTOR,
BANKER,
PRAYER_INSTRUCTOR,
IRONMAN_INSTRUCTOR,
MAGIC_INSTRUCTOR,
COMPLETE
}
Loading