Jump to content

[Tutorial] How to pass data from you GUI to your script


Traum

Recommended Posts

[Tutorial] How to pass data from you GUI to your script
 
Feel free to make suggestions / ask questions.
 
1. Create a DATA class
 
- This class is going to hold all the data and variables modifiable by the end user.
 
Example variables:
 
String monsterName;
String eatingThreshold;
boolean useAntipoison;
Position monsterCoordinates;

Roles:

 

- The variables need to be usable by our script (a different class),  in order to enable access we can provide getter methods (safe) or just change the visibility of the variables (lazy).
 
- The variables need to be modifiable by our GUI (again, a different class),  in order to enable modification we can provide setter methods (safe) or just change the visibility of the variables (lazy).
 
Example code:

 // This is a simple example data class for a fighter script.

 public class FighterData {

  // I'm not using getters / setters here to reduce verbosity, you should though.
  public String monsterName;
  public int eatThreshold;
  public boolean logWhenDied;

  public FighterData(String monsterName, int eatThreshold, boolean logWhenDied) {
   this.monsterName = monsterName;
   this.eatThreshold = eatThreshold;
   this.logWhenDied = logWhenDied;
  }

 }
2. Add an instance of your DATA class to your script
 
@ScriptManifest(author = "Me", info = "", logo = "", name = "Fighter Script", version = 0)
public class MyScript extends Script {

 private FighterData data = new FighterData("Goblin", 20, true);

 @Override
 public int onLoop() throws InterruptedException {
  if (myPlayer().getHealth() < data.eatThreshold) {
   // Eat.
  }
  return 200;
 }

}
So far all we have done is take the global variables from your script class (traditionally speaking) and moved them to their own separate class.
 
QUESTION: Can't I just make my global script variables public (or provide getters and setter to them) and pass my script instance to the GUI?
 
ANSWER: You could. Here are some arguments for using a separate class:
 
- It will make your life much easier when you decide to implement importation and exportation of the data.
- You can store multiple sets of data for later use easily and cleanly (for data-based task scheduling for example).
- By encapsulating the data you can easily and without too much code, provide preset values for that data.
 
Compare:
 
public static final FighterData GOBLIN_SUICIDER = new FighterData("Goblin", -1, false);
public static final FighterData DRUID_TANK = new FighterData("Druid", 20, true);
public static final FighterData SPIDER_BOSS = new FighterData("Spider", 5, true);
public static final FighterData ROCK_CRAB_SENSEI = new FighterData("Rock Crab", 6, true);
public static final FighterData UNICORN_PRO = new FighterData("Unicorn", 9, true);

private FighterData profile = GOBLIN_SUICIDER;
vs:
 
private String monsterName;
private int eatThreshold;
private boolean logWhenDied;

public void build(String preset) { // We are just typing our data class's constructor code over and over again...
switch (preset) {
case "GOBLIN_SUICIDER":
monsterName = "Goblin"
eatThreshold = -1;
logWhenDied = false;
break;
case "DRUID_TANK":
monsterName = "Druid"
eatThreshold = 20;
logWhenDied = true;
break;
case "SPIDER_BOSS":
monsterName = "Spider"
eatThreshold = 5;
logWhenDied = true;
break;
}
case "ROCK_CRAB_SENSEI":
monsterName = "Rock Crab"
eatThreshold = 6;
logWhenDied = true;
break;
}
case "UNICORN_PRO":
monsterName = "Unicorn"
eatThreshold = 9;
logWhenDied = true;
break;
}
}
build("GOBLIN_SUICIDER");
 
- Etcetera wink.png
 
3. Time for the GUI / VIEW code
 
Current implementation: DATA OBJECT -> SCRIPT
Desired implementation: GUI -> DATA OBJECT -> SCRIPT (The GUI and the SCRIPT share the DATA OBJECT).
 
QUESTION: How do we share the data object between the SCRIPT class and the GUI class?
ANSWER: Just pass a reference to the data object instance to the GUI (via a constructor for example).
 
SCRIPT:
 
package tutorial;

import org.osbot.rs07.api.model.NPC;
import org.osbot.rs07.script.Script;
import org.osbot.rs07.script.ScriptManifest;

@ScriptManifest(author = "Me", info = "", logo = "", name = "Fighter Script", version = 0)
public class MyScripty extends Script {

 private FighterData data = new FighterData("Goblin", 20, true);
 private FighterGUI gui = new FighterGUI(data);

 @Override
 public void onStart() throws InterruptedException {
  super.onStart();
  gui.setVisible(true);
  //...
 }

 private NPC target = null;
 private boolean died = false;

