Update campaign menu behaviors and make some progress on untested common j apis

This commit is contained in:
Retera 2021-04-04 10:28:40 -04:00
parent b5ab73fb47
commit f1bcb4ae54
27 changed files with 1710 additions and 69 deletions

View File

@ -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();

View File

@ -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;

View File

@ -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:

View File

@ -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;

View File

@ -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

View File

@ -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);
}
}
}
}
}

View File

@ -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;
}

View File

@ -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) {

File diff suppressed because it is too large Load Diff

View File

@ -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);
}
}

View File

@ -24,7 +24,8 @@ public class Quadtree<T> {
}
public void add(final T object, final Rectangle bounds) {
add(new Node<T>(object, bounds), 0);
final Node<T> node = new Node<T>(object, bounds);
add(node, 0);
}
public void remove(final T object, final Rectangle bounds) {

View File

@ -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;

View File

@ -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

View File

@ -167,7 +167,7 @@ public final class SdSequence<TYPE> {
}
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<TYPE> {
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;
}
}

View File

@ -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) {

View File

@ -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<Integer, CAbility> handleIdToAbility = new HashMap<>();
private final LinkedList<CTimer> 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;
}

View File

@ -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<CUnitAttack> unitSpecificAttacks;
private transient Set<CRegion> containingRegions = new LinkedHashSet<>();
private transient Set<CRegion> 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<Void> booleanTargetReceiver = BooleanAbilityTargetCheckReceiver
.<Void>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<Boolean>() {
@Override
public Boolean accept(final AbilityPointTarget target) {
final BooleanAbilityTargetCheckReceiver<AbilityPointTarget> booleanTargetReceiver = BooleanAbilityTargetCheckReceiver
.<AbilityPointTarget>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<CWidget> booleanTargetReceiver = BooleanAbilityTargetCheckReceiver
.<CWidget>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<CRegion> 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<CUnitClassification> 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;
}
}
}

View File

@ -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)) {

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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<CRegion> regionTree;
private final RegionChecker regionChecker = new RegionChecker();
private final List<CRegion>[][] 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<CRegion> 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<CRegion> {
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<CRegion> 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<CRegion> 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<CRegion> 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<CRegion> 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<CRegion> 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<CRegion> list = this.cellRegions[cellY][cellX];
if (list != null) {
return list.contains(region);
}
return false;
}
}

View File

@ -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

View File

@ -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();

View File

@ -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(