Tree-like objective node structure (#7152)
* overall structure * overall layout * field interpreter * less bloated UI * scroll pan't * strip off the 'Marker' suffix * e * all (hopefully all...) interpreters finished. * onset * two, four * i don't understand how icon mappings work. * separate remover and indexer * some cleanups * untested mobile support * contrib * ok anuke * fix conflicts 2 * hidden
This commit is contained in:
@@ -7,70 +7,311 @@ import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.game.MapObjectives.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.io.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import java.util.*;
|
||||
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class MapObjectives{
|
||||
public static Prov<MapObjective>[] allObjectiveTypes = new Prov[]{
|
||||
ResearchObjective::new, BuildCountObjective::new, UnitCountObjective::new, ItemObjective::new,
|
||||
CommandModeObjective::new, CoreItemObjective::new, DestroyCoreObjective::new, DestroyUnitsObjective::new,
|
||||
TimerObjective::new, FlagObjective::new, DestroyBlockObjective::new, ProduceObjective::new,
|
||||
DestroyBlocksObjective::new
|
||||
};
|
||||
/** Handles and executes in-map objectives. */
|
||||
public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObjective>{
|
||||
public static final Seq<Prov<? extends MapObjective>> allObjectiveTypes = new Seq<>();
|
||||
public static final Seq<Prov<? extends ObjectiveMarker>> allMarkerTypes = new Seq<>();
|
||||
|
||||
public static Prov<ObjectiveMarker>[] allMarkerTypes = new Prov[]{
|
||||
TextMarker::new, ShapeMarker::new, ShapeTextMarker::new, MinimapMarker::new
|
||||
};
|
||||
/**
|
||||
* All objectives the executor contains. Do not modify directly, ever!
|
||||
* @see #eachRunning(Cons)
|
||||
*/
|
||||
public Seq<MapObjective> all = new Seq<>(4);
|
||||
/** @see #checkChanged() */
|
||||
protected transient boolean changed;
|
||||
|
||||
static{
|
||||
registerObjective(
|
||||
ResearchObjective::new,
|
||||
ProduceObjective::new,
|
||||
ItemObjective::new,
|
||||
CoreItemObjective::new,
|
||||
BuildCountObjective::new,
|
||||
UnitCountObjective::new,
|
||||
DestroyUnitsObjective::new,
|
||||
TimerObjective::new,
|
||||
DestroyBlockObjective::new,
|
||||
DestroyBlocksObjective::new,
|
||||
DestroyCoreObjective::new,
|
||||
CommandModeObjective::new,
|
||||
FlagObjective::new
|
||||
);
|
||||
|
||||
registerMarker(
|
||||
ShapeTextMarker::new,
|
||||
MinimapMarker::new,
|
||||
ShapeMarker::new,
|
||||
TextMarker::new
|
||||
);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static void registerObjective(Prov<? extends MapObjective>... providers){
|
||||
for(var prov : providers){
|
||||
allObjectiveTypes.add(prov);
|
||||
|
||||
Class<? extends MapObjective> type = prov.get().getClass();
|
||||
JsonIO.classTag(type.getSimpleName().replace("Objective", ""), type);
|
||||
}
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static void registerMarker(Prov<? extends ObjectiveMarker>... providers){
|
||||
for(var prov : providers){
|
||||
allMarkerTypes.add(prov);
|
||||
|
||||
Class<? extends ObjectiveMarker> type = prov.get().getClass();
|
||||
JsonIO.classTag(type.getSimpleName().replace("Marker", ""), type);
|
||||
}
|
||||
}
|
||||
|
||||
/** Adds all given objectives to the executor as root objectives. */
|
||||
public void add(MapObjective... objectives){
|
||||
for(var objective : objectives) flatten(objective);
|
||||
}
|
||||
|
||||
/** Recursively adds the objective and its children. */
|
||||
private void flatten(MapObjective objective){
|
||||
for(var child : objective.children) flatten(child);
|
||||
|
||||
objective.children.clear();
|
||||
all.add(objective);
|
||||
}
|
||||
|
||||
/** Updates all objectives this executor contains. */
|
||||
public void update(){
|
||||
//TODO am i doing this correctly
|
||||
if(net.client()) return;
|
||||
eachRunning(obj -> {
|
||||
for(var marker : obj.markers){
|
||||
if(!marker.wasAdded){
|
||||
marker.wasAdded = true;
|
||||
marker.added();
|
||||
}
|
||||
}
|
||||
|
||||
if(obj.update()){
|
||||
obj.completed = true;
|
||||
obj.done();
|
||||
for(var marker : obj.markers){
|
||||
if(marker.wasAdded){
|
||||
marker.removed();
|
||||
marker.wasAdded = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
changed |= obj.changed;
|
||||
obj.changed = false;
|
||||
});
|
||||
}
|
||||
|
||||
/** @return True if map rules should be synced. Reserved for {@link Vars#logic}; do not invoke directly! */
|
||||
public boolean checkChanged(){
|
||||
boolean has = changed;
|
||||
changed = false;
|
||||
|
||||
return has;
|
||||
}
|
||||
|
||||
/** @return Whether there are any qualified objectives at all. */
|
||||
public boolean any(){
|
||||
return all.count(MapObjective::qualified) > 0;
|
||||
}
|
||||
|
||||
/** Iterates over all qualified in-map objectives. */
|
||||
public void eachRunning(Cons<MapObjective> cons){
|
||||
all.each(MapObjective::qualified, cons);
|
||||
}
|
||||
|
||||
/** Iterates over all qualified in-map objectives, with a filter. */
|
||||
public <T extends MapObjective> void eachRunning(Boolf<? super MapObjective> pred, Cons<T> cons){
|
||||
all.each(obj -> obj.qualified() && pred.get(obj), cons);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<MapObjective> iterator(){
|
||||
return all.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void each(Cons<? super MapObjective> cons){
|
||||
all.each(cons);
|
||||
}
|
||||
|
||||
/** Base abstract class for any in-map objective. */
|
||||
public static abstract class MapObjective{
|
||||
public @Nullable String details;
|
||||
public @Unordered String[] flagsAdded = {};
|
||||
public @Unordered String[] flagsRemoved = {};
|
||||
public ObjectiveMarker[] markers = {};
|
||||
|
||||
/** The parents of this objective. All parents must be done in order for this to be updated. */
|
||||
public transient Seq<MapObjective> parents = new Seq<>(2);
|
||||
/** Temporary container to store references since this class is static. Will immediately be flattened. */
|
||||
private transient final Seq<MapObjective> children = new Seq<>(2);
|
||||
|
||||
/** For the objectives UI dialog. Do not modify directly! */
|
||||
public transient int editorX = -1, editorY = -1;
|
||||
|
||||
/** Whether this objective has been done yet. This is internally set. */
|
||||
private boolean completed;
|
||||
/** Internal value. Do not modify! */
|
||||
private transient boolean depFinished, changed;
|
||||
|
||||
/** @return True if this objective is done and should be removed from the executor. */
|
||||
public abstract boolean update();
|
||||
|
||||
/** Reset internal state, if any. */
|
||||
public void reset(){}
|
||||
|
||||
/** Called once after {@link #update()} returns true, before this objective is removed. */
|
||||
public void done(){
|
||||
changed();
|
||||
state.rules.objectiveFlags.removeAll(flagsRemoved);
|
||||
state.rules.objectiveFlags.addAll(flagsAdded);
|
||||
}
|
||||
|
||||
/** Notifies the executor that map rules should be synced. */
|
||||
protected void changed(){
|
||||
changed = true;
|
||||
}
|
||||
|
||||
/** @return True if all {@link #parents} are completed, rendering this objective able to execute. */
|
||||
public final boolean dependencyFinished(){
|
||||
if(depFinished) return true;
|
||||
|
||||
boolean f = true;
|
||||
for(var parent : parents){
|
||||
if(!parent.isCompleted()) return false;
|
||||
}
|
||||
|
||||
return f && (depFinished = true);
|
||||
}
|
||||
|
||||
/** @return True if this objective is done (practically, has been removed from the executor). */
|
||||
public final boolean isCompleted(){
|
||||
return completed;
|
||||
}
|
||||
|
||||
/** @return Whether this objective should run at all. */
|
||||
public boolean qualified(){
|
||||
return !completed && dependencyFinished();
|
||||
}
|
||||
|
||||
/** @return This objective, with the given child's parents added with this, for chaining operations. */
|
||||
public MapObjective child(MapObjective child){
|
||||
child.parents.add(this);
|
||||
children.add(child);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** @return This objective, with the given parent added to this objective's parents, for chaining operations. */
|
||||
public MapObjective parent(MapObjective parent){
|
||||
parents.add(parent);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** @return This objective, with the details message assigned to, for chaining operations. */
|
||||
public MapObjective details(String details){
|
||||
this.details = details;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** @return This objective, with the added-flags assigned to, for chaining operations. */
|
||||
public MapObjective flagsAdded(String... flagsAdded){
|
||||
this.flagsAdded = flagsAdded;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** @return This objective, with the removed-flags assigned to, for chaining operations. */
|
||||
public MapObjective flagsRemoved(String... flagsRemoved){
|
||||
this.flagsRemoved = flagsRemoved;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** @return This objective, with the markers assigned to, for chaining operations. */
|
||||
public MapObjective markers(ObjectiveMarker... markers){
|
||||
this.markers = markers;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** @return Basic mission display text. If null, falls back to standard text. */
|
||||
public @Nullable String text(){
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @return Details that appear upon click. */
|
||||
public @Nullable String details(){
|
||||
return details;
|
||||
}
|
||||
|
||||
/** @return The localized type-name of this objective, defaulting to the class simple name without the "Objective" prefix. */
|
||||
public String typeName(){
|
||||
String className = getClass().getSimpleName().replace("Objective", "");
|
||||
return Core.bundle == null ? className : Core.bundle.get("objective." + className.toLowerCase() + ".name", className);
|
||||
}
|
||||
}
|
||||
|
||||
/** Research a specific piece of content in the tech tree. */
|
||||
public static class ResearchObjective extends MapObjective{
|
||||
public UnlockableContent content = Items.copper;
|
||||
public @Researchable UnlockableContent content = Items.copper;
|
||||
|
||||
public ResearchObjective(UnlockableContent content){
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public ResearchObjective(){
|
||||
public ResearchObjective(){}
|
||||
|
||||
@Override
|
||||
public boolean update(){
|
||||
return content.unlocked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String text(){
|
||||
return Core.bundle.format("objective.research", content.emoji(), content.localizedName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean complete(){
|
||||
return content.unlocked();
|
||||
}
|
||||
}
|
||||
|
||||
/** Produce a specific piece of content in the tech tree (essentially research with different text). */
|
||||
public static class ProduceObjective extends MapObjective{
|
||||
public UnlockableContent content = Items.copper;
|
||||
public @Researchable UnlockableContent content = Items.copper;
|
||||
|
||||
public ProduceObjective(UnlockableContent content){
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public ProduceObjective(){
|
||||
public ProduceObjective(){}
|
||||
|
||||
@Override
|
||||
public boolean update(){
|
||||
return content.unlocked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String text(){
|
||||
return Core.bundle.format("objective.produce", content.emoji(), content.localizedName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean complete(){
|
||||
return content.unlocked();
|
||||
}
|
||||
}
|
||||
|
||||
/** Have a certain amount of item in your core. */
|
||||
@@ -83,18 +324,17 @@ public class MapObjectives{
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public ItemObjective(){
|
||||
public ItemObjective(){}
|
||||
|
||||
@Override
|
||||
public boolean update(){
|
||||
return state.rules.defaultTeam.items().has(item, amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String text(){
|
||||
return Core.bundle.format("objective.item", state.rules.defaultTeam.items().get(item), amount, item.emoji(), item.localizedName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean complete(){
|
||||
return state.rules.defaultTeam.items().has(item, amount);
|
||||
}
|
||||
}
|
||||
|
||||
/** Get a certain item in your core (through a block, not manually.) */
|
||||
@@ -107,23 +347,22 @@ public class MapObjectives{
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public CoreItemObjective(){
|
||||
public CoreItemObjective(){}
|
||||
|
||||
@Override
|
||||
public boolean update(){
|
||||
return state.stats.coreItemCount.get(item) >= amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String text(){
|
||||
return Core.bundle.format("objective.coreitem", state.stats.coreItemCount.get(item), amount, item.emoji(), item.localizedName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean complete(){
|
||||
return state.stats.coreItemCount.get(item) >= amount;
|
||||
}
|
||||
}
|
||||
|
||||
/** Build a certain amount of a block. */
|
||||
public static class BuildCountObjective extends MapObjective{
|
||||
public Block block = Blocks.conveyor;
|
||||
public @Synthetic Block block = Blocks.conveyor;
|
||||
public int count = 1;
|
||||
|
||||
public BuildCountObjective(Block block, int count){
|
||||
@@ -131,18 +370,17 @@ public class MapObjectives{
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public BuildCountObjective(){
|
||||
public BuildCountObjective(){}
|
||||
|
||||
@Override
|
||||
public boolean update(){
|
||||
return state.stats.placedBlockCount.get(block, 0) >= count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String text(){
|
||||
return Core.bundle.format("objective.build", count, block.emoji(), block.localizedName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean complete(){
|
||||
return state.stats.placedBlockCount.get(block, 0) >= count;
|
||||
}
|
||||
}
|
||||
|
||||
/** Produce a certain amount of a unit. */
|
||||
@@ -155,18 +393,17 @@ public class MapObjectives{
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public UnitCountObjective(){
|
||||
public UnitCountObjective(){}
|
||||
|
||||
@Override
|
||||
public boolean update(){
|
||||
return state.rules.defaultTeam.data().countType(unit) >= count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String text(){
|
||||
return Core.bundle.format("objective.buildunit", count, unit.emoji(), unit.localizedName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean complete(){
|
||||
return state.rules.defaultTeam.data().countType(unit) >= count;
|
||||
}
|
||||
}
|
||||
|
||||
/** Produce a certain amount of units. */
|
||||
@@ -177,24 +414,24 @@ public class MapObjectives{
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public DestroyUnitsObjective(){
|
||||
public DestroyUnitsObjective(){}
|
||||
|
||||
@Override
|
||||
public boolean update(){
|
||||
return state.stats.enemyUnitsDestroyed >= count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String text(){
|
||||
return Core.bundle.format("objective.destroyunits", count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean complete(){
|
||||
return state.stats.enemyUnitsDestroyed >= count;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TimerObjective extends MapObjective{
|
||||
public String text;
|
||||
public float countup;
|
||||
public float duration = 60f * 30f;
|
||||
public @Second float duration = 60f * 30f;
|
||||
|
||||
protected float countup;
|
||||
|
||||
public TimerObjective(String text, float duration){
|
||||
this.text = text;
|
||||
@@ -205,13 +442,8 @@ public class MapObjectives{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean complete(){
|
||||
return countup >= duration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
countup += Time.delta;
|
||||
public boolean update(){
|
||||
return (countup += Time.delta) >= duration;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -243,28 +475,27 @@ public class MapObjectives{
|
||||
return Core.bundle.formatString(text, timeString.toString());
|
||||
}
|
||||
}
|
||||
return text;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class DestroyBlockObjective extends MapObjective{
|
||||
public int x, y;
|
||||
public Point2 pos = new Point2();
|
||||
public Team team = Team.crux;
|
||||
public Block block = Blocks.router;
|
||||
public @Synthetic Block block = Blocks.router;
|
||||
|
||||
public DestroyBlockObjective(Block block, int x, int y, Team team){
|
||||
this.block = block;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.team = team;
|
||||
this.pos.set(x, y);
|
||||
}
|
||||
|
||||
public DestroyBlockObjective(){
|
||||
}
|
||||
public DestroyBlockObjective(){}
|
||||
|
||||
@Override
|
||||
public boolean complete(){
|
||||
var build = world.build(x, y);
|
||||
public boolean update(){
|
||||
var build = world.build(pos.x, pos.y);
|
||||
return build == null || build.team != team || build.block != block;
|
||||
}
|
||||
|
||||
@@ -275,25 +506,22 @@ public class MapObjectives{
|
||||
}
|
||||
|
||||
public static class DestroyBlocksObjective extends MapObjective{
|
||||
public int[] positions = {};
|
||||
public @Unordered Point2[] positions = {};
|
||||
public Team team = Team.crux;
|
||||
public Block block = Blocks.router;
|
||||
public @Synthetic Block block = Blocks.router;
|
||||
|
||||
public DestroyBlocksObjective(Block block, Team team, int... positions){
|
||||
public DestroyBlocksObjective(Block block, Team team, Point2... positions){
|
||||
this.block = block;
|
||||
this.team = team;
|
||||
this.positions = positions;
|
||||
}
|
||||
|
||||
public DestroyBlocksObjective(){
|
||||
}
|
||||
public DestroyBlocksObjective(){}
|
||||
|
||||
public int progress(){
|
||||
int count = 0;
|
||||
for(var pos : positions){
|
||||
int x = Point2.x(pos), y = Point2.y(pos);
|
||||
|
||||
var build = world.build(x, y);
|
||||
var build = world.build(pos.x, pos.y);
|
||||
if(build == null || build.team != team || build.block != block){
|
||||
count ++;
|
||||
}
|
||||
@@ -302,7 +530,7 @@ public class MapObjectives{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean complete(){
|
||||
public boolean update(){
|
||||
return progress() >= positions.length;
|
||||
}
|
||||
|
||||
@@ -314,16 +542,15 @@ public class MapObjectives{
|
||||
|
||||
/** Command any unit to do anything. Always compete in headless mode. */
|
||||
public static class CommandModeObjective extends MapObjective{
|
||||
@Override
|
||||
public boolean update(){
|
||||
return headless || control.input.selectedUnits.contains(u -> u.isCommandable() && u.command().hasCommand());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String text(){
|
||||
return Core.bundle.get("objective.command");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean complete(){
|
||||
return headless || control.input.selectedUnits.contains(u -> u.isCommandable() && u.command().hasCommand());
|
||||
}
|
||||
}
|
||||
|
||||
/** Wait until a logic flag is set. */
|
||||
@@ -335,189 +562,138 @@ public class MapObjectives{
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public FlagObjective(){
|
||||
public FlagObjective(){}
|
||||
|
||||
@Override
|
||||
public boolean update(){
|
||||
return state.rules.objectiveFlags.contains(flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String text(){
|
||||
return text != null && text.startsWith("@") ? Core.bundle.get(text.substring(1)) : text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean complete(){
|
||||
return state.rules.objectiveFlags.contains(flag);
|
||||
}
|
||||
}
|
||||
|
||||
/** Destroy all enemy core(s). */
|
||||
public static class DestroyCoreObjective extends MapObjective{
|
||||
@Override
|
||||
public boolean update(){
|
||||
return state.rules.waveTeam.cores().size == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String text(){
|
||||
return Core.bundle.get("objective.destroycore");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean complete(){
|
||||
return state.rules.waveTeam.cores().size == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Base abstract class for any in-map objective. */
|
||||
public static abstract class MapObjective{
|
||||
public @Nullable String details;
|
||||
public String[] flagsAdded = {};
|
||||
public String[] flagsRemoved = {};
|
||||
public ObjectiveMarker[] markers = {};
|
||||
/** Marker used for drawing UI to indicate something along with an objective. */
|
||||
public static abstract class ObjectiveMarker{
|
||||
/** Makes sure markers are only added once. */
|
||||
private transient boolean wasAdded;
|
||||
|
||||
//TODO localize
|
||||
/** Called in the overlay draw layer.*/
|
||||
public void draw(){}
|
||||
/** Called in the small and large map. */
|
||||
public void drawMinimap(MinimapRenderer minimap){}
|
||||
/** Add any UI elements necessary. */
|
||||
public void added(){}
|
||||
/** Remove any UI elements, if necessary. */
|
||||
public void removed(){}
|
||||
|
||||
/** @return The localized type-name of this objective, defaulting to the class simple name without the "Marker" prefix. */
|
||||
public String typeName(){
|
||||
return getClass().getSimpleName().replace("Objective", "");
|
||||
}
|
||||
|
||||
public MapObjective withFlags(String... flags){
|
||||
this.flagsAdded = flags;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapObjective withFlagsRemoved(String... flags){
|
||||
this.flagsRemoved = flags;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapObjective withMarkers(ObjectiveMarker... markers){
|
||||
this.markers = markers;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapObjective withDetails(String details){
|
||||
this.details = details;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean complete(){
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Called immediately after this objective is completed and removed from the rules. */
|
||||
public void completed(){
|
||||
|
||||
}
|
||||
|
||||
public void update(){
|
||||
|
||||
}
|
||||
|
||||
/** Reset internal state, if any. */
|
||||
public void reset(){
|
||||
|
||||
}
|
||||
|
||||
/** Basic mission display text. If null, falls back to standard text. */
|
||||
public @Nullable String text(){
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Details that appear upon click. */
|
||||
public @Nullable String details(){
|
||||
return details;
|
||||
String className = getClass().getSimpleName().replace("Marker", "");
|
||||
return Core.bundle == null ? className : Core.bundle.get("marker." + className.toLowerCase() + ".name", className);
|
||||
}
|
||||
}
|
||||
|
||||
/** Displays text above a shape. */
|
||||
public static class ShapeTextMarker extends ObjectiveMarker{
|
||||
public String text = "frog";
|
||||
public float x, y, fontSize = 1f, textHeight = 7f;
|
||||
public byte flags = WorldLabel.flagBackground | WorldLabel.flagOutline;
|
||||
public @TilePos Vec2 pos = new Vec2();
|
||||
public float fontSize = 1f, textHeight = 7f;
|
||||
public @LabelFlag byte flags = WorldLabel.flagBackground | WorldLabel.flagOutline;
|
||||
|
||||
public float radius = 6f, rotation = 0f;
|
||||
public int sides = 4;
|
||||
public Color color = Color.valueOf("ffd37f");
|
||||
|
||||
//cached localized text
|
||||
// Cached localized text.
|
||||
private transient String fetchedText;
|
||||
|
||||
public ShapeTextMarker(String text, float x, float y){
|
||||
this.text = text;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.pos.set(x, y);
|
||||
}
|
||||
|
||||
public ShapeTextMarker(String text, float x, float y, float radius){
|
||||
this.text = text;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.pos.set(x, y);
|
||||
this.radius = radius;
|
||||
}
|
||||
|
||||
public ShapeTextMarker(String text, float x, float y, float radius, float rotation){
|
||||
this.text = text;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.pos.set(x, y);
|
||||
this.radius = radius;
|
||||
this.rotation = rotation;
|
||||
}
|
||||
|
||||
public ShapeTextMarker(String text, float x, float y, float radius, float rotation, float textHeight){
|
||||
this.text = text;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.pos.set(x, y);
|
||||
this.radius = radius;
|
||||
this.rotation = rotation;
|
||||
this.textHeight = textHeight;
|
||||
}
|
||||
|
||||
public ShapeTextMarker(){
|
||||
|
||||
}
|
||||
public ShapeTextMarker(){}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Lines.stroke(3f, Pal.gray);
|
||||
Lines.poly(x, y, sides, radius + 1f, rotation);
|
||||
Lines.poly(pos.x, pos.y, sides, radius + 1f, rotation);
|
||||
Lines.stroke(1f, color);
|
||||
Lines.poly(x, y, sides, radius + 1f, rotation);
|
||||
Lines.poly(pos.x, pos.y, sides, radius + 1f, rotation);
|
||||
Draw.reset();
|
||||
|
||||
if(fetchedText == null){
|
||||
fetchedText = text.startsWith("@") ? Core.bundle.get(text.substring(1)) : text;
|
||||
}
|
||||
|
||||
WorldLabel.drawAt(text, x, y + radius + textHeight, Draw.z(), flags, fontSize);
|
||||
WorldLabel.drawAt(text, pos.x, pos.y + radius + textHeight, Draw.z(), flags, fontSize);
|
||||
}
|
||||
}
|
||||
|
||||
/** Displays a circle on the minimap. */
|
||||
public static class MinimapMarker extends ObjectiveMarker{
|
||||
public int x, y;
|
||||
public Point2 pos = new Point2();
|
||||
public float radius = 5f, stroke = 11f;
|
||||
public Color color = Color.valueOf("f25555");
|
||||
|
||||
public MinimapMarker(int x, int y){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.pos.set(x, y);
|
||||
}
|
||||
|
||||
public MinimapMarker(int x, int y, Color color){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.pos.set(x, y);
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public MinimapMarker(int x, int y, float radius, float stroke, Color color){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.pos.set(x, y);
|
||||
this.stroke = stroke;
|
||||
this.radius = radius;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public MinimapMarker(){
|
||||
}
|
||||
public MinimapMarker(){}
|
||||
|
||||
@Override
|
||||
public void drawMinimap(MinimapRenderer minimap){
|
||||
minimap.transform(Tmp.v1.set(x * tilesize, y * tilesize));
|
||||
minimap.transform(Tmp.v1.set(pos.x * tilesize, pos.y * tilesize));
|
||||
|
||||
float rad = minimap.scale(radius * tilesize);
|
||||
float fin = Interp.pow2Out.apply((Time.globalTime / 100f) % 1f);
|
||||
@@ -530,25 +706,23 @@ public class MapObjectives{
|
||||
|
||||
/** Displays a shape with an outline and color. */
|
||||
public static class ShapeMarker extends ObjectiveMarker{
|
||||
public float x, y, radius = 8f, rotation = 0f, stroke = 1f;
|
||||
public @TilePos Vec2 pos = new Vec2();
|
||||
public float radius = 8f, rotation = 0f, stroke = 1f;
|
||||
public boolean fill = false, outline = true;
|
||||
public int sides = 4;
|
||||
public Color color = Color.valueOf("ffd37f");
|
||||
|
||||
public ShapeMarker(float x, float y){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.pos.set(x, y);
|
||||
}
|
||||
|
||||
public ShapeMarker(float x, float y, float radius, float rotation){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.pos.set(x, y);
|
||||
this.radius = radius;
|
||||
this.rotation = rotation;
|
||||
}
|
||||
|
||||
public ShapeMarker(){
|
||||
}
|
||||
public ShapeMarker(){}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
@@ -558,14 +732,14 @@ public class MapObjectives{
|
||||
if(!fill){
|
||||
if(outline){
|
||||
Lines.stroke(stroke + 2f, Pal.gray);
|
||||
Lines.poly(x, y, sides, radius + 1f, rotation);
|
||||
Lines.poly(pos.x, pos.y, sides, radius + 1f, rotation);
|
||||
}
|
||||
|
||||
Lines.stroke(stroke, color);
|
||||
Lines.poly(x, y, sides, radius + 1f, rotation);
|
||||
Lines.poly(pos.x, pos.y, sides, radius + 1f, rotation);
|
||||
}else{
|
||||
Draw.color(color);
|
||||
Fill.poly(x, y, sides, radius);
|
||||
Fill.poly(pos.x, pos.y, sides, radius);
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
@@ -575,27 +749,25 @@ public class MapObjectives{
|
||||
/** Displays text at a location. */
|
||||
public static class TextMarker extends ObjectiveMarker{
|
||||
public String text = "uwu";
|
||||
public float x, y, fontSize = 1f;
|
||||
public byte flags = WorldLabel.flagBackground | WorldLabel.flagOutline;
|
||||
//cached localized text
|
||||
public @TilePos Vec2 pos = new Vec2();
|
||||
public float fontSize = 1f;
|
||||
public @LabelFlag byte flags = WorldLabel.flagBackground | WorldLabel.flagOutline;
|
||||
// Cached localized text.
|
||||
private transient String fetchedText;
|
||||
|
||||
public TextMarker(String text, float x, float y, float fontSize, byte flags){
|
||||
this.text = text;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.fontSize = fontSize;
|
||||
this.flags = flags;
|
||||
this.pos.set(x, y);
|
||||
}
|
||||
|
||||
public TextMarker(String text, float x, float y){
|
||||
this.text = text;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.pos.set(x, y);
|
||||
}
|
||||
|
||||
public TextMarker(){
|
||||
}
|
||||
public TextMarker(){}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
@@ -603,27 +775,37 @@ public class MapObjectives{
|
||||
fetchedText = text.startsWith("@") ? Core.bundle.get(text.substring(1)) : text;
|
||||
}
|
||||
|
||||
WorldLabel.drawAt(fetchedText, x, y, Draw.z(), flags, fontSize);
|
||||
WorldLabel.drawAt(fetchedText, pos.x, pos.y, Draw.z(), flags, fontSize);
|
||||
}
|
||||
}
|
||||
|
||||
/** Marker used for drawing UI to indicate something along with an objective. */
|
||||
public static abstract class ObjectiveMarker{
|
||||
/** makes sure markers are only added once */
|
||||
public transient boolean wasAdded;
|
||||
/** For arrays or {@link Seq}s; does not create element rearrangement buttons. */
|
||||
@Target(FIELD)
|
||||
@Retention(RUNTIME)
|
||||
public @interface Unordered{}
|
||||
|
||||
//TODO localize
|
||||
public String typeName(){
|
||||
return getClass().getSimpleName().replace("Marker", "");
|
||||
}
|
||||
/** For {@code byte}; treats it as a world label flag. */
|
||||
@Target(FIELD)
|
||||
@Retention(RUNTIME)
|
||||
public @interface LabelFlag{}
|
||||
|
||||
/** Called in the overlay draw layer.*/
|
||||
public void draw(){}
|
||||
/** Called in the small & large map. */
|
||||
public void drawMinimap(MinimapRenderer minimap){}
|
||||
/** Add any UI elements necessary. */
|
||||
public void added(){}
|
||||
/** Remove any UI elements, if necessary. */
|
||||
public void removed(){}
|
||||
}
|
||||
/** For {@link UnlockableContent}; filters all un-researchable content. */
|
||||
@Target(FIELD)
|
||||
@Retention(RUNTIME)
|
||||
public @interface Researchable{}
|
||||
|
||||
/** For {@link Block}; filters all un-buildable blocks. */
|
||||
@Target(FIELD)
|
||||
@Retention(RUNTIME)
|
||||
public @interface Synthetic{}
|
||||
|
||||
/** For {@code float}; multiplies the UI input by 60. */
|
||||
@Target(FIELD)
|
||||
@Retention(RUNTIME)
|
||||
public @interface Second{}
|
||||
|
||||
/** For {@code float} or similar data structures, such as {@link Vec2}; multiplies the UI input by {@link Vars#tilesize}. */
|
||||
@Target(FIELD)
|
||||
@Retention(RUNTIME)
|
||||
public @interface TilePos{}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user