Thanks for the info. Lol i never knew you could call this() within a class.
I tried to do something similar to your framework, except with all the Actions and Conditions being defined in different classes. It did seem like I was writing a language inside a language, but there were only four different Parameter classes needed to define all the functionality needed, which were Quantity, PlayerParam, UnitParam, and Area, so it wasn’t that big of a deal. The main classes were implemented like so:
Parameter:
package CremelianWars.Triggers;
/*A parameter is used to test various pieces of data that are sent to Condition
objects.*/
public interface Parameter{
public boolean test(Object other);
}
Quantity:
package CremelianWars.Triggers;
/*Consists of a number, and the following tests:
at least
at most
exactly*/
public class Quantity implements Parameter{
public static final int AT_LEAST = 0;
public static final int AT_MOST = 1;
public static final int EXACTLY = 2;
private int value = -1;
private int comparison = -1;
public Quantity(int val, int comp){
super();
value = val;
comparison = comp;
}
public boolean test(Object other){
if(other == null)
return false;
if(!(other instanceof Integer))
return false;
Integer input = (Integer)other;
if(getComparison() == -1)
return false;
if(comparison == AT_LEAST)//num must be greater or equal to comparison
{
if(input >= new Integer(value))
return true;
}
else if(comparison == AT_MOST)//num must be less than or equal to comparison
{
if(input <= value)
return true;
}
else if(comparison == EXACTLY){//num must equal comparison
if(input == value)
return true;
}
return false;
}
public boolean test(int num){
return test(new Integer(num));
}
public int getValue(){
return value;
}
public int getComparison(){
return comparison;
}
}
Condition:
package CremelianWars.Triggers;
import java.util.*;
/*Tests a list of input data against the data this condition was defined with.
It uses Parameter objects to test one another*/
public abstract class Condition{
public static final int SINGLE = 1;
public static final int MULTIPLE = 2;
public static final int CONTINUOUS = 3;
protected ArrayList<Object> parameters;
public Condition()
{
}
public abstract boolean isMet();
public void setTestData(ArrayList<Object> params){
parameters = params;
}
}
Action:
package CremelianWars.Triggers;
public abstract class Action{
int timesExecuted = 0;
public abstract boolean run();
public int getNumExecutions(){
return timesExecuted;
}
}
A class that extends Condition:
package CremelianWars.Triggers;
import CremelianWars.*;
/*Tests of a given player has accumulated a certain amount of funds:
-----------------------------------
Requires PlayerParam, Quantity
Tests Player
*/
public class Accumulate extends Condition{
PlayerParam activePlayer;
Quantity compQuantity;
public Accumulate(PlayerParam p, Quantity q){
super();
activePlayer = p;
compQuantity = q;
}
public boolean isMet(){
try{
Player p = (Player)parameters.get(0);
if(activePlayer.test(p))
if(compQuantity.test(p.getFunds()))
return true;
return false;
}
catch(Exception e){
System.out.println(e);
return false;
}
}
}
Basically, this implementation just cascades tasks to break them up into smaller and smaller segments, them being trigger -> condition -> Parameter. But because all of the conditions deal with different things, it seemed like a good idea to use an ArrayList of objects. Since that’s the case, though, its almost impossible to make these function within some sort of a loop, since they all have variable needs. Typing them out isn’t easy either. I tried implementing the system, and it took a good deal of work to make the system perform a simple task, which looked like this:
package CremelianWars.Triggers;
import java.awt.*;
import java.util.*;
import CremelianWars.Unit.*;
import CremelianWars.*;
public class TriggerTest{
public static void main(String[] args){
Random rand = new Random();
Trigger t = new Trigger();
//The initialized player's turn is set to 3
Player p = new Player(3, false);
//A map is filled with units controlled by randomized players
GameMap map = new GameMap(3,3);
for(int x=0;x<map.getWidth();x++)
for(int y=0;y<map.getHeight();y++)
map.getLoc(new Point(x,y)).setUnit(new Unit(2,2,rand.nextInt(4),2));
//Creates three necessary Parameters for the control condition
Parameter unitParam= new UnitParam(-1, UnitParam.UNIT);
Parameter playerParam = new PlayerParam(p.getTurn(), PlayerParam.SPECIFIC);
Parameter quantity = new Quantity(2, Quantity.AT_LEAST);
//Creates the conditions from the parameters
Condition control = new Control(unitParam, playerParam, quantity);
Condition elapsed = new ElapsedTime(new Quantity(2, Quantity.EXACTLY));
//adds the conditions to the Trigger
t.addCondition(control);
t.addCondition(elapsed);
//sets the test data for the Control condition
ArrayList<Object> input = new ArrayList<Object>();
input.add(map);
control.setTestData(input);
//sets the test data for the ElapstedTime (in turns) condition
ArrayList<Object> elapsedInput = new ArrayList<Object>();
elapsedInput.add(new Integer(1));
elapsed.setTestData(elapsedInput);
System.out.println(t.allConditionsMet()+","+control.isMet());
System.out.println(elapsed.isMet());
}
}
Which brings me to my next point of how to use them in a map editor. I suppose there could be a toString method associated with each, as well as a buildFromString method, so the user could type them in. XML would probably work better, but the map is already loaded using a similar method dealing with break chars. So, does there need to be some sort of a static factory method to create these, or do you guys see another way?
Oh yeah, and in the above piece of code, below the comment:"//Creates the conditions from the parameters",
The compiler displays that its illegal to pass those three Parameters to the Control Condition. It would probably be beneficial if there was someway around this, as to allow for more generic objects to be passed.
The Control condition is defined like so:
package CremelianWars.Triggers;
import java.util.*;
import CremelianWars.*;
import CremelianWars.Unit.*;
/**Tests if a player has a certain amount of units:
-----------------------------------
Requires UnitParam, PlayerParam, Quantity
Tests GameMap
This method loops through the game map, getting every location, testing if
the unit it contains meets the test of the unit parameter, and if it does,
adding it to a total, which is then tested against a quantity.
*/
public class Control extends Condition{
UnitParam unitParam;
PlayerParam playerParam;
Quantity quant;
public Control(UnitParam u, PlayerParam p, Quantity q){
super();
unitParam = u;
playerParam = p;
quant = q;
}
public boolean isMet(){
try{
GameMap map = (GameMap)parameters.get(0);
ArrayList<Unit> allUnits = map.getAllUnits();
int controls = 0;
for(Unit u : allUnits){
if(unitParam.test(u))
if(playerParam.test(u.getPlayer()))
controls++;
}
if(quant.test(controls))
return true;
return false;
}
catch(Exception e){
System.out.println(e);
return false;
}
}
}