 @Override
 public int onLoop() throws InterruptedException {
  if (myPlayer().getHealth() < data.eatThreshold) {
   // Eat.
  }
  if (died && data.logWhenDied) {
   // Log out.
  } else {
   target = getNpcs().closest(data.monsterName);
   // Attack.
  }
  return 200;
 }

}
GUI:
 
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class FighterGUI extends JFrame {

 private static final long serialVersionUID = -1969829387256339229 L;

 private FighterData data;

 public FighterGUI(FighterData model) {
  super();
  this.data = model;
  SwingUtilities.invokeLater(new Runnable() {
   public void run() {
    setTitle("Fighter Script");
    setDefaultCloseOperation(DISPOSE_ON_CLOSE);
    setResizable(false);
    populate();
    pack();
    setLocationRelativeTo(null);
   }
  });
 }

 private void populate() {
  JPanel cp = new JPanel();
  setContentPane(cp);

  JTextField name = new JTextField("monster name");
  cp.add(name);

  JButton nameok = new JButton("ok");
  nameok.addActionListener((al) -> data.monsterName = name.getText());
  cp.add(nameok);

  JTextField eat = new JTextField("eat threshold");
  cp.add(eat);

  JButton eatok = new JButton("ok");
  nameok.addActionListener((al) -> data.eatThreshold = Integer.parseInt(eat.getText()));
  // PLEASE DO NOT FORGET TO ADD INTEGER PARSING ERROR HANDLING HERE!
  cp.add(eatok);

  JCheckBox log = new JCheckBox("log after death");
  log.addActionListener((al) -> data.logWhenDied = log.isSelected());
  cp.add(log);
 }

}
 
Edited by Traum
  • Like 1
Link to comment
Share on other sites

public static final FighterData GOBLIN_SUICIDER = new FighterData("Goblin", -1, false);
public static final FighterData DRUID_TANK = new FighterData("Druid", 20, true);
public static final FighterData SPIDER_BOSS = new FighterData("Spider", 5, true);
public static final FighterData ROCK_CRAB_SENSEI = new FighterData("Rock Crab", 6, true);
public static final FighterData UNICORN_PRO = new FighterData("Unicorn", 9, true);

Not a fan of this, especially when you're getting big amounts of objects.

Using a map or list to store the objects would be a better approach imo.

//DataManager
private static Map<EnumExample, DataObject> data = new HashMap<>();

public static void load() {
  data.add(EnumExample.GOBLIN, new DataObject(#, #));
}

public static DataObject find(EnumExample type) {
  return data.get(type);
}

//Main
@Override
public void onStart() {
  DataManager.load();
}

But that's just my 2 cents.

For the rest great guide! Sure a lot of people will learn a lot from this

Edited by lisabe96
Link to comment
Share on other sites

public static final FighterData GOBLIN_SUICIDER = new FighterData("Goblin", -1, false);
public static final FighterData DRUID_TANK = new FighterData("Druid", 20, true);
public static final FighterData SPIDER_BOSS = new FighterData("Spider", 5, true);
public static final FighterData ROCK_CRAB_SENSEI = new FighterData("Rock Crab", 6, true);
public static final FighterData UNICORN_PRO = new FighterData("Unicorn", 9, true);

Not a fan of this, especially when you're getting big amounts of objects.

Using a map or list to store the objects would be a better approach imo.

//DataManager
private static Map<EnumExample, DataObject> data = new HashMap<>();

public static void load() {
  data.add(EnumExample.GOBLIN, new DataObject(#, #));
}

public static DataObject find(EnumExample type) {
  return data.get(type);
}

//Main
@Override
public void onStart() {
  DataManager.load();
}

But that's just my 2 cents.

For the rest great guide! Sure a lot of people will learn a lot from this

 

 

Oh yes absolutely, I just wanted to point out the verbosity when not making the data composite.

Quick tip: when using enums as key values in a map, consider choosing an EnumMap over a HashMap!

  • Like 1
Link to comment
Share on other sites

Another thing. Your monsters are not variable as you're creating objects for each type of monster.

So it's possible that the monster the player enters, doesn't actually exists in our data.

So you would have to store the available ones in a comboBox probably to avoid this.

Unless I've misinterpreted your code 

Link to comment
Share on other sites

Another thing. Your monsters are not variable as you're creating objects for each type of monster.

So it's possible that the monster the player enters, doesn't actually exists in our data.

So you would have to store the available ones in a comboBox probably to avoid this.

Unless I've misinterpreted your code 

 

I think you did misinterpret it, but that's probably my fault:

 

The list of presets is not part of the provided script-gui implementation, it's simply there to demonstrate an extra benefit of having a unifying data structure .  Also, my GUI example is very minimal and not very thorough, I just quickly wrote it up to be able to demonstrate data passing (the actual subject of the tutorial) .

  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...