From f1bcb4ae54f0ff4a8d5e4bd5892b6a6622441b15 Mon Sep 17 00:00:00 2001 From: Retera Date: Sun, 4 Apr 2021 10:28:40 -0400 Subject: [PATCH] Update campaign menu behaviors and make some progress on untested common j apis --- .../warsmash/WarsmashGdxMapScreen.java | 2 +- .../warsmash/WarsmashGdxMenuScreen.java | 2 +- .../etheller/warsmash/parsers/fdf/GameUI.java | 29 +- .../fdf/frames/AbstractRenderableFrame.java | 2 +- .../parsers/fdf/frames/GlueButtonFrame.java | 12 + .../fdf/frames/GlueTextButtonFrame.java | 42 + .../parsers/fdf/frames/SingleStringFrame.java | 4 + .../parsers/fdf/frames/StringFrame.java | 27 +- .../etheller/warsmash/parsers/jass/Jass2.java | 978 +++++++++++++++++- .../scope/CommonTriggerExecutionScope.java | 64 ++ .../com/etheller/warsmash/util/Quadtree.java | 3 +- .../warsmash/util/WarsmashConstants.java | 2 +- .../viewer5/handlers/mdx/BatchGroup.java | 2 +- .../viewer5/handlers/mdx/SdSequence.java | 36 +- .../viewer5/handlers/w3x/War3MapViewer.java | 50 +- .../handlers/w3x/simulation/CSimulation.java | 7 + .../handlers/w3x/simulation/CUnit.java | 136 ++- .../w3x/simulation/CWorldCollision.java | 3 + .../simulation/behaviors/CBehaviorMove.java | 2 +- .../behaviors/build/CBehaviorUndeadBuild.java | 2 +- .../w3x/simulation/players/CPlayerJass.java | 2 + .../w3x/simulation/region/CRegion.java | 125 +++ .../region/CRegionEnumFunction.java | 11 + .../w3x/simulation/region/CRegionManager.java | 189 ++++ .../viewer5/handlers/w3x/ui/MeleeUI.java | 23 +- .../viewer5/handlers/w3x/ui/MenuUI.java | 21 +- .../handlers/w3x/ui/menu/CampaignMenuUI.java | 3 - 27 files changed, 1710 insertions(+), 69 deletions(-) create mode 100644 core/src/com/etheller/warsmash/parsers/jass/scope/CommonTriggerExecutionScope.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/region/CRegion.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/region/CRegionEnumFunction.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/region/CRegionManager.java diff --git a/core/src/com/etheller/warsmash/WarsmashGdxMapScreen.java b/core/src/com/etheller/warsmash/WarsmashGdxMapScreen.java index 9f4c9b1..bc3601c 100644 --- a/core/src/com/etheller/warsmash/WarsmashGdxMapScreen.java +++ b/core/src/com/etheller/warsmash/WarsmashGdxMapScreen.java @@ -53,7 +53,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.ui.MeleeUI; public class WarsmashGdxMapScreen implements InputProcessor, Screen { public static final boolean ENABLE_AUDIO = true; - private static final boolean ENABLE_MUSIC = true; + private static final boolean ENABLE_MUSIC = false; private final War3MapViewer viewer; private final Rectangle tempRect = new Rectangle(); diff --git a/core/src/com/etheller/warsmash/WarsmashGdxMenuScreen.java b/core/src/com/etheller/warsmash/WarsmashGdxMenuScreen.java index 2faf476..84d7674 100644 --- a/core/src/com/etheller/warsmash/WarsmashGdxMenuScreen.java +++ b/core/src/com/etheller/warsmash/WarsmashGdxMenuScreen.java @@ -52,7 +52,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.ui.MenuUI; public class WarsmashGdxMenuScreen implements InputProcessor, Screen, SingleModelScreen { private static final boolean ENABLE_AUDIO = true; - private static final boolean ENABLE_MUSIC = true; + private static final boolean ENABLE_MUSIC = false; private DataSource codebase; private MdxViewer viewer; private MdxModel model; diff --git a/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java b/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java index 34fa256..dd5fd4c 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java @@ -294,7 +294,8 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { this.fontParam.size = 128; } final BitmapFont frameFont = this.fontGenerator.generateFont(this.fontParam); - final StringFrame stringFrame = new StringFrame(name, parent, color, justifyH, justifyV, frameFont, name); + final StringFrame stringFrame = new StringFrame(name, parent, color, justifyH, justifyV, frameFont, name, null, + null); this.nameToFrame.put(name, stringFrame); add(stringFrame); return stringFrame; @@ -400,6 +401,28 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { fontColorDefinition.getZ(), fontColorDefinition.getW()); } + Color fontHighlightColor; + final Vector4Definition fontHighlightColorDefinition = frameDefinition.getVector4("FontHighlightColor"); + if (fontHighlightColorDefinition == null) { + fontHighlightColor = null; + } + else { + fontHighlightColor = new Color(fontHighlightColorDefinition.getX(), + fontHighlightColorDefinition.getY(), fontHighlightColorDefinition.getZ(), + fontHighlightColorDefinition.getW()); + } + + Color fontDisabledColor; + final Vector4Definition fontDisabledColorDefinition = frameDefinition.getVector4("FontDisabledColor"); + if (fontDisabledColorDefinition == null) { + fontDisabledColor = null; + } + else { + fontDisabledColor = new Color(fontDisabledColorDefinition.getX(), + fontDisabledColorDefinition.getY(), fontDisabledColorDefinition.getZ(), + fontDisabledColorDefinition.getW()); + } + Color fontShadowColor; final Vector4Definition fontShadowColorDefinition = frameDefinition.getVector4("FontShadowColor"); if (fontShadowColorDefinition == null) { @@ -428,7 +451,7 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { textString = text; } final StringFrame stringFrame = new StringFrame(frameDefinition.getName(), parent, fontColor, justifyH, - justifyV, frameFont, textString); + justifyV, frameFont, textString, fontHighlightColor, fontDisabledColor); if (fontShadowColor != null) { final Vector2Definition shadowOffset = frameDefinition.getVector2("FontShadowOffset"); stringFrame.setFontShadowColor(fontShadowColor); @@ -850,7 +873,7 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { textString = text; } final StringFrame stringFrame = new StringFrame(frameDefinition.getName(), parent, fontColor, justifyH, - justifyV, frameFont, textString); + justifyV, frameFont, textString, null, null); inflatedFrame = stringFrame; break; case Texture: diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractRenderableFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractRenderableFrame.java index 1ca76a6..418840f 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractRenderableFrame.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractRenderableFrame.java @@ -23,7 +23,7 @@ public abstract class AbstractRenderableFrame implements UIFrame { private static final FramePoint[] TOP_ANCHOR_PRIORITY = { FramePoint.TOP, FramePoint.TOPLEFT, FramePoint.TOPRIGHT }; private static final FramePoint[] BOTTOM_ANCHOR_PRIORITY = { FramePoint.BOTTOM, FramePoint.BOTTOMLEFT, FramePoint.BOTTOMRIGHT }; - private static final boolean DEBUG_LOG = true; + private static final boolean DEBUG_LOG = false; protected String name; protected UIFrame parent; protected boolean visible = true; diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/GlueButtonFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/GlueButtonFrame.java index de20d4a..f71be58 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/frames/GlueButtonFrame.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/GlueButtonFrame.java @@ -54,6 +54,10 @@ public class GlueButtonFrame extends AbstractRenderableFrame implements Clickabl } } + public boolean isEnabled() { + return this.enabled; + } + public void setHighlightOnMouseOver(final boolean highlightOnMouseOver) { this.highlightOnMouseOver = highlightOnMouseOver; } @@ -112,12 +116,20 @@ public class GlueButtonFrame extends AbstractRenderableFrame implements Clickabl public void mouseEnter(final GameUI gameUI, final Viewport uiViewport) { if (this.highlightOnMouseOver) { this.mouseOver = true; + onMouseEnter(); } } + protected void onMouseEnter() { + } + @Override public void mouseExit(final GameUI gameUI, final Viewport uiViewport) { this.mouseOver = false; + onMouseExit(); + } + + protected void onMouseExit() { } @Override diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/GlueTextButtonFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/GlueTextButtonFrame.java index a64db3c..fc0ba43 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/frames/GlueTextButtonFrame.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/GlueTextButtonFrame.java @@ -1,5 +1,6 @@ package com.etheller.warsmash.parsers.fdf.frames; +import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.GlyphLayout; import com.badlogic.gdx.graphics.g2d.SpriteBatch; @@ -32,4 +33,45 @@ public class GlueTextButtonFrame extends GlueButtonFrame { this.buttonText.render(batch, baseFont, glyphLayout); } } + + @Override + public void setEnabled(final boolean enabled) { + super.setEnabled(enabled); + if (this.buttonText instanceof StringFrame) { + final StringFrame stringButtonText = (StringFrame) this.buttonText; + final Color fontColor = enabled ? stringButtonText.getFontOriginalColor() + : stringButtonText.getFontDisabledColor(); + if (fontColor != null) { + stringButtonText.setColor(fontColor); + } + } + } + + @Override + protected void onMouseEnter() { + super.onMouseEnter(); + if (isEnabled()) { + if (this.buttonText instanceof StringFrame) { + final StringFrame stringFrame = (StringFrame) this.buttonText; + final Color fontHighlightColor = stringFrame.getFontHighlightColor(); + if (fontHighlightColor != null) { + stringFrame.setColor(fontHighlightColor); + } + } + } + } + + @Override + protected void onMouseExit() { + super.onMouseExit(); + if (isEnabled()) { + if (this.buttonText instanceof StringFrame) { + final StringFrame stringFrame = (StringFrame) this.buttonText; + final Color fontOriginalColor = stringFrame.getFontOriginalColor(); + if (fontOriginalColor != null) { + stringFrame.setColor(fontOriginalColor); + } + } + } + } } diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/SingleStringFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/SingleStringFrame.java index 4ea4756..b6968d4 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/frames/SingleStringFrame.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/SingleStringFrame.java @@ -40,6 +40,10 @@ public class SingleStringFrame extends AbstractRenderableFrame { this.color = color; } + public Color getColor() { + return this.color; + } + public void setFontShadowColor(final Color fontShadowColor) { this.fontShadowColor = fontShadowColor; } diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/StringFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/StringFrame.java index ad8e2ed..11cfeb1 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/frames/StringFrame.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/StringFrame.java @@ -30,15 +30,22 @@ public class StringFrame extends AbstractRenderableFrame { private float predictedViewportHeight; static ShapeRenderer shapeRenderer = new ShapeRenderer(); + private final Color fontHighlightColor; + private final Color fontDisabledColor; + private final Color fontColor; public StringFrame(final String name, final UIFrame parent, final Color color, final TextJustify justifyH, - final TextJustify justifyV, final BitmapFont frameFont, final String text) { + final TextJustify justifyV, final BitmapFont frameFont, final String text, final Color fontHighlightColor, + final Color fontDisabledColor) { super(name, parent); + this.fontColor = color; this.color = color; this.justifyH = justifyH; this.justifyV = justifyV; this.frameFont = frameFont; this.text = text; + this.fontHighlightColor = fontHighlightColor; + this.fontDisabledColor = fontDisabledColor; this.internalFramesContainer = new SimpleFrame(null, this); } @@ -55,16 +62,30 @@ public class StringFrame extends AbstractRenderableFrame { } public void setColor(final Color color) { - this.color = color; for (final SingleStringFrame internalFrame : this.internalFrames) { - internalFrame.setColor(color); + if (internalFrame.getColor() == this.color) { + internalFrame.setColor(color); + } } + this.color = color; } public Color getColor() { return this.color; } + public Color getFontOriginalColor() { + return this.fontColor; + } + + public Color getFontDisabledColor() { + return this.fontDisabledColor; + } + + public Color getFontHighlightColor() { + return this.fontHighlightColor; + } + public void setFontShadowColor(final Color fontShadowColor) { this.fontShadowColor = fontShadowColor; for (final SingleStringFrame internalFrame : this.internalFrames) { diff --git a/core/src/com/etheller/warsmash/parsers/jass/Jass2.java b/core/src/com/etheller/warsmash/parsers/jass/Jass2.java index e08320d..6246602 100644 --- a/core/src/com/etheller/warsmash/parsers/jass/Jass2.java +++ b/core/src/com/etheller/warsmash/parsers/jass/Jass2.java @@ -2,6 +2,8 @@ package com.etheller.warsmash.parsers.jass; import java.awt.geom.Point2D; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Locale; @@ -12,6 +14,8 @@ import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.Recognizer; import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.math.Rectangle; +import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.viewport.Viewport; import com.etheller.interpreter.JassLexer; import com.etheller.interpreter.JassParser; @@ -42,6 +46,7 @@ import com.etheller.warsmash.parsers.fdf.datamodel.FramePoint; import com.etheller.warsmash.parsers.fdf.frames.SetPoint; import com.etheller.warsmash.parsers.fdf.frames.StringFrame; import com.etheller.warsmash.parsers.fdf.frames.UIFrame; +import com.etheller.warsmash.parsers.jass.scope.CommonTriggerExecutionScope; import com.etheller.warsmash.parsers.jass.triggers.BoolExprAnd; import com.etheller.warsmash.parsers.jass.triggers.BoolExprCondition; import com.etheller.warsmash.parsers.jass.triggers.BoolExprFilter; @@ -52,12 +57,17 @@ import com.etheller.warsmash.parsers.jass.triggers.TriggerCondition; import com.etheller.warsmash.units.Element; import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject; import com.etheller.warsmash.util.War3ID; +import com.etheller.warsmash.util.WarsmashConstants; import com.etheller.warsmash.viewer5.Scene; import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer; import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.ItemUI; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CDestructableType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitEnumFunction; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitType; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.ai.AIDifficulty; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.config.CPlayerAPI; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.config.War3MapConfig; @@ -75,6 +85,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayerStat import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CRace; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CRacePreference; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CStartLocPrio; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.region.CRegion; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.state.CGameState; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.state.CUnitState; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.timers.CTimerJass; @@ -557,6 +568,8 @@ public class Jass2 { public CommonEnvironment(final JassProgramVisitor jassProgramVisitor, final DataSource dataSource, final Viewport uiViewport, final Scene uiScene, final War3MapViewer war3MapViewer, final RootFrameListener rootFrameListener) { + final Rectangle tempRect = new Rectangle(); + final CSimulation simulation = war3MapViewer.simulation; final GlobalScope globals = jassProgramVisitor.getGlobals(); final HandleJassType agentType = globals.registerHandleType("agent"); final HandleJassType eventType = globals.registerHandleType("event"); @@ -1022,8 +1035,7 @@ public class Jass2 { public JassValue call(final List arguments, final GlobalScope globalScope, final TriggerExecutionScope triggerScope) { final String idString = arguments.get(0).visit(StringJassValueVisitor.getInstance()); - final CUnitType unitType = war3MapViewer.simulation.getUnitData() - .getUnitTypeByJassLegacyName(idString); + final CUnitType unitType = simulation.getUnitData().getUnitTypeByJassLegacyName(idString); if (unitType == null) { return new IntegerJassValue(0); } @@ -1036,8 +1048,7 @@ public class Jass2 { final TriggerExecutionScope triggerScope) { final Integer id = arguments.get(0).visit(IntegerJassValueVisitor.getInstance()); final War3ID war3id = new War3ID(id); - return new StringJassValue( - war3MapViewer.simulation.getUnitData().getUnitType(war3id).getLegacyName()); + return new StringJassValue(simulation.getUnitData().getUnitType(war3id).getLegacyName()); } }); jassProgramVisitor.getJassNativeManager().createNative("AbilityId", new JassFunction() { @@ -1060,7 +1071,6 @@ public class Jass2 { final TriggerExecutionScope triggerScope) { final Integer id = arguments.get(0).visit(IntegerJassValueVisitor.getInstance()); final War3ID war3id = new War3ID(id); - final CSimulation simulation = war3MapViewer.simulation; final CUnitType unitType = simulation.getUnitData().getUnitType(war3id); if (unitType != null) { return new StringJassValue(unitType.getName()); @@ -1328,8 +1338,11 @@ public class Jass2 { final War3MapConfig mapConfig = war3MapViewer.getMapConfig(); registerConfigNatives(jassProgramVisitor, mapConfig, startlocprioType, gametypeType, placementType, gamespeedType, gamedifficultyType, mapdensityType, locationType, playerType, playercolorType, - mapcontrolType, playerslotstateType, war3MapViewer.simulation); + mapcontrolType, playerslotstateType, simulation); + // ============================================================================ + // Timer API + // jassProgramVisitor.getJassNativeManager().createNative("CreateTimer", new JassFunction() { @Override public JassValue call(final List arguments, final GlobalScope globalScope, @@ -1342,7 +1355,7 @@ public class Jass2 { public JassValue call(final List arguments, final GlobalScope globalScope, final TriggerExecutionScope triggerScope) { final CTimerJass timer = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); - war3MapViewer.simulation.unregisterTimer(timer); + simulation.unregisterTimer(timer); return null; } }); @@ -1358,7 +1371,7 @@ public class Jass2 { timer.setTimeoutTime(timeout.floatValue()); timer.setRepeats(periodic); timer.setHandlerFunc(handlerFunc); - timer.start(war3MapViewer.simulation); + timer.start(simulation); } return null; } @@ -1368,7 +1381,7 @@ public class Jass2 { public JassValue call(final List arguments, final GlobalScope globalScope, final TriggerExecutionScope triggerScope) { final CTimerJass timer = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); - return new RealJassValue(timer.getElapsed(war3MapViewer.simulation)); + return new RealJassValue(timer.getElapsed(simulation)); } }); jassProgramVisitor.getJassNativeManager().createNative("TimerGetRemaining", new JassFunction() { @@ -1376,7 +1389,7 @@ public class Jass2 { public JassValue call(final List arguments, final GlobalScope globalScope, final TriggerExecutionScope triggerScope) { final CTimerJass timer = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); - return new RealJassValue(timer.getRemaining(war3MapViewer.simulation)); + return new RealJassValue(timer.getRemaining(simulation)); } }); jassProgramVisitor.getJassNativeManager().createNative("TimerGetTimeout", new JassFunction() { @@ -1392,7 +1405,7 @@ public class Jass2 { public JassValue call(final List arguments, final GlobalScope globalScope, final TriggerExecutionScope triggerScope) { final CTimerJass timer = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); - timer.pause(war3MapViewer.simulation); + timer.pause(simulation); return null; } }); @@ -1401,10 +1414,951 @@ public class Jass2 { public JassValue call(final List arguments, final GlobalScope globalScope, final TriggerExecutionScope triggerScope) { final CTimerJass timer = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); - timer.resume(war3MapViewer.simulation); + timer.resume(simulation); return null; } }); + + // ============================================================================ + // Group API + // + jassProgramVisitor.getJassNativeManager().createNative("CreateGroup", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + return new HandleJassValue(groupType, new ArrayList()); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("DestroyGroup", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + System.err.println( + "DestroyGroup called but in Java we don't have a destructor, so we need to unregister later when that is implemented"); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GroupAddUnit", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + final CUnit whichUnit = arguments.get(1).visit(ObjectJassValueVisitor.getInstance()); + group.add(whichUnit); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GroupRemoveUnit", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + final CUnit whichUnit = arguments.get(1).visit(ObjectJassValueVisitor.getInstance()); + group.remove(whichUnit); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GroupClear", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + group.clear(); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GroupEnumUnitsOfType", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + final String unitname = arguments.get(1).visit(StringJassValueVisitor.getInstance()); + final TriggerBooleanExpression filter = arguments.get(2) + .visit(ObjectJassValueVisitor.getInstance()); + for (final CUnit unit : simulation.getUnits()) { + if (unitname.equals(unit.getUnitType().getLegacyName())) { + if (filter.evaluate(globalScope, + CommonTriggerExecutionScope.filterScope(triggerScope, unit))) { + // TODO the trigger scope for evaluation here might need to be a clean one? + group.add(unit); + } + } + } + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GroupEnumUnitsOfPlayer", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + final CPlayerJass player = arguments.get(1) + .visit(ObjectJassValueVisitor.getInstance()); + final TriggerBooleanExpression filter = arguments.get(2) + .visit(ObjectJassValueVisitor.getInstance()); + for (final CUnit unit : simulation.getUnits()) { + if (unit.getPlayerIndex() == player.getId()) { + if (filter.evaluate(globalScope, + CommonTriggerExecutionScope.filterScope(triggerScope, unit))) { + // TODO the trigger scope for evaluation here might need to be a clean one? + group.add(unit); + } + } + } + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GroupEnumUnitsOfTypeCounted", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + final String unitname = arguments.get(1).visit(StringJassValueVisitor.getInstance()); + final TriggerBooleanExpression filter = arguments.get(2) + .visit(ObjectJassValueVisitor.getInstance()); + final Integer countLimit = arguments.get(3).visit(IntegerJassValueVisitor.getInstance()); + int count = 0; + for (final CUnit unit : simulation.getUnits()) { + if (unitname.equals(unit.getUnitType().getLegacyName())) { + if (filter.evaluate(globalScope, + CommonTriggerExecutionScope.filterScope(triggerScope, unit))) { + // TODO the trigger scope for evaluation here might need to be a clean one? + group.add(unit); + count++; + if (count >= countLimit) { + break; + } + } + } + } + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GroupEnumUnitsInRect", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + final Rectangle rect = arguments.get(1).visit(ObjectJassValueVisitor.getInstance()); + final TriggerBooleanExpression filter = arguments.get(2) + .visit(ObjectJassValueVisitor.getInstance()); + simulation.getWorldCollision().enumUnitsInRect(rect, new CUnitEnumFunction() { + @Override + public boolean call(final CUnit unit) { + if (filter.evaluate(globalScope, + CommonTriggerExecutionScope.filterScope(triggerScope, unit))) { + // TODO the trigger scope for evaluation here might need to be a clean one? + group.add(unit); + } + return false; + } + }); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GroupEnumUnitsInRectCounted", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + final Rectangle rect = arguments.get(1).visit(ObjectJassValueVisitor.getInstance()); + final TriggerBooleanExpression filter = arguments.get(2) + .visit(ObjectJassValueVisitor.getInstance()); + final Integer countLimit = arguments.get(3).visit(IntegerJassValueVisitor.getInstance()); + simulation.getWorldCollision().enumUnitsInRect(rect, new CUnitEnumFunction() { + int count = 0; + + @Override + public boolean call(final CUnit unit) { + if (filter.evaluate(globalScope, + CommonTriggerExecutionScope.filterScope(triggerScope, unit))) { + // TODO the trigger scope for evaluation here might need to be a clean one? + group.add(unit); + this.count++; + if (this.count >= countLimit) { + return true; + } + } + return false; + } + }); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GroupEnumUnitsInRange", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + final float x = arguments.get(1).visit(RealJassValueVisitor.getInstance()).floatValue(); + final float y = arguments.get(2).visit(RealJassValueVisitor.getInstance()).floatValue(); + final float radius = arguments.get(3).visit(RealJassValueVisitor.getInstance()).floatValue(); + final TriggerBooleanExpression filter = arguments.get(4) + .visit(ObjectJassValueVisitor.getInstance()); + simulation.getWorldCollision().enumUnitsInRect(tempRect.set(x - radius, y - radius, radius, radius), + new CUnitEnumFunction() { + + @Override + public boolean call(final CUnit unit) { + if (unit.distance(x, y) <= radius) { + if (filter.evaluate(globalScope, + CommonTriggerExecutionScope.filterScope(triggerScope, unit))) { + // TODO the trigger scope for evaluation here might need to be a clean one? + group.add(unit); + } + } + return false; + } + }); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GroupEnumUnitsInRangeOfLoc", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + final Point2D.Double whichLocation = arguments.get(1) + .visit(ObjectJassValueVisitor.getInstance()); + final float x = (float) whichLocation.x; + final float y = (float) whichLocation.y; + final float radius = arguments.get(2).visit(RealJassValueVisitor.getInstance()).floatValue(); + final TriggerBooleanExpression filter = arguments.get(3) + .visit(ObjectJassValueVisitor.getInstance()); + simulation.getWorldCollision().enumUnitsInRect(tempRect.set(x - radius, y - radius, radius, radius), + new CUnitEnumFunction() { + + @Override + public boolean call(final CUnit unit) { + if (unit.distance(x, y) <= radius) { + if (filter.evaluate(globalScope, + CommonTriggerExecutionScope.filterScope(triggerScope, unit))) { + // TODO the trigger scope for evaluation here might need to be a clean one? + group.add(unit); + } + } + return false; + } + }); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GroupEnumUnitsInRangeCounted", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + final float x = arguments.get(1).visit(RealJassValueVisitor.getInstance()).floatValue(); + final float y = arguments.get(2).visit(RealJassValueVisitor.getInstance()).floatValue(); + final float radius = arguments.get(3).visit(RealJassValueVisitor.getInstance()).floatValue(); + final TriggerBooleanExpression filter = arguments.get(4) + .visit(ObjectJassValueVisitor.getInstance()); + final Integer countLimit = arguments.get(5).visit(IntegerJassValueVisitor.getInstance()); + simulation.getWorldCollision().enumUnitsInRect(tempRect.set(x - radius, y - radius, radius, radius), + new CUnitEnumFunction() { + int count = 0; + + @Override + public boolean call(final CUnit unit) { + if (unit.distance(x, y) <= radius) { + if (filter.evaluate(globalScope, + CommonTriggerExecutionScope.filterScope(triggerScope, unit))) { + // TODO the trigger scope for evaluation here might need to be a clean one? + group.add(unit); + this.count++; + if (this.count >= countLimit) { + return true; + } + } + } + return false; + } + }); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GroupEnumUnitsInRangeOfLocCounted", + new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0) + .visit(ObjectJassValueVisitor.>getInstance()); + final Point2D.Double whichLocation = arguments.get(1) + .visit(ObjectJassValueVisitor.getInstance()); + final float x = (float) whichLocation.x; + final float y = (float) whichLocation.y; + final float radius = arguments.get(2).visit(RealJassValueVisitor.getInstance()) + .floatValue(); + final TriggerBooleanExpression filter = arguments.get(3) + .visit(ObjectJassValueVisitor.getInstance()); + final Integer countLimit = arguments.get(4).visit(IntegerJassValueVisitor.getInstance()); + simulation.getWorldCollision().enumUnitsInRect( + tempRect.set(x - radius, y - radius, radius, radius), new CUnitEnumFunction() { + int count = 0; + + @Override + public boolean call(final CUnit unit) { + if (unit.distance(x, y) <= radius) { + if (filter.evaluate(globalScope, + CommonTriggerExecutionScope.filterScope(triggerScope, unit))) { + // TODO the trigger scope for evaluation here might need to be a + // clean one? + group.add(unit); + this.count++; + if (this.count >= countLimit) { + return true; + } + } + } + return false; + } + }); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GroupEnumUnitsSelected", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + final CPlayerJass whyichPlayer = arguments.get(1) + .visit(ObjectJassValueVisitor.getInstance()); + final TriggerBooleanExpression filter = arguments.get(2) + .visit(ObjectJassValueVisitor.getInstance()); + throw new UnsupportedOperationException("GroupEnumUnitsSelected not supported yet."); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GroupImmediateOrder", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + final String order = arguments.get(1).visit(StringJassValueVisitor.getInstance()); + final int orderId = OrderIdUtils.getOrderId(order); + boolean success = true; + for (final CUnit unit : group) { + success &= unit.order(simulation, orderId, null); + } + return BooleanJassValue.of(success); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GroupImmediateOrderById", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + final int order = arguments.get(1).visit(IntegerJassValueVisitor.getInstance()); + boolean success = true; + for (final CUnit unit : group) { + success &= unit.order(simulation, order, null); + } + return BooleanJassValue.of(success); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GroupPointOrder", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + final String order = arguments.get(1).visit(StringJassValueVisitor.getInstance()); + final Double x = arguments.get(2).visit(RealJassValueVisitor.getInstance()); + final Double y = arguments.get(3).visit(RealJassValueVisitor.getInstance()); + final AbilityPointTarget target = new AbilityPointTarget(x.floatValue(), y.floatValue()); + final int orderId = OrderIdUtils.getOrderId(order); + boolean success = true; + for (final CUnit unit : group) { + success &= unit.order(simulation, orderId, target); + } + return BooleanJassValue.of(success); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GroupPointOrderLoc", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + final String order = arguments.get(1).visit(StringJassValueVisitor.getInstance()); + final Point2D.Double whichLocation = arguments.get(2) + .visit(ObjectJassValueVisitor.getInstance()); + final AbilityPointTarget target = new AbilityPointTarget((float) whichLocation.x, + (float) whichLocation.y); + final int orderId = OrderIdUtils.getOrderId(order); + boolean success = true; + for (final CUnit unit : group) { + success &= unit.order(simulation, orderId, target); + } + return BooleanJassValue.of(success); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GroupPointOrderById", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + final int orderId = arguments.get(1).visit(IntegerJassValueVisitor.getInstance()); + final Double x = arguments.get(2).visit(RealJassValueVisitor.getInstance()); + final Double y = arguments.get(3).visit(RealJassValueVisitor.getInstance()); + final AbilityPointTarget target = new AbilityPointTarget(x.floatValue(), y.floatValue()); + boolean success = true; + for (final CUnit unit : group) { + success &= unit.order(simulation, orderId, target); + } + return BooleanJassValue.of(success); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GroupPointOrderByIdLoc", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + final int orderId = arguments.get(1).visit(IntegerJassValueVisitor.getInstance()); + final Point2D.Double whichLocation = arguments.get(2) + .visit(ObjectJassValueVisitor.getInstance()); + final AbilityPointTarget target = new AbilityPointTarget((float) whichLocation.x, + (float) whichLocation.y); + boolean success = true; + for (final CUnit unit : group) { + success &= unit.order(simulation, orderId, target); + } + return BooleanJassValue.of(success); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GroupTargetOrder", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + final String order = arguments.get(1).visit(StringJassValueVisitor.getInstance()); + final CWidget target = arguments.get(2).visit(ObjectJassValueVisitor.getInstance()); + final int orderId = OrderIdUtils.getOrderId(order); + boolean success = true; + for (final CUnit unit : group) { + success &= unit.order(simulation, orderId, target); + } + return BooleanJassValue.of(success); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GroupTargetOrderById", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + final int orderId = arguments.get(1).visit(IntegerJassValueVisitor.getInstance()); + final CWidget target = arguments.get(2).visit(ObjectJassValueVisitor.getInstance()); + boolean success = true; + for (final CUnit unit : group) { + success &= unit.order(simulation, orderId, target); + } + return BooleanJassValue.of(success); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("ForGroup", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + final JassFunction callback = arguments.get(1).visit(JassFunctionJassValueVisitor.getInstance()); + for (final CUnit unit : group) { + callback.call(Collections.emptyList(), globalScope, + CommonTriggerExecutionScope.enumScope(triggerScope, unit)); + } + return new HandleJassValue(unitType, group.get(0)); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("FirstOfGroup", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List group = arguments.get(0).visit(ObjectJassValueVisitor.>getInstance()); + return new HandleJassValue(unitType, group.get(0)); + } + }); + // ============================================================================ + // Force API + // + jassProgramVisitor.getJassNativeManager().createNative("CreateForce", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + return new HandleJassValue(forceType, new ArrayList()); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("DestroyForce", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List force = arguments.get(0) + .visit(ObjectJassValueVisitor.>getInstance()); + System.err.println( + "DestroyForce called but in Java we don't have a destructor, so we need to unregister later when that is implemented"); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("ForceAddPlayer", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List force = arguments.get(0) + .visit(ObjectJassValueVisitor.>getInstance()); + final CPlayerJass player = arguments.get(1) + .visit(ObjectJassValueVisitor.getInstance()); + force.add(player); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("ForceRemovePlayer", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List force = arguments.get(0) + .visit(ObjectJassValueVisitor.>getInstance()); + final CPlayerJass player = arguments.get(1) + .visit(ObjectJassValueVisitor.getInstance()); + force.remove(player); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("ForceClear", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List force = arguments.get(0) + .visit(ObjectJassValueVisitor.>getInstance()); + force.clear(); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("ForceEnumPlayers", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List force = arguments.get(0) + .visit(ObjectJassValueVisitor.>getInstance()); + final TriggerBooleanExpression filter = arguments.get(1) + .visit(ObjectJassValueVisitor.getInstance()); + for (int i = 0; i < WarsmashConstants.MAX_PLAYERS; i++) { + final CPlayerJass jassPlayer = simulation.getPlayer(i); + if (filter.evaluate(globalScope, + CommonTriggerExecutionScope.filterScope(triggerScope, jassPlayer))) { + force.add(jassPlayer); + } + } + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("ForceEnumPlayersCounted", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List force = arguments.get(0) + .visit(ObjectJassValueVisitor.>getInstance()); + final TriggerBooleanExpression filter = arguments.get(1) + .visit(ObjectJassValueVisitor.getInstance()); + final Integer countLimit = arguments.get(2).visit(IntegerJassValueVisitor.getInstance()); + int count = 0; + for (int i = 0; (i < WarsmashConstants.MAX_PLAYERS) && (count < countLimit); i++) { + final CPlayerJass jassPlayer = simulation.getPlayer(i); + if (filter.evaluate(globalScope, + CommonTriggerExecutionScope.filterScope(triggerScope, jassPlayer))) { + force.add(jassPlayer); + count++; + } + } + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("ForceEnumAllies", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List force = arguments.get(0) + .visit(ObjectJassValueVisitor.>getInstance()); + final CPlayerJass player = arguments.get(1) + .visit(ObjectJassValueVisitor.getInstance()); + final TriggerBooleanExpression filter = arguments.get(2) + .visit(ObjectJassValueVisitor.getInstance()); + for (int i = 0; i < WarsmashConstants.MAX_PLAYERS; i++) { + final CPlayerJass jassPlayer = simulation.getPlayer(i); + if (player.hasAlliance(i, CAllianceType.PASSIVE)) { + if (filter.evaluate(globalScope, + CommonTriggerExecutionScope.filterScope(triggerScope, jassPlayer))) { + force.add(jassPlayer); + } + } + } + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("ForceEnumEnemies", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List force = arguments.get(0) + .visit(ObjectJassValueVisitor.>getInstance()); + final CPlayerJass player = arguments.get(1) + .visit(ObjectJassValueVisitor.getInstance()); + final TriggerBooleanExpression filter = arguments.get(2) + .visit(ObjectJassValueVisitor.getInstance()); + for (int i = 0; i < WarsmashConstants.MAX_PLAYERS; i++) { + final CPlayerJass jassPlayer = simulation.getPlayer(i); + if (!player.hasAlliance(i, CAllianceType.PASSIVE)) { + if (filter.evaluate(globalScope, + CommonTriggerExecutionScope.filterScope(triggerScope, jassPlayer))) { + force.add(jassPlayer); + } + } + } + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("ForForce", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final List force = arguments.get(0) + .visit(ObjectJassValueVisitor.>getInstance()); + final JassFunction callback = arguments.get(1).visit(JassFunctionJassValueVisitor.getInstance()); + for (final CPlayerJass player : force) { + callback.call(Collections.emptyList(), globalScope, + CommonTriggerExecutionScope.enumScope(triggerScope, player)); + } + return null; + } + }); + // ============================================================================ + // Region and Location API + // + jassProgramVisitor.getJassNativeManager().createNative("Rect", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final float minx = arguments.get(0).visit(RealJassValueVisitor.getInstance()).floatValue(); + final float miny = arguments.get(1).visit(RealJassValueVisitor.getInstance()).floatValue(); + final float maxx = arguments.get(2).visit(RealJassValueVisitor.getInstance()).floatValue(); + final float maxy = arguments.get(3).visit(RealJassValueVisitor.getInstance()).floatValue(); + return new HandleJassValue(rectType, new Rectangle(minx, miny, maxx - minx, maxy - miny)); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("RectFromLoc", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final Point2D.Double min = arguments.get(0) + .visit(ObjectJassValueVisitor.getInstance()); + final Point2D.Double max = arguments.get(1) + .visit(ObjectJassValueVisitor.getInstance()); + final float minx = (float) min.x; + final float miny = (float) min.y; + final float maxx = (float) max.x; + final float maxy = (float) max.y; + return new HandleJassValue(rectType, new Rectangle(minx, miny, maxx - minx, maxy - miny)); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("RemoveRect", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final Rectangle rect = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); + System.err.println( + "RemoveRect called but in Java we don't have a destructor, so we need to unregister later when that is implemented"); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("SetRect", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final Rectangle rect = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); + final float minx = arguments.get(1).visit(RealJassValueVisitor.getInstance()).floatValue(); + final float miny = arguments.get(2).visit(RealJassValueVisitor.getInstance()).floatValue(); + final float maxx = arguments.get(3).visit(RealJassValueVisitor.getInstance()).floatValue(); + final float maxy = arguments.get(4).visit(RealJassValueVisitor.getInstance()).floatValue(); + rect.set(minx, miny, maxx - minx, maxy - miny); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("SetRectFromLoc", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final Rectangle rect = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); + final Point2D.Double min = arguments.get(1) + .visit(ObjectJassValueVisitor.getInstance()); + final Point2D.Double max = arguments.get(2) + .visit(ObjectJassValueVisitor.getInstance()); + final float minx = (float) min.x; + final float miny = (float) min.y; + final float maxx = (float) max.x; + final float maxy = (float) max.y; + rect.set(minx, miny, maxx - minx, maxy - miny); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("MoveRectTo", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final Rectangle rect = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); + final float newCenterX = arguments.get(1).visit(RealJassValueVisitor.getInstance()).floatValue(); + final float newCenterY = arguments.get(2).visit(RealJassValueVisitor.getInstance()).floatValue(); + rect.setCenter(newCenterX, newCenterY); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("MoveRectToLoc", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final Rectangle rect = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); + final Point2D.Double newCenterLoc = arguments.get(1) + .visit(ObjectJassValueVisitor.getInstance()); + rect.setCenter((float) newCenterLoc.x, (float) newCenterLoc.y); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GetRectCenterX", new JassFunction() { + Vector2 centerHeap = new Vector2(); + + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final Rectangle rect = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); + return new RealJassValue(rect.getCenter(this.centerHeap).x); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GetRectCenterY", new JassFunction() { + Vector2 centerHeap = new Vector2(); + + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final Rectangle rect = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); + return new RealJassValue(rect.getCenter(this.centerHeap).y); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GetRectMinX", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final Rectangle rect = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); + return new RealJassValue(rect.getX()); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GetRectMinY", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final Rectangle rect = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); + return new RealJassValue(rect.getY()); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GetRectMaxX", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final Rectangle rect = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); + return new RealJassValue(rect.getX() + rect.getWidth()); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GetRectMaxY", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final Rectangle rect = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); + return new RealJassValue(rect.getY() + rect.getHeight()); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("CreateRegion", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + return new HandleJassValue(regionType, new CRegion()); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("RemoveRegion", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final CRegion region = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); + region.remove(simulation.getRegionManager()); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("RegionAddRect", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final CRegion region = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); + final Rectangle rect = arguments.get(1).visit(ObjectJassValueVisitor.getInstance()); + region.addRect(rect, simulation.getRegionManager()); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("RegionClearRect", new JassFunction() { + + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final CRegion region = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); + final Rectangle rect = arguments.get(1).visit(ObjectJassValueVisitor.getInstance()); + region.clearRect(rect, simulation.getRegionManager()); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("RegionAddCell", new JassFunction() { + + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final CRegion region = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); + final float x = arguments.get(1).visit(RealJassValueVisitor.getInstance()).floatValue(); + final float y = arguments.get(2).visit(RealJassValueVisitor.getInstance()).floatValue(); + region.addCell(x, y, simulation.getRegionManager()); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("RegionAddCellAtLoc", new JassFunction() { + + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final CRegion region = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); + final Point2D.Double whichLocation = arguments.get(1) + .visit(ObjectJassValueVisitor.getInstance()); + region.addCell((float) whichLocation.x, (float) whichLocation.y, simulation.getRegionManager()); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("RegionClearCell", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final CRegion region = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); + final float x = arguments.get(1).visit(RealJassValueVisitor.getInstance()).floatValue(); + final float y = arguments.get(2).visit(RealJassValueVisitor.getInstance()).floatValue(); + region.clearCell(x, y, simulation.getRegionManager()); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("RegionClearCellAtLoc", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final CRegion region = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); + final Point2D.Double whichLocation = arguments.get(1) + .visit(ObjectJassValueVisitor.getInstance()); + region.clearCell((float) whichLocation.x, (float) whichLocation.y, simulation.getRegionManager()); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("Location", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final float x = arguments.get(0).visit(RealJassValueVisitor.getInstance()).floatValue(); + final float y = arguments.get(1).visit(RealJassValueVisitor.getInstance()).floatValue(); + return new HandleJassValue(locationType, new Point2D.Double(x, y)); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("RemoveLocation", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final Point2D.Double whichLocation = arguments.get(0) + .visit(ObjectJassValueVisitor.getInstance()); + System.err.println( + "RemoveRect called but in Java we don't have a destructor, so we need to unregister later when that is implemented"); + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("MoveLocation", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final Point2D.Double whichLocation = arguments.get(0) + .visit(ObjectJassValueVisitor.getInstance()); + final float x = arguments.get(1).visit(RealJassValueVisitor.getInstance()).floatValue(); + final float y = arguments.get(2).visit(RealJassValueVisitor.getInstance()).floatValue(); + whichLocation.x = x; + whichLocation.y = y; + return null; + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GetLocationX", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final Point2D.Double whichLocation = arguments.get(0) + .visit(ObjectJassValueVisitor.getInstance()); + return new RealJassValue(whichLocation.x); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GetLocationY", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final Point2D.Double whichLocation = arguments.get(0) + .visit(ObjectJassValueVisitor.getInstance()); + return new RealJassValue(whichLocation.y); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GetLocationZ", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final Point2D.Double whichLocation = arguments.get(0) + .visit(ObjectJassValueVisitor.getInstance()); + return new RealJassValue( + war3MapViewer.terrain.getGroundHeight((float) whichLocation.x, (float) whichLocation.y)); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("IsUnitInRegion", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final CRegion whichRegion = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); + final CUnit whichUnit = arguments.get(1).visit(ObjectJassValueVisitor.getInstance()); + return BooleanJassValue.of(whichUnit.isInRegion(whichRegion)); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("IsPointInRegion", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final CRegion whichRegion = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); + final float x = arguments.get(1).visit(RealJassValueVisitor.getInstance()).floatValue(); + final float y = arguments.get(2).visit(RealJassValueVisitor.getInstance()).floatValue(); + return BooleanJassValue.of(whichRegion.contains(x, y, simulation.getRegionManager())); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("IsLocationInRegion", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final CRegion whichRegion = arguments.get(0).visit(ObjectJassValueVisitor.getInstance()); + final Point2D.Double whichLocation = arguments.get(1) + .visit(ObjectJassValueVisitor.getInstance()); + return BooleanJassValue.of(whichRegion.contains((float) whichLocation.x, (float) whichLocation.y, + simulation.getRegionManager())); + } + }); + jassProgramVisitor.getJassNativeManager().createNative("GetWorldBounds", new JassFunction() { + @Override + public JassValue call(final List arguments, final GlobalScope globalScope, + final TriggerExecutionScope triggerScope) { + final float worldMinX = simulation.getPathingGrid().getWorldX(0) - 16f; + final float worldMinY = simulation.getPathingGrid().getWorldY(0) - 16f; + final float worldMaxX = simulation.getPathingGrid() + .getWorldX(simulation.getPathingGrid().getWidth() - 1) + 16f; + final float worldMaxY = simulation.getPathingGrid() + .getWorldY(simulation.getPathingGrid().getHeight() - 1) + 16f; + return new HandleJassValue(rectType, + new Rectangle(worldMinX, worldMinY, worldMaxX - worldMinX, worldMaxY - worldMinY)); + } + }); + } private void registerConfigNatives(final JassProgramVisitor jassProgramVisitor, final War3MapConfig mapConfig, diff --git a/core/src/com/etheller/warsmash/parsers/jass/scope/CommonTriggerExecutionScope.java b/core/src/com/etheller/warsmash/parsers/jass/scope/CommonTriggerExecutionScope.java new file mode 100644 index 0000000..c6d6564 --- /dev/null +++ b/core/src/com/etheller/warsmash/parsers/jass/scope/CommonTriggerExecutionScope.java @@ -0,0 +1,64 @@ +package com.etheller.warsmash.parsers.jass.scope; + +import com.etheller.interpreter.ast.scope.TriggerExecutionScope; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayerJass; + +public class CommonTriggerExecutionScope extends TriggerExecutionScope { + private final CUnit triggeringUnit; + private final CUnit filterUnit; + private final CUnit enumUnit; + private final CPlayerJass filterPlayer; + private final CPlayerJass enumPlayer; + + public CommonTriggerExecutionScope(final TriggerExecutionScope parentScope, final CUnit triggeringUnit, + final CUnit filterUnit, final CUnit enumUnit, final CPlayerJass filterPlayer, + final CPlayerJass enumPlayer) { + super(parentScope.getTriggeringTrigger()); + this.triggeringUnit = triggeringUnit; + this.filterUnit = filterUnit; + this.enumUnit = enumUnit; + this.filterPlayer = filterPlayer; + this.enumPlayer = enumPlayer; + } + + public CUnit getEnumUnit() { + return this.enumUnit; + } + + public CUnit getTriggeringUnit() { + return this.triggeringUnit; + } + + public CUnit getFilterUnit() { + return this.filterUnit; + } + + public CPlayerJass getFilterPlayer() { + return this.filterPlayer; + } + + public CPlayerJass getEnumPlayer() { + return this.enumPlayer; + } + + public static CommonTriggerExecutionScope filterScope(final TriggerExecutionScope parentScope, + final CUnit filterUnit) { + return new CommonTriggerExecutionScope(parentScope, null, filterUnit, null, null, null); + } + + public static CommonTriggerExecutionScope enumScope(final TriggerExecutionScope parentScope, final CUnit enumUnit) { + return new CommonTriggerExecutionScope(parentScope, null, null, enumUnit, null, null); + } + + public static CommonTriggerExecutionScope filterScope(final TriggerExecutionScope parentScope, + final CPlayerJass filterPlayer) { + return new CommonTriggerExecutionScope(parentScope, null, null, null, filterPlayer, null); + } + + public static CommonTriggerExecutionScope enumScope(final TriggerExecutionScope parentScope, + final CPlayerJass enumPlayer) { + return new CommonTriggerExecutionScope(parentScope, null, null, null, null, enumPlayer); + } + +} diff --git a/core/src/com/etheller/warsmash/util/Quadtree.java b/core/src/com/etheller/warsmash/util/Quadtree.java index 6ab8595..81a9308 100644 --- a/core/src/com/etheller/warsmash/util/Quadtree.java +++ b/core/src/com/etheller/warsmash/util/Quadtree.java @@ -24,7 +24,8 @@ public class Quadtree { } public void add(final T object, final Rectangle bounds) { - add(new Node(object, bounds), 0); + final Node node = new Node(object, bounds); + add(node, 0); } public void remove(final T object, final Rectangle bounds) { diff --git a/core/src/com/etheller/warsmash/util/WarsmashConstants.java b/core/src/com/etheller/warsmash/util/WarsmashConstants.java index 694d5b2..6b2e4ab 100644 --- a/core/src/com/etheller/warsmash/util/WarsmashConstants.java +++ b/core/src/com/etheller/warsmash/util/WarsmashConstants.java @@ -6,7 +6,7 @@ public class WarsmashConstants { * With version, we use 0 for RoC, 1 for TFT emulation, and probably 2+ or * whatever for custom mods and other stuff */ - public static int GAME_VERSION = 0; + public static int GAME_VERSION = 1; public static final int REPLACEABLE_TEXTURE_LIMIT = 64; public static final float SIMULATION_STEP_TIME = 1 / 20f; public static final int PORT_NUMBER = 6115; diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/BatchGroup.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/BatchGroup.java index 0295e3c..d80a550 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/BatchGroup.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/BatchGroup.java @@ -97,7 +97,7 @@ public class BatchGroup extends GenericGroup { final float[] geosetColor = instance.geosetColors[geosetIndex]; final float layerAlpha = instance.layerAlphas[layerIndex]; - if ((geosetColor[3] > 0) && (layerAlpha > 0)) { + if ((geosetColor[3] > 0.01) && (layerAlpha > 0.01)) { // BELOW: I updated it to "Math.max(0," because MDL and MDX parser for PRSCMOD // menu screen behaved differently, // the MDL case was getting "no data" for default value when unanimated, and "no diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SdSequence.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SdSequence.java index 4708c73..5ec445c 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SdSequence.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SdSequence.java @@ -167,7 +167,7 @@ public final class SdSequence { } public int getValue(final TYPE out, final long frame) { - final int l = this.frames.length; + final int length = this.frames.length; if (this.constant || (frame < this.start)) { this.sd.copy(out, this.values[0]); @@ -175,34 +175,34 @@ public final class SdSequence { return -1; } else { - int startFrame = -1; - int endFrame = -1; - final int l1 = l - 1; - if ((frame < this.frames[0]) || (frame >= this.frames[l1])) { - startFrame = l1; - endFrame = 0; + int startFrameIndex = -1; + int endFrameIndex = -1; + final int lengthLessOne = length - 1; + if ((frame < this.frames[0]) || (frame >= this.frames[lengthLessOne])) { + startFrameIndex = lengthLessOne; + endFrameIndex = 0; } else { - for (int i = 1; i < l; i++) { + for (int i = 1; i < length; i++) { if (this.frames[i] > frame) { - startFrame = i - 1; - endFrame = i; + startFrameIndex = i - 1; + endFrameIndex = i; break; } } } - long start = this.frames[startFrame]; - final long end = this.frames[endFrame]; - long timeBetweenFrames = end - start; + long startFrame = this.frames[startFrameIndex]; + final long endFrame = this.frames[endFrameIndex]; + long timeBetweenFrames = endFrame - startFrame; if (timeBetweenFrames < 0) { timeBetweenFrames += (this.end - this.start); - if (frame < start) { - start = end; + if (frame < startFrame) { + startFrame = endFrame; } } - final float t = ((timeBetweenFrames) == 0 ? 0 : ((frame - start) / (float) (timeBetweenFrames))); - this.sd.interpolate(out, this.values, this.inTans, this.outTans, startFrame, endFrame, t); - return startFrame; + final float t = ((timeBetweenFrames) == 0 ? 0 : ((frame - startFrame) / (float) (timeBetweenFrames))); + this.sd.interpolate(out, this.values, this.inTans, this.outTans, startFrameIndex, endFrameIndex, t); + return startFrameIndex; } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java index 7778f4f..dbd628d 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java @@ -939,11 +939,10 @@ public class War3MapViewer extends AbstractMdxModelViewer { final RenderDestructable renderDestructable = new RenderDestructable(this, model, row, doodad, type, maxPitch, maxRoll, doodad.getLife(), destructableShadow, simulationDestructable); if (row.readSLKTagBoolean("walkable")) { + final float angle = doodad.getAngle(); final BoundingBox boundingBox = model.bounds.getBoundingBox(); - final float minX = boundingBox.min.x + x; - final float minY = boundingBox.min.y + y; - final Rectangle renderDestructableBounds = new Rectangle(minX, minY, boundingBox.getWidth(), - boundingBox.getHeight()); + final Rectangle renderDestructableBounds = getRotatedBoundingBox(x, y, angle, boundingBox); + System.out.println("ROTATED BOUNDS TO: " + renderDestructableBounds); this.walkableObjectsTree.add((MdxComplexInstance) renderDestructable.instance, renderDestructableBounds); renderDestructable.walkableBounds = renderDestructableBounds; @@ -1020,6 +1019,39 @@ public class War3MapViewer extends AbstractMdxModelViewer { this.anyReady = true; } + private Rectangle getRotatedBoundingBox(final float x, final float y, final float angle, + final BoundingBox boundingBox) { + final float x1 = boundingBox.min.x; + final float y1 = boundingBox.min.y; + final float x2 = boundingBox.min.x + boundingBox.getWidth(); + final float y2 = boundingBox.min.y; + final float x3 = boundingBox.min.x + boundingBox.getWidth(); + final float y3 = boundingBox.min.y + boundingBox.getHeight(); + final float x4 = boundingBox.min.x; + final float y4 = boundingBox.min.y + boundingBox.getHeight(); + final float angle1 = (float) StrictMath.atan2(y1, x1) + angle; + final float len1 = (float) StrictMath.sqrt((x1 * x1) + (y1 * y1)); + final float angle2 = (float) StrictMath.atan2(y2, x2) + angle; + final float len2 = (float) StrictMath.sqrt((x2 * x2) + (y2 * y2)); + final float angle3 = (float) StrictMath.atan2(y3, x3) + angle; + final float len3 = (float) StrictMath.sqrt((x3 * x3) + (y3 * y3)); + final float angle4 = (float) StrictMath.atan2(y4, x4) + angle; + final float len4 = (float) StrictMath.sqrt((x4 * x4) + (y4 * y4)); + final double x1prime = StrictMath.cos(angle1) * len1; + final double x2prime = StrictMath.cos(angle2) * len2; + final double x3prime = StrictMath.cos(angle3) * len3; + final double x4prime = StrictMath.cos(angle4) * len4; + final double y1prime = StrictMath.sin(angle1) * len1; + final double y2prime = StrictMath.sin(angle2) * len2; + final double y3prime = StrictMath.sin(angle3) * len3; + final double y4prime = StrictMath.sin(angle4) * len4; + final float minX = (float) StrictMath.min(StrictMath.min(x1prime, x2prime), StrictMath.min(x3prime, x4prime)); + final float minY = (float) StrictMath.min(StrictMath.min(y1prime, y2prime), StrictMath.min(y3prime, y4prime)); + final float maxX = (float) StrictMath.max(StrictMath.max(x1prime, x2prime), StrictMath.max(x3prime, x4prime)); + final float maxY = (float) StrictMath.max(StrictMath.max(y1prime, y2prime), StrictMath.max(y3prime, y4prime)); + return new Rectangle(x + minX, y + minY, maxX - minX, maxY - minY); + } + private void applyModificationFile(final MappedData doodadsData2, final MappedData doodadMetaData2, final MutableObjectData destructibles, final WorldEditorDataType dataType) { // TODO condense ported MappedData from Ghostwolf and MutableObjectData from @@ -1732,6 +1764,16 @@ public class War3MapViewer extends AbstractMdxModelViewer { return mdxPath; } + public static String mdl(String mdxPath) { + if (mdxPath.toLowerCase().endsWith(".mdx")) { + mdxPath = mdxPath.substring(0, mdxPath.length() - 4); + } + if (!mdxPath.toLowerCase().endsWith(".mdl")) { + mdxPath += ".mdl"; + } + return mdxPath; + } + public String blp(String iconPath) { final int lastDotIndex = iconPath.lastIndexOf('.'); if (lastDotIndex != -1) { diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CSimulation.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CSimulation.java index 24e23c3..e472ce9 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CSimulation.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CSimulation.java @@ -37,6 +37,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.pathing.CPathfindin import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CAllianceType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CRace; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.region.CRegionManager; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.timers.CTimer; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.ResourceType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.SimulationRenderController; @@ -70,6 +71,7 @@ public class CSimulation implements CPlayerAPI { private final Map handleIdToAbility = new HashMap<>(); private final LinkedList activeTimers = new LinkedList<>(); private transient CommandErrorListener commandErrorListener; + private final CRegionManager regionManager; public CSimulation(final War3MapConfig config, final DataTable miscData, final MutableObjectData parsedUnitData, final MutableObjectData parsedItemData, final MutableObjectData parsedDestructableData, @@ -92,6 +94,7 @@ public class CSimulation implements CPlayerAPI { this.newProjectiles = new ArrayList<>(); this.handleIdAllocator = new HandleIdAllocator(); this.worldCollision = new CWorldCollision(entireMapBounds, this.gameplayConstants.getMaxCollisionRadius()); + this.regionManager = new CRegionManager(entireMapBounds, pathingGrid); this.pathfindingProcessors = new CPathfindingProcessor[WarsmashConstants.MAX_PLAYERS]; this.playerHeroes = new ArrayList[WarsmashConstants.MAX_PLAYERS]; for (int i = 0; i < WarsmashConstants.MAX_PLAYERS; i++) { @@ -293,6 +296,10 @@ public class CSimulation implements CPlayerAPI { return this.worldCollision; } + public CRegionManager getRegionManager() { + return this.regionManager; + } + public CGameplayConstants getGameplayConstants() { return this.gameplayConstants; } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnit.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnit.java index 442321b..48fd227 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnit.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnit.java @@ -4,9 +4,11 @@ import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.EnumSet; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Queue; +import java.util.Set; import com.badlogic.gdx.math.Rectangle; import com.etheller.warsmash.util.War3ID; @@ -37,15 +39,21 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CAttackType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttack; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrder; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrderNoTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrderTargetPoint; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrderTargetWidget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CAllianceType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.region.CRegion; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.region.CRegionEnumFunction; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.region.CRegionManager; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.BooleanAbilityActivationReceiver; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.BooleanAbilityTargetCheckReceiver; public class CUnit extends CWidget { + private static RegionCheckerImpl regionCheckerImpl = new RegionCheckerImpl(); + private War3ID typeId; private float facing; // degrees private float mana; @@ -111,6 +119,8 @@ public class CUnit extends CWidget { private int foodUsed; private List unitSpecificAttacks; + private transient Set containingRegions = new LinkedHashSet<>(); + private transient Set priorContainingRegions = new LinkedHashSet<>(); public CUnit(final int handleId, final int playerIndex, final float x, final float y, final float life, final War3ID typeId, final float facing, final float mana, final int maximumLife, final int maximumMana, @@ -474,6 +484,74 @@ public class CUnit extends CWidget { } } + public boolean order(final CSimulation simulation, final int orderId, final AbilityTarget target) { + if (orderId == OrderIds.stop) { + order(simulation, new COrderNoTarget(0, orderId, false), false); + return true; + } + for (final CAbility ability : this.abilities) { + final BooleanAbilityActivationReceiver activationReceiver = BooleanAbilityActivationReceiver.INSTANCE; + ability.checkCanUse(simulation, this, orderId, activationReceiver); + if (activationReceiver.isOk()) { + if (target == null) { + final BooleanAbilityTargetCheckReceiver booleanTargetReceiver = BooleanAbilityTargetCheckReceiver + .getInstance().reset(); + ability.checkCanTargetNoTarget(simulation, this, orderId, booleanTargetReceiver); + if (booleanTargetReceiver.isTargetable()) { + order(simulation, new COrderNoTarget(ability.getHandleId(), orderId, false), false); + return true; + } + } + final boolean targetable = target.visit(new AbilityTargetVisitor() { + @Override + public Boolean accept(final AbilityPointTarget target) { + final BooleanAbilityTargetCheckReceiver booleanTargetReceiver = BooleanAbilityTargetCheckReceiver + .getInstance().reset(); + ability.checkCanTarget(simulation, CUnit.this, orderId, target, booleanTargetReceiver); + final boolean pointTargetable = booleanTargetReceiver.isTargetable(); + if (pointTargetable) { + order(simulation, new COrderTargetPoint(ability.getHandleId(), orderId, target, false), + false); + } + return pointTargetable; + } + + public Boolean acceptWidget(final CWidget target) { + final BooleanAbilityTargetCheckReceiver booleanTargetReceiver = BooleanAbilityTargetCheckReceiver + .getInstance().reset(); + ability.checkCanTarget(simulation, CUnit.this, orderId, target, booleanTargetReceiver); + final boolean widgetTargetable = booleanTargetReceiver.isTargetable(); + if (widgetTargetable) { + order(simulation, + new COrderTargetWidget(ability.getHandleId(), orderId, target.getHandleId(), false), + false); + } + return widgetTargetable; + } + + @Override + public Boolean accept(final CUnit target) { + return acceptWidget(target); + } + + @Override + public Boolean accept(final CDestructable target) { + return acceptWidget(target); + } + + @Override + public Boolean accept(final CItem target) { + return acceptWidget(target); + } + }); + if (targetable) { + return true; + } + } + } + return false; + } + private CBehavior beginOrder(final CSimulation game, final COrder order) { this.lastStartedOrder = order; CBehavior nextBehavior; @@ -550,20 +628,22 @@ public class CUnit extends CWidget { return this.collisionRectangle; } - public void setX(final float newX, final CWorldCollision collision) { + public void setX(final float newX, final CWorldCollision collision, final CRegionManager regionManager) { final float prevX = getX(); if (!this.unitType.isBuilding()) { setX(newX); collision.translate(this, newX - prevX, 0); } + checkRegionEvents(regionManager); } - public void setY(final float newY, final CWorldCollision collision) { + public void setY(final float newY, final CWorldCollision collision, final CRegionManager regionManager) { final float prevY = getY(); if (!this.unitType.isBuilding()) { setY(newY); collision.translate(this, 0, newY - prevY); } + checkRegionEvents(regionManager); } public void setPointAndCheckUnstuck(final float newX, final float newY, final CSimulation game) { @@ -602,11 +682,12 @@ public class CUnit extends CWidget { checkX -= (int) Math.cos(angle); checkY -= (int) Math.sin(angle); } - setPoint(outputX, outputY, collision); + setPoint(outputX, outputY, collision, game.getRegionManager()); game.unitRepositioned(this); } - public void setPoint(final float newX, final float newY, final CWorldCollision collision) { + public void setPoint(final float newX, final float newY, final CWorldCollision collision, + final CRegionManager regionManager) { final float prevX = getX(); final float prevY = getY(); setX(newX); @@ -614,6 +695,29 @@ public class CUnit extends CWidget { if (!this.unitType.isBuilding()) { collision.translate(this, newX - prevX, newY - prevY); } + checkRegionEvents(regionManager); + } + + private void checkRegionEvents(final CRegionManager regionManager) { + final Set temp = this.containingRegions; + this.containingRegions = this.priorContainingRegions; + this.priorContainingRegions = temp; + this.containingRegions.clear(); + regionManager.checkRegions(this.collisionRectangle == null ? tempRect.set(this.getX(), this.getY(), 0, 0) + : this.collisionRectangle, regionCheckerImpl.reset(this)); + for (final CRegion region : this.priorContainingRegions) { + if (!this.containingRegions.contains(region)) { + onLeaveRegion(region); + } + } + } + + private void onEnterRegion(final CRegion region) { + + } + + private void onLeaveRegion(final CRegion region) { + } public EnumSet getClassifications() { @@ -1428,4 +1532,28 @@ public class CUnit extends CWidget { game.unitDropItemEvent(this, droppedItem); } } + + public boolean isInRegion(final CRegion region) { + return this.containingRegions.contains(region); + } + + private static final class RegionCheckerImpl implements CRegionEnumFunction { + private CUnit unit; + + public RegionCheckerImpl reset(final CUnit unit) { + this.unit = unit; + return this; + } + + @Override + public boolean call(final CRegion region) { + if (this.unit.containingRegions.add(region)) { + if (!this.unit.priorContainingRegions.contains(region)) { + this.unit.onEnterRegion(region); + } + } + return false; + } + + } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CWorldCollision.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CWorldCollision.java index fdcffd5..8f6600d 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CWorldCollision.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CWorldCollision.java @@ -225,6 +225,9 @@ public class CWorldCollision { return false; } if (this.done) { + // This check is because we may use the intersector for multiple intersect + // calls, see "enumUnitsInRect" and how it uses this intersector first on the + // ground unit layer, then the flying unit layer, without recycling return true; } if (this.intersectedUnits.add(intersectingObject)) { diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/CBehaviorMove.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/CBehaviorMove.java index 4cc32fa..15099b1 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/CBehaviorMove.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/CBehaviorMove.java @@ -240,7 +240,7 @@ public class CBehaviorMove implements CBehavior { // / 16) // * 16 && !worldCollision.intersectsAnythingOtherThan(tempRect, this.unit, movementType))) { - this.unit.setPoint(nextX, nextY, worldCollision); + this.unit.setPoint(nextX, nextY, worldCollision, simulation.getRegionManager()); if (done) { // if we're making headway along the path then it's OK to start thinking fast // again diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/build/CBehaviorUndeadBuild.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/build/CBehaviorUndeadBuild.java index 6fd5b1c..897d2c1 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/build/CBehaviorUndeadBuild.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/build/CBehaviorUndeadBuild.java @@ -95,7 +95,7 @@ public class CBehaviorUndeadBuild extends CAbstractRangedBehavior { final float delta = (float) Math.sqrt((deltaX * deltaX) + (deltaY * deltaY)); this.unit.setPoint(this.target.getX() + ((deltaX / delta) * unitTypeToCreate.getCollisionSize()), this.target.getY() + ((deltaY / delta) * unitTypeToCreate.getCollisionSize()), - simulation.getWorldCollision()); + simulation.getWorldCollision(), simulation.getRegionManager()); simulation.unitRepositioned(this.unit); simulation.unitConstructedEvent(this.unit, constructedStructure); this.doneTick = simulation.getGameTurnTick() + delayAnimationTicks; diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/players/CPlayerJass.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/players/CPlayerJass.java index 57cc14d..9a14f0b 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/players/CPlayerJass.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/players/CPlayerJass.java @@ -15,6 +15,8 @@ public interface CPlayerJass { void setAlliance(int otherPlayerIndex, CAllianceType whichAllianceSetting, boolean value); + boolean hasAlliance(int otherPlayerIndex, CAllianceType allianceType); + void setTaxRate(int otherPlayerIndex, CPlayerState whichResource, int rate); void setRacePref(CRacePreference whichRacePreference); diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/region/CRegion.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/region/CRegion.java new file mode 100644 index 0000000..f215fac --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/region/CRegion.java @@ -0,0 +1,125 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.region; + +import com.badlogic.gdx.math.Rectangle; + +public class CRegion { + private Rectangle currentBounds; + private boolean complexRegion; + + public void addRect(final Rectangle rect, final CRegionManager regionManager) { + if (this.currentBounds == null) { + this.currentBounds = new Rectangle(rect); + regionManager.addRectForRegion(this, this.currentBounds); + } + else { + if (!this.complexRegion) { + convertToComplexRegionAndAddRect(rect, regionManager); + } + else { + complexRegionAddRect(rect, regionManager); + } + } + } + + public void clearRect(final Rectangle rect, final CRegionManager regionManager) { + if (this.currentBounds == null) { + return; + } + if (this.complexRegion) { + regionManager.removeRectForRegion(this, this.currentBounds); + regionManager.removeComplexRegionCells(this, rect); + regionManager.computeNewMinimumComplexRegionBounds(this, this.currentBounds); + regionManager.addRectForRegion(this, this.currentBounds); + } + else { + this.complexRegion = true; + regionManager.addComplexRegionCells(this, this.currentBounds); + regionManager.removeComplexRegionCells(this, rect); + } + } + + public void remove(final CRegionManager regionManager) { + if (this.currentBounds == null) { + return; + } + if (this.complexRegion) { + regionManager.removeComplexRegionCells(this, this.currentBounds); + } + regionManager.removeRectForRegion(this, this.currentBounds); + } + + public void addCell(final float x, final float y, final CRegionManager regionManager) { + if (this.currentBounds == null) { + this.complexRegion = true; + this.currentBounds = new Rectangle(x, y, 0, 0); + regionManager.addComplexRegionCell(this, x, y, this.currentBounds); + regionManager.addRectForRegion(this, this.currentBounds); + } + else { + regionManager.removeRectForRegion(this, this.currentBounds); + if (!this.complexRegion) { + regionManager.addComplexRegionCells(this, this.currentBounds); + this.complexRegion = true; + } + regionManager.addComplexRegionCell(this, x, y, this.currentBounds); + regionManager.addRectForRegion(this, this.currentBounds); + } + } + + public void clearCell(final float x, final float y, final CRegionManager regionManager) { + if (this.currentBounds == null) { + return; + } + else { + regionManager.removeRectForRegion(this, this.currentBounds); + if (!this.complexRegion) { + regionManager.addComplexRegionCells(this, this.currentBounds); + this.complexRegion = true; + } + regionManager.clearComplexRegionCell(this, x, y, this.currentBounds); + regionManager.addRectForRegion(this, this.currentBounds); + } + } + + private void complexRegionAddRect(final Rectangle rect, final CRegionManager regionManager) { + regionManager.removeRectForRegion(this, this.currentBounds); + regionManager.addComplexRegionCells(this, rect); + this.currentBounds = this.currentBounds.merge(rect); + regionManager.addRectForRegion(this, this.currentBounds); + } + + private void convertToComplexRegionAndAddRect(final Rectangle rect, final CRegionManager regionManager) { + regionManager.removeRectForRegion(this, this.currentBounds); + this.complexRegion = true; + regionManager.addComplexRegionCells(this, this.currentBounds); + regionManager.addComplexRegionCells(this, rect); + this.currentBounds = this.currentBounds.merge(rect); + regionManager.addRectForRegion(this, this.currentBounds); + } + + public Rectangle getCurrentBounds() { + return this.currentBounds; + } + + public void setCurrentBounds(final Rectangle currentBounds) { + this.currentBounds = currentBounds; + } + + public boolean isComplexRegion() { + return this.complexRegion; + } + + public void setComplexRegion(final boolean complexRegion) { + this.complexRegion = complexRegion; + } + + public boolean contains(final float x, final float y, final CRegionManager regionManager) { + if (this.currentBounds == null) { + return false; + } + if (this.complexRegion) { + return regionManager.isPointInComplexRegion(this, x, y); + } + return this.currentBounds.contains(x, y); + } +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/region/CRegionEnumFunction.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/region/CRegionEnumFunction.java new file mode 100644 index 0000000..8cf6135 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/region/CRegionEnumFunction.java @@ -0,0 +1,11 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.region; + +public interface CRegionEnumFunction { + /** + * Operates on a region, returning true if we should stop execution. + * + * @param region + * @return + */ + boolean call(CRegion region); +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/region/CRegionManager.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/region/CRegionManager.java new file mode 100644 index 0000000..6ed9c65 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/region/CRegionManager.java @@ -0,0 +1,189 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.region; + +import java.util.ArrayList; +import java.util.List; + +import com.badlogic.gdx.math.Rectangle; +import com.etheller.warsmash.util.Quadtree; +import com.etheller.warsmash.util.QuadtreeIntersector; +import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid; + +public class CRegionManager { + private static Rectangle tempRect = new Rectangle(); + private final Quadtree regionTree; + private final RegionChecker regionChecker = new RegionChecker(); + private final List[][] cellRegions; + private final PathingGrid pathingGrid; + + public CRegionManager(final Rectangle entireMapBounds, final PathingGrid pathingGrid) { + this.regionTree = new Quadtree<>(entireMapBounds); + this.cellRegions = new List[pathingGrid.getHeight()][pathingGrid.getWidth()]; + this.pathingGrid = pathingGrid; + } + + public void addRectForRegion(final CRegion region, final Rectangle rect) { + this.regionTree.add(region, rect); + } + + public void removeRectForRegion(final CRegion region, final Rectangle rect) { + this.regionTree.remove(region, rect); + } + + /** + * Calls back on the enum function for every region that touches the given area. + * Sometimes, for performance, this algorithm is designed to call the enum + * function twice for the same region, because our expected use case is to store + * the regions in a set that guarantees uniqueness anyway (see CUnit and/or + * other uses of this method). + */ + public void checkRegions(final Rectangle area, final CRegionEnumFunction enumFunction) { + this.regionTree.intersect(area, this.regionChecker.reset(enumFunction)); + if (this.regionChecker.includesComplex) { + final int minX = this.pathingGrid.getCellX(area.x); + final int minY = this.pathingGrid.getCellY(area.y); + final int maxX = this.pathingGrid.getCellX(area.x + area.width); + final int maxY = this.pathingGrid.getCellY(area.y + area.height); + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + final List cellRegionsAtPoint = this.cellRegions[y][x]; + if (cellRegionsAtPoint != null) { + for (final CRegion region : cellRegionsAtPoint) { + if (enumFunction.call(region)) { + return; + } + } + } + } + } + } + } + + private static final class RegionChecker implements QuadtreeIntersector { + private CRegionEnumFunction delegate; + private boolean includesComplex = false; + + public RegionChecker reset(final CRegionEnumFunction delegate) { + this.delegate = delegate; + return this; + } + + @Override + public boolean onIntersect(final CRegion intersectingObject) { + if (intersectingObject.isComplexRegion()) { + this.includesComplex = true; + // handle this type of region differently + return false; + } + return this.delegate.call(intersectingObject); + } + + } + + public void addComplexRegionCells(final CRegion region, final Rectangle currentBounds) { + final int minX = this.pathingGrid.getCellX(currentBounds.x); + final int minY = this.pathingGrid.getCellY(currentBounds.y); + final int maxX = this.pathingGrid.getCellX(currentBounds.x + currentBounds.width); + final int maxY = this.pathingGrid.getCellY(currentBounds.y + currentBounds.height); + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + List list = this.cellRegions[y][x]; + if (list == null) { + this.cellRegions[y][x] = list = new ArrayList<>(); + } + list.add(region); + } + } + } + + public void removeComplexRegionCells(final CRegion region, final Rectangle currentBounds) { + final int minX = this.pathingGrid.getCellX(currentBounds.x); + final int minY = this.pathingGrid.getCellY(currentBounds.y); + final int maxX = this.pathingGrid.getCellX(currentBounds.x + currentBounds.width); + final int maxY = this.pathingGrid.getCellY(currentBounds.y + currentBounds.height); + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + final List list = this.cellRegions[y][x]; + if (list != null) { + list.remove(region); + } + } + } + } + + public void computeNewMinimumComplexRegionBounds(final CRegion region, final Rectangle complexRegionBounds) { + final int minX = this.pathingGrid.getCellX(complexRegionBounds.x); + final int minY = this.pathingGrid.getCellY(complexRegionBounds.y); + final int maxX = this.pathingGrid.getCellX(complexRegionBounds.x + complexRegionBounds.width); + final int maxY = this.pathingGrid.getCellY(complexRegionBounds.y + complexRegionBounds.height); + float newMinX = this.pathingGrid.getWorldX(this.pathingGrid.getWidth() - 1); + float newMaxX = this.pathingGrid.getWorldX(0); + float newMinY = this.pathingGrid.getWorldY(this.pathingGrid.getHeight() - 1); + float newMaxY = this.pathingGrid.getWorldY(0); + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + final List list = this.cellRegions[y][x]; + if (list != null) { + if (list.contains(region)) { + final float worldX = this.pathingGrid.getWorldX(x); + final float worldY = this.pathingGrid.getWorldY(y); + final float wMinX = worldX - 16f; + final float wMinY = worldY - 16f; + final float wMaxX = worldX + 15f; + final float wMaxY = worldY + 15f; + if (wMinX < newMinX) { + newMinX = wMinX; + } + if (wMinY < newMinY) { + newMinY = wMinY; + } + if (wMaxX > newMaxX) { + newMaxX = wMaxX; + } + if (wMaxY > newMaxY) { + newMaxY = wMaxY; + } + } + } + } + } + complexRegionBounds.set(newMinX, newMinY, newMaxX - newMinX, newMaxY - newMinY); + } + + public void addComplexRegionCell(final CRegion region, final float x, final float y, + final Rectangle boundsToUpdate) { + final int cellX = this.pathingGrid.getCellX(x); + final int cellY = this.pathingGrid.getCellY(y); + List list = this.cellRegions[cellY][cellX]; + if (list == null) { + this.cellRegions[cellY][cellX] = list = new ArrayList<>(); + } + list.add(region); + final float worldX = this.pathingGrid.getWorldX(cellX); + final float worldY = this.pathingGrid.getWorldY(cellY); + final float wMinX = worldX - 16f; + final float wMinY = worldY - 16f; + boundsToUpdate.merge(tempRect.set(wMinX, wMinY, 31f, 31f)); + } + + public void clearComplexRegionCell(final CRegion region, final float x, final float y, + final Rectangle boundsToUpdate) { + final int cellX = this.pathingGrid.getCellX(x); + final int cellY = this.pathingGrid.getCellY(y); + final List list = this.cellRegions[cellY][cellX]; + if (list != null) { + list.remove(region); + } + computeNewMinimumComplexRegionBounds(region, boundsToUpdate); + } + + public boolean isPointInComplexRegion(final CRegion region, final float x, final float y) { + final int cellX = this.pathingGrid.getCellX(x); + final int cellY = this.pathingGrid.getCellY(y); + final List list = this.cellRegions[cellY][cellX]; + if (list != null) { + return list.contains(region); + } + return false; + } + +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUI.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUI.java index bf5aee9..84064df 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUI.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUI.java @@ -295,6 +295,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma private SimpleButtonFrame alliesButton; private SimpleButtonFrame chatButton; private final Runnable exitGameRunnable; + private SimpleFrame smashEscMenu; public MeleeUI(final DataSource dataSource, final ExtendViewport uiViewport, final Scene uiScene, final Scene portraitScene, final CameraPreset[] cameraPresets, final CameraRates cameraRates, @@ -448,12 +449,14 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma this.chatButton = (SimpleButtonFrame) this.rootFrame.getFrameByName("UpperButtonBarChatButton", 0); this.chatButton.setEnabled(false); - final UIFrame escMenuBackdrop = this.rootFrame.createFrame("EscMenuBackdrop", this.rootFrame, 0, 0); + this.smashEscMenu = (SimpleFrame) this.rootFrame.createSimpleFrame("SmashEscMenu", this.rootFrame, 0); + this.smashEscMenu.addAnchor(new AnchorDefinition(FramePoint.TOP, 0, GameUI.convertY(this.uiViewport, -0.05f))); + final UIFrame escMenuBackdrop = this.rootFrame.createFrame("EscMenuBackdrop", this.smashEscMenu, 0, 0); escMenuBackdrop.setVisible(false); - escMenuBackdrop.addAnchor(new AnchorDefinition(FramePoint.TOP, 0, GameUI.convertY(this.uiViewport, -0.05f))); - final UIFrame escMenuMainPanel = this.rootFrame.createFrame("EscMenuMainPanel", this.rootFrame, 0, 0); + final UIFrame escMenuMainPanel = this.rootFrame.createFrame("EscMenuMainPanel", this.smashEscMenu, 0, 0); escMenuMainPanel.setVisible(false); - escMenuMainPanel.addAnchor(new AnchorDefinition(FramePoint.TOP, 0, GameUI.convertY(this.uiViewport, -0.05f))); + this.smashEscMenu.add(escMenuBackdrop); + this.smashEscMenu.add(escMenuMainPanel); final UIFrame escMenuInnerMainPanel = this.rootFrame.getFrameByName("MainPanel", 0); final GlueTextButtonFrame pauseButton = (GlueTextButtonFrame) this.rootFrame.getFrameByName("PauseButton", 0); @@ -504,6 +507,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma public void run() { escMenuBackdrop.setVisible(true); escMenuMainPanel.setVisible(true); + MeleeUI.this.smashEscMenu.setVisible(true); escMenuInnerMainPanel.setVisible(true); updateEscMenuCurrentPanel(escMenuBackdrop, escMenuMainPanel, escMenuInnerMainPanel); } @@ -513,6 +517,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma public void run() { escMenuBackdrop.setVisible(false); escMenuMainPanel.setVisible(false); + MeleeUI.this.smashEscMenu.setVisible(false); escMenuInnerMainPanel.setVisible(false); } }); @@ -924,12 +929,12 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma private void updateEscMenuCurrentPanel(final UIFrame escMenuBackdrop, final UIFrame escMenuMainPanel, final UIFrame escMenuInnerMainPanel) { - escMenuMainPanel.setHeight(escMenuInnerMainPanel.getAssignedHeight()); - escMenuMainPanel.setWidth(escMenuInnerMainPanel.getAssignedWidth()); - escMenuBackdrop.setHeight(escMenuInnerMainPanel.getAssignedHeight()); + this.smashEscMenu.setWidth(escMenuInnerMainPanel.getAssignedWidth()); + this.smashEscMenu.setHeight(escMenuInnerMainPanel.getAssignedHeight()); escMenuBackdrop.setWidth(escMenuInnerMainPanel.getAssignedWidth()); - escMenuMainPanel.positionBounds(MeleeUI.this.rootFrame, MeleeUI.this.uiViewport); - escMenuBackdrop.positionBounds(MeleeUI.this.rootFrame, MeleeUI.this.uiViewport); + escMenuBackdrop.setHeight(escMenuInnerMainPanel.getAssignedHeight()); + this.smashEscMenu.positionBounds(this.rootFrame, this.uiViewport); + } @Override diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MenuUI.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MenuUI.java index 1d41c7f..32308c8 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MenuUI.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MenuUI.java @@ -166,12 +166,23 @@ public class MenuUI { this.heightRatioCorrection = getMinWorldHeight() / 1200f; this.campaignStrings = new DataTable(StringBundle.EMPTY); - try (InputStream campaignStringStream = dataSource.getResourceAsStream( - "UI\\CampaignStrings" + (WarsmashConstants.GAME_VERSION == 1 ? "_exp" : "") + ".txt")) { - this.campaignStrings.readTXT(campaignStringStream, true); + final String campaignStringPath = "UI\\CampaignStrings" + (WarsmashConstants.GAME_VERSION == 1 ? "_exp" : "") + + ".txt"; + if (dataSource.has(campaignStringPath)) { + try (InputStream campaignStringStream = dataSource.getResourceAsStream(campaignStringPath)) { + this.campaignStrings.readTXT(campaignStringStream, true); + } + catch (final IOException e) { + throw new RuntimeException(e); + } } - catch (final IOException e) { - throw new RuntimeException(e); + else { + try (InputStream campaignStringStream = dataSource.getResourceAsStream("UI\\CampaignInfoClassic.txt")) { + this.campaignStrings.readTXT(campaignStringStream, true); + } + catch (final IOException e) { + throw new RuntimeException(e); + } } this.profileManager = PlayerProfileManager.loadFromGdx(); diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/menu/CampaignMenuUI.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/menu/CampaignMenuUI.java index 335e241..8aeee26 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/menu/CampaignMenuUI.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/menu/CampaignMenuUI.java @@ -56,7 +56,6 @@ public class CampaignMenuUI extends SimpleFrame { super.innerPositionBounds(gameUI, viewport); final float myHeight = this.renderBounds.height; final int buttonCount = this.buttonUIs.size(); - System.out.println("myheight / count is " + myHeight + "/" + buttonCount); final float buttonSpacing = Math.min(myHeight / buttonCount, GameUI.convertY(this.uiViewport, 0.056f)); final float buttonsHeight = buttonSpacing * buttonCount; final float expectedHeight = Math.min(buttonsHeight, myHeight); @@ -65,12 +64,10 @@ public class CampaignMenuUI extends SimpleFrame { UIFrame lastButton = null; for (final CampaignButtonUI campaignButtonUI : this.buttonUIs) { if (lastButton == null) { - System.out.println("offsetting from this " + getName() + " by " + yOffset); campaignButtonUI.addSetPoint(new SetPoint(FramePoint.TOPLEFT, this, FramePoint.TOPLEFT, 0, -yOffset)); campaignButtonUI.addSetPoint(new SetPoint(FramePoint.TOPRIGHT, this, FramePoint.TOPRIGHT, 0, -yOffset)); } else { - System.out.println("offsetting from bs " + lastButton.getName() + " by " + buttonSpacing); campaignButtonUI.addSetPoint( new SetPoint(FramePoint.TOPLEFT, lastButton, FramePoint.TOPLEFT, 0, -buttonSpacing)); campaignButtonUI.addSetPoint(