First draft of build queue, better working structures built list

This commit is contained in:
Retera 2020-11-14 17:10:15 -05:00
parent 4ffd0c3283
commit f589afdf46
28 changed files with 2276 additions and 1419 deletions

View File

@ -56,7 +56,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.CommandErrorListene
public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProvider, InputProcessor {
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 War3MapViewer viewer;
private final Rectangle tempRect = new Rectangle();

View File

@ -276,7 +276,9 @@ public final class GameUI extends AbstractUIFrame implements UIFrame {
case Frame:
if ("SIMPLEFRAME".equals(frameDefinition.getFrameType())) {
final SimpleFrame simpleFrame = new SimpleFrame(frameDefinition.getName(), parent);
// TODO: we should not need to put ourselves in this map 2x
// TODO: we should not need to put ourselves in this map 2x, but we do
// since there are nested inflate calls happening before the general case
// mapping
this.nameToFrame.put(frameDefinition.getName(), simpleFrame);
for (final FrameDefinition childDefinition : frameDefinition.getInnerFrames()) {
simpleFrame.add(inflate(childDefinition, simpleFrame, frameDefinition));
@ -315,7 +317,13 @@ public final class GameUI extends AbstractUIFrame implements UIFrame {
}
break;
case Layer:
// NOT HANDLED YET
final SimpleFrame simpleFrame = new SimpleFrame(frameDefinition.getName(), parent);
simpleFrame.setSetAllPoints(true);
this.nameToFrame.put(frameDefinition.getName(), simpleFrame);
for (final FrameDefinition childDefinition : frameDefinition.getInnerFrames()) {
simpleFrame.add(inflate(childDefinition, simpleFrame, frameDefinition));
}
inflatedFrame = simpleFrame;
break;
case String:
final Float textLength = frameDefinition.getFloat("TextLength");

View File

@ -307,8 +307,8 @@ public abstract class AbstractRenderableFrame implements UIFrame {
}
}
if (DEBUG_LOG) {
System.out.println(
getClass().getSimpleName() + ":" + this.name + " finishing position bounds: " + this.renderBounds);
System.out.println(getClass().getSimpleName() + ":" + this.name + ":" + hashCode()
+ " finishing position bounds: " + this.renderBounds);
}
innerPositionBounds(viewport);
}

View File

@ -5,4 +5,6 @@ public class WarsmashConstants {
public static final int REPLACEABLE_TEXTURE_LIMIT = 64;
public static final float SIMULATION_STEP_TIME = 1 / 20f;
public static final int PORT_NUMBER = 6115;
public static final float BUILDING_CONSTRUCT_START_LIFE = 0.1f;
public static final int BUILD_QUEUE_SIZE = 7;
}

View File

@ -225,9 +225,19 @@ public class SplatModel {
}
public void add(float x, float y, float z, float scale, float[] centerOffset) {
locations.add(new float[]{x - scale, y - scale, x + scale, y + scale, z});
public SplatMover add(final float x, final float y, final float w, final float h, final float zDepthUpward,
final float[] centerOffset) {
this.locations.add(new float[] { x, y, w, h, zDepthUpward });
final SplatMover splatMover;
if (this.splatInstances != null) {
splatMover = new SplatMover(this);
this.splatInstances.add(splatMover);
}
else {
splatMover = null;
}
compact(Gdx.gl30, centerOffset);
return splatMover;
}
private static final class Batch {
@ -413,5 +423,12 @@ public class SplatModel {
gl.glBufferSubData(GL30.GL_ARRAY_BUFFER, this.uvsOffset + ((this.startOffset / 3) * 2),
4 * 2 * this.uvs.size(), RenderMathUtils.wrap(this.uvs));
}
public void show(final float[] centerOffset) {
// It tries to only update if it is located at a new position... but here we are
// forcing it visible again by putting the position outside the map
this.ix0 = this.ix1 = this.iy0 = this.iy1 = Integer.MIN_VALUE;
move(0, 0, centerOffset);
}
}
}

View File

@ -72,6 +72,8 @@ import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode;
import com.etheller.warsmash.viewer5.handlers.tga.TgaFile;
import com.etheller.warsmash.viewer5.handlers.w3x.SplatModel.SplatMover;
import com.etheller.warsmash.viewer5.handlers.w3x.environment.BuildingShadow;
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid.RemovablePathingMapInstance;
import com.etheller.warsmash.viewer5.handlers.w3x.environment.Terrain;
import com.etheller.warsmash.viewer5.handlers.w3x.environment.Terrain.Splat;
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderAttackInstant;
@ -97,6 +99,7 @@ import mpq.MPQException;
public class War3MapViewer extends ModelViewer {
private static final War3ID UNIT_FILE = War3ID.fromString("umdl");
private static final War3ID UNIT_SPECIAL = War3ID.fromString("uspa");
private static final War3ID UBER_SPLAT = War3ID.fromString("uubs");
private static final War3ID UNIT_SHADOW = War3ID.fromString("ushu");
private static final War3ID UNIT_SHADOW_X = War3ID.fromString("ushx");
@ -553,6 +556,35 @@ public class War3MapViewer extends ModelViewer {
return createNewUnit(War3MapViewer.this.allObjectData, typeId, x, y, 0f, playerIndex,
(float) Math.toRadians(facing));
}
@Override
public void spawnBuildingDeathEffect(final CUnit source) {
final RenderUnit renderUnit = War3MapViewer.this.unitToRenderPeer.get(source);
if (renderUnit.specialArtModel != null) {
final MdxComplexInstance modelInstance = (MdxComplexInstance) renderUnit.specialArtModel
.addInstance();
modelInstance.setTeamColor(source.getPlayerIndex());
modelInstance.setLocation(renderUnit.location);
modelInstance.setScene(War3MapViewer.this.worldScene);
SequenceUtils.randomBirthSequence(modelInstance);
War3MapViewer.this.projectiles
.add(new RenderAttackInstant(modelInstance, War3MapViewer.this,
(float) Math.toRadians(renderUnit.getSimulationUnit().getFacing())));
}
}
@Override
public void spawnUnitReadySound(final CUnit trainedUnit) {
final RenderUnit renderPeer = War3MapViewer.this.unitToRenderPeer.get(trainedUnit);
renderPeer.soundset.ready.playUnitResponse(War3MapViewer.this.worldScene.audioContext,
renderPeer);
}
@Override
public void unitRepositioned(final CUnit cUnit) {
final RenderUnit renderPeer = War3MapViewer.this.unitToRenderPeer.get(cUnit);
renderPeer.repositioned(War3MapViewer.this);
}
}, this.terrain.pathingGrid, this.terrain.getEntireMap(), this.seededRandom, w3iFile.getPlayers());
this.walkableObjectsTree = new Quadtree<>(this.terrain.getEntireMap());
@ -664,7 +696,7 @@ public class War3MapViewer extends ModelViewer {
}
}
if (bufferedImage != null) {
this.terrain.pathingGrid.blitPathingOverlayTexture(doodad.getLocation()[0],
this.terrain.pathingGrid.blitRemovablePathingOverlayTexture(doodad.getLocation()[0],
doodad.getLocation()[1], (int) Math.toDegrees(doodad.getAngle()), bufferedImage);
}
}
@ -814,8 +846,13 @@ public class War3MapViewer extends ModelViewer {
MutableGameObject row = null;
String path = null;
Splat unitShadowSplat = null;
SplatMover unitShadowSplatDynamicIngame = null;
Splat buildingUberSplat = null;
SplatMover buildingUberSplatDynamicIngame = null;
BufferedImage buildingPathingPixelMap = null;
final float unitVertexScale = 1.0f;
RemovablePathingMapInstance pathingInstance = null;
BuildingShadow buildingShadowInstance = null;
// Hardcoded?
WorldEditorDataType type = null;
@ -871,8 +908,8 @@ public class War3MapViewer extends ModelViewer {
if (buildingPathingPixelMap != null) {
unitX = Math.round(unitX / 64f) * 64f;
unitY = Math.round(unitY / 64f) * 64f;
this.terrain.pathingGrid.blitPathingOverlayTexture(unitX, unitY, (int) Math.toDegrees(unitAngle),
buildingPathingPixelMap);
pathingInstance = this.terrain.pathingGrid.blitRemovablePathingOverlayTexture(unitX, unitY,
(int) Math.toDegrees(unitAngle), buildingPathingPixelMap);
}
final String uberSplat = row.getFieldAsString(UBER_SPLAT, 0);
@ -883,7 +920,7 @@ public class War3MapViewer extends ModelViewer {
+ ".blp";
final float s = uberSplatInfo.getFieldFloatValue("Scale");
if (this.unitsReady) {
this.terrain.addUberSplat(texturePath, unitX, unitY, 1, s);
buildingUberSplatDynamicIngame = this.terrain.addUberSplat(texturePath, unitX, unitY, 1, s);
}
else {
if (!this.terrain.splats.containsKey(texturePath)) {
@ -891,8 +928,8 @@ public class War3MapViewer extends ModelViewer {
}
final float x = unitX;
final float y = unitY;
this.terrain.splats.get(texturePath).locations
.add(new float[] { x - s, y - s, x + s, y + s, 1 });
buildingUberSplat = this.terrain.splats.get(texturePath);
buildingUberSplat.locations.add(new float[] { x - s, y - s, x + s, y + s, 1 });
}
}
}
@ -905,22 +942,28 @@ public class War3MapViewer extends ModelViewer {
final float shadowWidth = row.getFieldAsFloat(UNIT_SHADOW_W, 0);
final float shadowHeight = row.getFieldAsFloat(UNIT_SHADOW_H, 0);
if (this.mapMpq.has(texture)) {
if (!this.terrain.splats.containsKey(texture)) {
final Splat splat = new Splat();
splat.opacity = 0.5f;
this.terrain.splats.put(texture, splat);
}
final float x = unitX - shadowX;
final float y = unitY - shadowY;
this.terrain.splats.get(texture).locations
.add(new float[] { x, y, x + shadowWidth, y + shadowHeight, 3 });
unitShadowSplat = this.terrain.splats.get(texture);
if (this.unitsReady) {
unitShadowSplatDynamicIngame = this.terrain.addUnitShadowSplat(texture, x, y,
x + shadowWidth, y + shadowHeight, 3, 0.5f);
}
else {
if (!this.terrain.splats.containsKey(texture)) {
final Splat splat = new Splat();
splat.opacity = 0.5f;
this.terrain.splats.put(texture, splat);
}
this.terrain.splats.get(texture).locations
.add(new float[] { x, y, x + shadowWidth, y + shadowHeight, 3 });
unitShadowSplat = this.terrain.splats.get(texture);
}
}
}
final String buildingShadow = row.getFieldAsString(BUILDING_SHADOW, 0);
if ((buildingShadow != null) && !"_".equals(buildingShadow)) {
this.terrain.addShadow(buildingShadow, unitX, unitY);
buildingShadowInstance = this.terrain.addShadow(buildingShadow, unitX, unitY);
}
final String soundName = row.getFieldAsString(UNIT_SOUNDSET, 0);
@ -935,6 +978,21 @@ public class War3MapViewer extends ModelViewer {
}
if (path != null) {
final String unitSpecialArtPath = row.getFieldAsString(UNIT_SPECIAL, 0);
MdxModel specialArtModel;
if (unitSpecialArtPath != null) {
try {
specialArtModel = (MdxModel) this.load(mdx(unitSpecialArtPath), this.mapPathSolver,
this.solverParams);
}
catch (final Exception exc) {
exc.printStackTrace();
specialArtModel = null;
}
}
else {
specialArtModel = null;
}
final MdxModel model = (MdxModel) this.load(path, this.mapPathSolver, this.solverParams);
MdxModel portraitModel;
final String portraitPath = path.substring(0, path.length() - 4) + "_portrait.mdx";
@ -947,10 +1005,10 @@ public class War3MapViewer extends ModelViewer {
if (type == WorldEditorDataType.UNITS) {
final float angle = (float) Math.toDegrees(unitAngle);
final CUnit simulationUnit = this.simulation.createUnit(row.getAlias(), playerIndex, unitX, unitY,
angle, buildingPathingPixelMap);
angle, buildingPathingPixelMap, pathingInstance, buildingShadowInstance);
final RenderUnitTypeData typeData = getUnitTypeData(unitId, row);
final RenderUnit renderUnit = new RenderUnit(this, model, row, unitX, unitY, unitZ, playerIndex,
soundset, portraitModel, simulationUnit, typeData);
soundset, portraitModel, simulationUnit, typeData, specialArtModel);
this.unitToRenderPeer.put(simulationUnit, renderUnit);
this.units.add(renderUnit);
if (unitShadowSplat != null) {
@ -961,6 +1019,20 @@ public class War3MapViewer extends ModelViewer {
}
});
}
if (unitShadowSplatDynamicIngame != null) {
renderUnit.shadow = unitShadowSplatDynamicIngame;
}
if (buildingUberSplat != null) {
buildingUberSplat.unitMapping.add(new Consumer<SplatModel.SplatMover>() {
@Override
public void accept(final SplatMover t) {
renderUnit.uberSplat = t;
}
});
}
if (buildingUberSplatDynamicIngame != null) {
renderUnit.uberSplat = buildingUberSplatDynamicIngame;
}
return simulationUnit;
}
else {
@ -976,6 +1048,8 @@ public class War3MapViewer extends ModelViewer {
}
});
}
if (unitShadowSplatDynamicIngame != null) {
}
}
}
else {
@ -1222,24 +1296,7 @@ public class War3MapViewer extends ModelViewer {
public List<RenderUnit> selectUnit(final float x, final float y, final boolean toggle) {
System.out.println("world: " + x + "," + y);
final float[] ray = rayHeap;
mousePosHeap.set(x, y);
this.worldScene.camera.screenToWorldRay(ray, mousePosHeap);
gdxRayHeap.set(ray[0], ray[1], ray[2], ray[3] - ray[0], ray[4] - ray[1], ray[5] - ray[2]);
gdxRayHeap.direction.nor();// needed for libgdx
RenderUnit entity = null;
for (final RenderUnit unit : this.units) {
final MdxComplexInstance instance = unit.instance;
if (instance.isVisible(this.worldScene.camera)
&& instance.intersectRayWithCollision(gdxRayHeap, intersectionHeap,
unit.getSimulationUnit().getUnitType().isBuilding(), false)
&& !unit.getSimulationUnit().isDead()) {
if ((entity == null) || (entity.instance.depth > instance.depth)) {
entity = unit;
}
}
}
final RenderUnit entity = rayPickUnit(x, y, CUnitFilterFunction.ACCEPT_ALL_LIVING);
List<RenderUnit> sel;
if (entity != null) {
if (toggle) {
@ -1277,8 +1334,8 @@ public class War3MapViewer extends ModelViewer {
RenderUnit entity = null;
for (final RenderUnit unit : this.units) {
final MdxComplexInstance instance = unit.instance;
if (instance.isVisible(this.worldScene.camera) && instance.intersectRayWithCollision(gdxRayHeap,
intersectionHeap, unit.getSimulationUnit().getUnitType().isBuilding(), false)) {
if (instance.shown() && instance.isVisible(this.worldScene.camera) && instance.intersectRayWithCollision(
gdxRayHeap, intersectionHeap, unit.getSimulationUnit().getUnitType().isBuilding(), false)) {
if (filter.call(unit.getSimulationUnit()) && (intersectionHeap.z > this.terrain
.getGroundHeight(intersectionHeap.x, intersectionHeap.y))) {
if ((entity == null) || (entity.instance.depth > instance.depth)) {
@ -1438,7 +1495,7 @@ public class War3MapViewer extends ModelViewer {
public void setGameUI(final GameUI gameUI) {
this.gameUI = gameUI;
this.abilityDataUI = new AbilityDataUI(this.allObjectData.getAbilities(), this.allObjectData.getUnits(),
gameUI);
this.allObjectData.getUpgrades(), gameUI);
}
public GameUI getGameUI() {

View File

@ -0,0 +1,7 @@
package com.etheller.warsmash.viewer5.handlers.w3x.environment;
public interface BuildingShadow {
void remove();
void move(float x, float y);
}

View File

@ -1,7 +1,10 @@
package com.etheller.warsmash.viewer5.handlers.w3x.environment;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.etheller.warsmash.parsers.w3x.wpm.War3MapWpm;
@ -20,18 +23,20 @@ public class PathingGrid {
private final short[] dynamicPathingOverlay; // for buildings and trees
private final int[] pathingGridSizes;
private final float[] centerOffset;
private final List<RemovablePathingMapInstance> dynamicPathingInstances;
public PathingGrid(final War3MapWpm terrainPathing, final float[] centerOffset) {
this.centerOffset = centerOffset;
this.pathingGrid = terrainPathing.getPathing();
this.pathingGridSizes = terrainPathing.getSize();
this.dynamicPathingOverlay = new short[this.pathingGrid.length];
this.dynamicPathingInstances = new ArrayList<>();
}
// this blit function is basically copied from HiveWE, maybe remember to mention
// that in credits as well:
// https://github.com/stijnherfst/HiveWE/blob/master/Base/PathingMap.cpp
public void blitPathingOverlayTexture(final float positionX, final float positionY, final int rotationInput,
private void blitPathingOverlayTexture(final float positionX, final float positionY, final int rotationInput,
final BufferedImage pathingTextureTga) {
final int rotation = (rotationInput + 450) % 360;
final int divW = ((rotation % 180) != 0) ? pathingTextureTga.getHeight() : pathingTextureTga.getWidth();
@ -80,6 +85,15 @@ public class PathingGrid {
}
}
public RemovablePathingMapInstance blitRemovablePathingOverlayTexture(final float positionX, final float positionY,
final int rotationInput, final BufferedImage pathingTextureTga) {
final RemovablePathingMapInstance removablePathingMapInstance = new RemovablePathingMapInstance(positionX,
positionY, rotationInput, pathingTextureTga);
removablePathingMapInstance.blit();
this.dynamicPathingInstances.add(removablePathingMapInstance);
return removablePathingMapInstance;
}
public int getWidth() {
return this.pathingGridSizes[0];
}
@ -294,4 +308,31 @@ public class PathingGrid {
this.preventionFlag = preventionFlag;
}
}
public final class RemovablePathingMapInstance {
private final float positionX;
private final float positionY;
private final int rotationInput;
private final BufferedImage pathingTextureTga;
public RemovablePathingMapInstance(final float positionX, final float positionY, final int rotationInput,
final BufferedImage pathingTextureTga) {
this.positionX = positionX;
this.positionY = positionY;
this.rotationInput = rotationInput;
this.pathingTextureTga = pathingTextureTga;
}
private void blit() {
blitPathingOverlayTexture(this.positionX, this.positionY, this.rotationInput, this.pathingTextureTga);
}
public void remove() {
PathingGrid.this.dynamicPathingInstances.remove(this);
Arrays.fill(PathingGrid.this.dynamicPathingOverlay, (short) 0);
for (final RemovablePathingMapInstance instance : PathingGrid.this.dynamicPathingInstances) {
instance.blit();
}
}
}
}

View File

@ -52,8 +52,6 @@ public class RenderUnit {
public SplatMover shadow;
public SplatMover selectionCircle;
private float x;
private float y;
private float facing;
private boolean swimming;
@ -67,13 +65,17 @@ public class RenderUnit {
private boolean corpse;
private boolean boneCorpse;
private final RenderUnitTypeData typeData;
public final MdxModel specialArtModel;
public SplatMover uberSplat;
public RenderUnit(final War3MapViewer map, final MdxModel model, final MutableGameObject row, final float x,
final float y, final float z, final int playerIndex, final UnitSoundset soundset,
final MdxModel portraitModel, final CUnit simulationUnit, final RenderUnitTypeData typeData) {
final MdxModel portraitModel, final CUnit simulationUnit, final RenderUnitTypeData typeData,
final MdxModel specialArtModel) {
this.portraitModel = portraitModel;
this.simulationUnit = simulationUnit;
this.typeData = typeData;
this.specialArtModel = specialArtModel;
final MdxComplexInstance instance = (MdxComplexInstance) model.addInstance();
this.location[0] = x;
@ -83,8 +85,6 @@ public class RenderUnit {
this.facing = simulationUnit.getFacing();
final float angle = (float) Math.toRadians(this.facing);
// instance.localRotation.setFromAxisRad(RenderMathUtils.VEC3_UNIT_Z, angle);
this.x = simulationUnit.getX();
this.y = simulationUnit.getY();
instance.rotate(tempQuat.setFromAxisRad(RenderMathUtils.VEC3_UNIT_Z, angle));
this.playerIndex = playerIndex & 0xFFFF;
instance.setTeamColor(this.playerIndex);
@ -139,9 +139,11 @@ public class RenderUnit {
public void populateCommandCard(final CSimulation game, final CommandButtonListener commandButtonListener,
final AbilityDataUI abilityDataUI, final int subMenuOrderId) {
final CommandCardPopulatingAbilityVisitor commandCardPopulatingVisitor = CommandCardPopulatingAbilityVisitor.INSTANCE
.reset(game, this.simulationUnit, commandButtonListener, abilityDataUI, subMenuOrderId,
this.simulationUnit.isConstructing());
for (final CAbility ability : this.simulationUnit.getAbilities()) {
ability.visit(CommandCardPopulatingAbilityVisitor.INSTANCE.reset(game, this.simulationUnit,
commandButtonListener, abilityDataUI, subMenuOrderId));
ability.visit(commandCardPopulatingVisitor);
}
}
@ -161,48 +163,48 @@ public class RenderUnit {
}
else {
this.instance.show();
if (wasHidden) {
if (this.shadow != null) {
this.shadow.show(map.terrain.centerOffset);
}
}
}
final float prevX = this.location[0];
final float prevY = this.location[1];
final float simulationX = this.simulationUnit.getX();
final float simulationY = this.simulationUnit.getY();
if (wasHidden) {
this.x = simulationX;
this.y = simulationY;
}
final float deltaTime = Gdx.graphics.getDeltaTime();
final float simDx = simulationX - this.x;
final float simDy = simulationY - this.y;
final float simDx = simulationX - this.location[0];
final float simDy = simulationY - this.location[1];
final float distanceToSimulation = (float) Math.sqrt((simDx * simDx) + (simDy * simDy));
final int speed = this.simulationUnit.getSpeed();
final float speedDelta = speed * deltaTime;
if ((distanceToSimulation > speedDelta) && (deltaTime < 1.0)) {
// The 1.0 here says that after 1 second of lag, units just teleport to show
// where they actually are
this.x += (speedDelta * simDx) / distanceToSimulation;
this.y += (speedDelta * simDy) / distanceToSimulation;
this.location[0] += (speedDelta * simDx) / distanceToSimulation;
this.location[1] += (speedDelta * simDy) / distanceToSimulation;
}
else {
this.x = simulationX;
this.y = simulationY;
this.location[0] = simulationX;
this.location[1] = simulationY;
}
final float x = this.x;
final float dx = x - this.location[0];
this.location[0] = x;
final float y = this.y;
final float dy = y - this.location[1];
this.location[1] = y;
final float dx = this.location[0] - prevX;
final float dy = this.location[1] - prevY;
final float groundHeight;
final MovementType movementType = this.simulationUnit.getUnitType().getMovementType();
final short terrainPathing = map.terrain.pathingGrid.getPathing(x, y);
final short terrainPathing = map.terrain.pathingGrid.getPathing(this.location[0], this.location[1]);
boolean swimming = (movementType == MovementType.AMPHIBIOUS)
&& PathingGrid.isPathingFlag(terrainPathing, PathingGrid.PathingType.SWIMMABLE)
&& !PathingGrid.isPathingFlag(terrainPathing, PathingGrid.PathingType.WALKABLE);
final float groundHeightTerrain = map.terrain.getGroundHeight(x, y);
final float groundHeightTerrain = map.terrain.getGroundHeight(this.location[0], this.location[1]);
float groundHeightTerrainAndWater;
MdxComplexInstance currentWalkableUnder;
final boolean standingOnWater = (swimming) || (movementType == MovementType.FLOAT)
|| (movementType == MovementType.FLY) || (movementType == MovementType.HOVER);
if (standingOnWater) {
groundHeightTerrainAndWater = Math.max(groundHeightTerrain, map.terrain.getWaterHeight(x, y));
groundHeightTerrainAndWater = Math.max(groundHeightTerrain,
map.terrain.getWaterHeight(this.location[0], this.location[1]));
}
else {
// land units will have their feet pass under the surface of the water
@ -214,8 +216,8 @@ public class RenderUnit {
currentWalkableUnder = null;
}
else {
currentWalkableUnder = map.getHighestWalkableUnder(x, y);
War3MapViewer.gdxRayHeap.set(x, y, 4096, 0, 0, -8192);
currentWalkableUnder = map.getHighestWalkableUnder(this.location[0], this.location[1]);
War3MapViewer.gdxRayHeap.set(this.location[0], this.location[1], 4096, 0, 0, -8192);
if ((currentWalkableUnder != null)
&& currentWalkableUnder.intersectRayWithCollision(War3MapViewer.gdxRayHeap,
War3MapViewer.intersectionHeap, true, true)
@ -244,6 +246,10 @@ public class RenderUnit {
this.shadow.destroy(Gdx.gl30, map.terrain.centerOffset);
this.shadow = null;
}
if (this.uberSplat != null) {
this.uberSplat.destroy(Gdx.gl30, map.terrain.centerOffset);
this.uberSplat = null;
}
if (this.selectionCircle != null) {
this.selectionCircle.destroy(Gdx.gl30, map.terrain.centerOffset);
this.selectionCircle = null;
@ -311,15 +317,15 @@ public class RenderUnit {
final float maxRoll = this.typeData.getMaxRoll();
final float sampleRadius = this.typeData.getElevationSampleRadius();
float pitch, roll;
final float pitchSampleForwardX = x + (sampleRadius * (float) Math.cos(facingRadians));
final float pitchSampleForwardY = y + (sampleRadius * (float) Math.sin(facingRadians));
final float pitchSampleBackwardX = x - (sampleRadius * (float) Math.cos(facingRadians));
final float pitchSampleBackwardY = y - (sampleRadius * (float) Math.sin(facingRadians));
final float pitchSampleForwardX = this.location[0] + (sampleRadius * (float) Math.cos(facingRadians));
final float pitchSampleForwardY = this.location[1] + (sampleRadius * (float) Math.sin(facingRadians));
final float pitchSampleBackwardX = this.location[0] - (sampleRadius * (float) Math.cos(facingRadians));
final float pitchSampleBackwardY = this.location[1] - (sampleRadius * (float) Math.sin(facingRadians));
final double leftOfFacingAngle = facingRadians + (Math.PI / 2);
final float rollSampleForwardX = x + (sampleRadius * (float) Math.cos(leftOfFacingAngle));
final float rollSampleForwardY = y + (sampleRadius * (float) Math.sin(leftOfFacingAngle));
final float rollSampleBackwardX = x - (sampleRadius * (float) Math.cos(leftOfFacingAngle));
final float rollSampleBackwardY = y - (sampleRadius * (float) Math.sin(leftOfFacingAngle));
final float rollSampleForwardX = this.location[0] + (sampleRadius * (float) Math.cos(leftOfFacingAngle));
final float rollSampleForwardY = this.location[1] + (sampleRadius * (float) Math.sin(leftOfFacingAngle));
final float rollSampleBackwardX = this.location[0] - (sampleRadius * (float) Math.cos(leftOfFacingAngle));
final float rollSampleBackwardY = this.location[1] - (sampleRadius * (float) Math.sin(leftOfFacingAngle));
final float pitchSampleGroundHeight1;
final float pitchSampleGroundHeight2;
final float rollSampleGroundHeight1;
@ -504,4 +510,21 @@ public class RenderUnit {
this.allowRarityVariations = allowRarityVariations;
}
}
public void repositioned(final War3MapViewer map) {
final float prevX = this.location[0];
final float prevY = this.location[1];
final float simulationX = this.simulationUnit.getX();
final float simulationY = this.simulationUnit.getY();
final float dx = simulationX - prevX;
final float dy = simulationY - prevY;
if (this.shadow != null) {
this.shadow.move(dx, dy, map.terrain.centerOffset);
}
if (this.selectionCircle != null) {
this.selectionCircle.move(dx, dy, map.terrain.centerOffset);
}
this.location[0] = this.simulationUnit.getX();
this.location[1] = this.simulationUnit.getY();
}
}

View File

@ -1,6 +1,8 @@
package com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.badlogic.gdx.graphics.Texture;
@ -26,8 +28,14 @@ public class AbilityDataUI {
private static final War3ID UNIT_ICON_NORMAL_Y = War3ID.fromString("ubpy");
private static final War3ID UNIT_ICON_NORMAL = War3ID.fromString("uico");
private static final War3ID UPGRADE_ICON_NORMAL_X = War3ID.fromString("gbpx");
private static final War3ID UPGRADE_ICON_NORMAL_Y = War3ID.fromString("gbpy");
private static final War3ID UPGRADE_ICON_NORMAL = War3ID.fromString("gar1");
private static final War3ID UPGRADE_LEVELS = War3ID.fromString("glvl");
private final Map<War3ID, AbilityIconUI> rawcodeToUI = new HashMap<>();
private final Map<War3ID, IconUI> rawcodeToUnitUI = new HashMap<>();
private final Map<War3ID, List<IconUI>> rawcodeToUpgradeUI = new HashMap<>();
private final IconUI moveUI;
private final IconUI stopUI;
private final IconUI holdPosUI;
@ -41,8 +49,10 @@ public class AbilityDataUI {
private final IconUI buildNeutralUI;
private final IconUI buildNagaUI;
private final IconUI cancelUI;
private final IconUI cancelBuildUI;
public AbilityDataUI(final MutableObjectData abilityData, final MutableObjectData unitData, final GameUI gameUI) {
public AbilityDataUI(final MutableObjectData abilityData, final MutableObjectData unitData,
final MutableObjectData upgradeData, final GameUI gameUI) {
final String disabledPrefix = gameUI.getSkinField("CommandButtonDisabledArtPath");
for (final War3ID alias : abilityData.keySet()) {
final MutableGameObject abilityTypeData = abilityData.get(alias);
@ -75,6 +85,21 @@ public class AbilityDataUI {
final Texture iconNormalDisabled = gameUI.loadTexture(disable(iconNormalPath, disabledPrefix));
this.rawcodeToUnitUI.put(alias, new IconUI(iconNormal, iconNormalDisabled, iconNormalX, iconNormalY));
}
for (final War3ID alias : upgradeData.keySet()) {
final MutableGameObject upgradeTypeData = upgradeData.get(alias);
final int upgradeLevels = upgradeTypeData.getFieldAsInteger(UPGRADE_LEVELS, 0);
final int iconNormalX = upgradeTypeData.getFieldAsInteger(UPGRADE_ICON_NORMAL_X, 0);
final int iconNormalY = upgradeTypeData.getFieldAsInteger(UPGRADE_ICON_NORMAL_Y, 0);
final List<IconUI> upgradeIconsByLevel = new ArrayList<>();
for (int i = 0; i < upgradeLevels; i++) {
final String iconNormalPath = gameUI
.trySkinField(upgradeTypeData.getFieldAsString(UPGRADE_ICON_NORMAL, i));
final Texture iconNormal = gameUI.loadTexture(iconNormalPath);
final Texture iconNormalDisabled = gameUI.loadTexture(disable(iconNormalPath, disabledPrefix));
upgradeIconsByLevel.add(new IconUI(iconNormal, iconNormalDisabled, iconNormalX, iconNormalY));
}
this.rawcodeToUpgradeUI.put(alias, upgradeIconsByLevel);
}
this.moveUI = createBuiltInIconUI(gameUI, "CmdMove", disabledPrefix);
this.stopUI = createBuiltInIconUI(gameUI, "CmdStop", disabledPrefix);
this.holdPosUI = createBuiltInIconUI(gameUI, "CmdHoldPos", disabledPrefix);
@ -88,6 +113,7 @@ public class AbilityDataUI {
this.buildNeutralUI = createBuiltInIconUI(gameUI, "CmdBuild", disabledPrefix);
this.attackGroundUI = createBuiltInIconUI(gameUI, "CmdAttackGround", disabledPrefix);
this.cancelUI = createBuiltInIconUI(gameUI, "CmdCancel", disabledPrefix);
this.cancelBuildUI = createBuiltInIconUI(gameUI, "CmdCancelBuild", disabledPrefix);
}
private IconUI createBuiltInIconUI(final GameUI gameUI, final String key, final String disabledPrefix) {
@ -108,6 +134,19 @@ public class AbilityDataUI {
return this.rawcodeToUnitUI.get(rawcode);
}
public IconUI getUpgradeUI(final War3ID rawcode, final int level) {
final List<IconUI> upgradeUI = this.rawcodeToUpgradeUI.get(rawcode);
if (upgradeUI != null) {
if (level < upgradeUI.size()) {
return upgradeUI.get(level);
}
else {
return upgradeUI.get(upgradeUI.size() - 1);
}
}
return null;
}
private static String disable(final String path, final String disabledPrefix) {
final int slashIndex = path.lastIndexOf('\\');
String name = path;
@ -169,4 +208,8 @@ public class AbilityDataUI {
return this.cancelUI;
}
public IconUI getCancelBuildUI() {
return this.cancelBuildUI;
}
}

View File

@ -11,6 +11,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityG
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityMove;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityVisitor;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.AbstractCAbilityBuild;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAbilityBuildInProgress;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAbilityHumanBuild;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAbilityNagaBuild;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAbilityNeutralBuild;
@ -18,7 +19,9 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAb
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAbilityOrcBuild;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAbilityUndeadBuild;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.combat.CAbilityColdArrows;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityQueue;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer;
public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void> {
public static final CommandCardPopulatingAbilityVisitor INSTANCE = new CommandCardPopulatingAbilityVisitor();
@ -29,22 +32,24 @@ public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void
private AbilityDataUI abilityDataUI;
private int menuBaseOrderId;
private boolean hasStop;
private boolean underConstruction;
public CommandCardPopulatingAbilityVisitor reset(final CSimulation game, final CUnit unit,
final CommandButtonListener commandButtonListener, final AbilityDataUI abilityDataUI,
final int menuBaseOrderId) {
final int menuBaseOrderId, final boolean underConstruction) {
this.game = game;
this.unit = unit;
this.commandButtonListener = commandButtonListener;
this.abilityDataUI = abilityDataUI;
this.menuBaseOrderId = menuBaseOrderId;
this.underConstruction = underConstruction;
this.hasStop = false;
return this;
}
@Override
public Void accept(final CAbilityAttack ability) {
if (this.menuBaseOrderId == 0) {
if ((this.menuBaseOrderId == 0) && !this.underConstruction) {
addCommandButton(ability, this.abilityDataUI.getAttackUI(), ability.getHandleId(), OrderIds.attack, 0,
false, false);
if (!this.hasStop) {
@ -57,7 +62,7 @@ public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void
@Override
public Void accept(final CAbilityMove ability) {
if (this.menuBaseOrderId == 0) {
if ((this.menuBaseOrderId == 0) && !this.underConstruction) {
addCommandButton(ability, this.abilityDataUI.getMoveUI(), ability.getHandleId(), OrderIds.move, 0, false,
false);
addCommandButton(ability, this.abilityDataUI.getHoldPosUI(), ability.getHandleId(), OrderIds.holdposition,
@ -74,7 +79,7 @@ public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void
@Override
public Void accept(final CAbilityGeneric ability) {
if (this.menuBaseOrderId == 0) {
if ((this.menuBaseOrderId == 0) && !this.underConstruction) {
addCommandButton(ability, this.abilityDataUI.getUI(ability.getRawcode()).getOnIconUI(),
ability.getHandleId(), 0, 0, false, false);
}
@ -83,7 +88,7 @@ public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void
@Override
public Void accept(final CAbilityColdArrows ability) {
if (this.menuBaseOrderId == 0) {
if ((this.menuBaseOrderId == 0) && !this.underConstruction) {
final boolean autoCastActive = ability.isAutoCastActive();
int autoCastId;
if (autoCastActive) {
@ -135,7 +140,7 @@ public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void
}
private void handleBuildMenu(final AbstractCAbilityBuild ability, final IconUI buildUI) {
if (this.menuBaseOrderId == ability.getBaseOrderId()) {
if ((this.menuBaseOrderId == ability.getBaseOrderId()) && !this.underConstruction) {
for (final War3ID unitType : ability.getStructuresBuilt()) {
final IconUI unitUI = this.abilityDataUI.getUnitUI(unitType);
if (unitUI != null) {
@ -155,4 +160,33 @@ public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void
this.commandButtonListener.commandButton(iconUI.getButtonPositionX(), iconUI.getButtonPositionY(),
iconUI.getIcon(), handleId, orderId, autoCastOrderId, active, autoCastActive, menuButton);
}
@Override
public Void accept(final CAbilityBuildInProgress ability) {
if (this.menuBaseOrderId == 0) {
addCommandButton(ability, this.abilityDataUI.getCancelBuildUI(), ability.getHandleId(), OrderIds.cancel, 0,
false, false);
}
return null;
}
@Override
public Void accept(final CAbilityQueue ability) {
if ((this.menuBaseOrderId == 0) && !this.underConstruction) {
for (final War3ID unitType : ability.getUnitsTrained()) {
final IconUI unitUI = this.abilityDataUI.getUnitUI(unitType);
if (unitUI != null) {
addCommandButton(ability, unitUI, ability.getHandleId(), unitType.getValue(), 0, false, false);
}
}
for (final War3ID unitType : ability.getResearchesAvailable()) {
final CPlayer player = this.game.getPlayer(this.unit.getPlayerIndex());
final IconUI unitUI = this.abilityDataUI.getUpgradeUI(unitType, player.getTechtreeUnlocked(unitType));
if (unitUI != null) {
addCommandButton(ability, unitUI, ability.getHandleId(), unitType.getValue(), 0, false, false);
}
}
}
return null;
}
}

View File

@ -15,7 +15,9 @@ import com.etheller.warsmash.units.DataTable;
import com.etheller.warsmash.units.manager.MutableObjectData;
import com.etheller.warsmash.util.War3ID;
import com.etheller.warsmash.util.WarsmashConstants;
import com.etheller.warsmash.viewer5.handlers.w3x.environment.BuildingShadow;
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid;
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid.RemovablePathingMapInstance;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackInstant;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackMissile;
@ -59,7 +61,7 @@ public class CSimulation {
this.simulationRenderController = simulationRenderController;
this.pathingGrid = pathingGrid;
this.abilityData = new CAbilityData(parsedAbilityData);
this.unitData = new CUnitData(parsedUnitData, this.abilityData);
this.unitData = new CUnitData(parsedUnitData, this.abilityData, this.simulationRenderController);
this.units = new ArrayList<>();
this.newUnits = new ArrayList<>();
this.projectiles = new ArrayList<>();
@ -118,14 +120,12 @@ public class CSimulation {
}
public CUnit createUnit(final War3ID typeId, final int playerIndex, final float x, final float y,
final float facing, final BufferedImage buildingPathingPixelMap) {
final float facing, final BufferedImage buildingPathingPixelMap,
final RemovablePathingMapInstance pathingInstance, final BuildingShadow buildingShadowInstance) {
final CUnit unit = this.unitData.create(this, playerIndex, typeId, x, y, facing, buildingPathingPixelMap,
this.simulationRenderController, this.handleIdAllocator);
this.handleIdAllocator, pathingInstance, buildingShadowInstance);
this.newUnits.add(unit);
this.handleIdToUnit.put(unit.getHandleId(), unit);
for (final CAbility ability : unit.getAbilities()) {
this.handleIdToAbility.put(ability.getHandleId(), ability);
}
this.worldCollision.addUnit(unit);
return unit;
}
@ -143,6 +143,10 @@ public class CSimulation {
return this.handleIdToAbility.get(handleId);
}
protected void onAbilityAddedToUnit(final CUnit unit, final CAbility ability) {
this.handleIdToAbility.put(ability.getHandleId(), ability);
}
public CAttackProjectile createProjectile(final CUnit source, final float launchX, final float launchY,
final float launchFacing, final CUnitAttackMissile attack, final CWidget target, final float damage,
final int bounceIndex) {
@ -223,7 +227,7 @@ public class CSimulation {
this.simulationRenderController.spawnUnitDamageSound(damagedUnit, weaponSound, armorType);
}
public void unitConstructedEvent(CUnit constructingUnit, CUnit constructedStructure) {
public void unitConstructedEvent(final CUnit constructingUnit, final CUnit constructedStructure) {
this.simulationRenderController.spawnUnitConstructionSound(constructingUnit, constructedStructure);
}
@ -235,7 +239,23 @@ public class CSimulation {
return this.commandErrorListener;
}
public void unitConstructFinishEvent(CUnit constructedStructure) {
public void unitConstructFinishEvent(final CUnit constructedStructure) {
this.simulationRenderController.spawnUnitConstructionFinishSound(constructedStructure);
}
}
public void createBuildingDeathEffect(final CUnit cUnit) {
this.simulationRenderController.spawnBuildingDeathEffect(cUnit);
}
public HandleIdAllocator getHandleIdAllocator() {
return this.handleIdAllocator;
}
public void unitTrainedEvent(final CUnit trainingUnit, final CUnit trainedUnit) {
this.simulationRenderController.spawnUnitReadySound(trainedUnit);
}
public void unitRepositioned(final CUnit cUnit) {
this.simulationRenderController.unitRepositioned(cUnit);
}
}

View File

@ -3,6 +3,7 @@ package com.etheller.warsmash.viewer5.handlers.w3x.simulation;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
@ -12,8 +13,11 @@ import com.etheller.warsmash.util.War3ID;
import com.etheller.warsmash.util.WarsmashConstants;
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
import com.etheller.warsmash.viewer5.handlers.w3x.SequenceUtils;
import com.etheller.warsmash.viewer5.handlers.w3x.environment.BuildingShadow;
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid.RemovablePathingMapInstance;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitStateListener.CUnitStateNotifier;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAbilityBuildInProgress;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorAttack;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorFollow;
@ -49,6 +53,8 @@ public class CUnit extends CWidget {
private final CUnitType unitType;
private Rectangle collisionRectangle;
private RemovablePathingMapInstance pathingInstance;
private BuildingShadow buildingShadowInstance;
private final EnumSet<CUnitClassification> classifications = EnumSet.noneOf(CUnitClassification.class);
@ -75,10 +81,13 @@ public class CUnit extends CWidget {
private boolean hidden = false;
private boolean updating = true;
private CUnit workerInside;
private final War3ID[] buildQueue = new War3ID[WarsmashConstants.BUILD_QUEUE_SIZE];
private final QueueItemType[] buildQueueTypes = new QueueItemType[WarsmashConstants.BUILD_QUEUE_SIZE];
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,
final int speed, final int defense, final CUnitType unitType) {
final int speed, final int defense, final CUnitType unitType,
final RemovablePathingMapInstance pathingInstance, final BuildingShadow buildingShadowInstance) {
super(handleId, x, y, life);
this.playerIndex = playerIndex;
this.typeId = typeId;
@ -88,6 +97,8 @@ public class CUnit extends CWidget {
this.maximumMana = maximumMana;
this.speed = speed;
this.defense = defense;
this.pathingInstance = pathingInstance;
this.buildingShadowInstance = buildingShadowInstance;
this.flyHeight = unitType.getDefaultFlyingHeight();
this.unitType = unitType;
this.classifications.addAll(unitType.getClassifications());
@ -107,6 +118,7 @@ public class CUnit extends CWidget {
public void add(final CSimulation simulation, final CAbility ability) {
this.abilities.add(ability);
simulation.onAbilityAddedToUnit(this, ability);
ability.onAdd(simulation, this);
}
@ -207,33 +219,87 @@ public class CUnit extends CWidget {
else if (this.updating) {
if (this.constructing) {
this.constructionProgress += WarsmashConstants.SIMULATION_STEP_TIME;
if (this.constructionProgress >= this.unitType.getBuildTime()) {
final int buildTime = this.unitType.getBuildTime();
final float healthGain = (WarsmashConstants.SIMULATION_STEP_TIME / buildTime)
* (this.maximumLife * (1.0f - WarsmashConstants.BUILDING_CONSTRUCT_START_LIFE));
setLife(game, Math.min(this.life + healthGain, this.maximumLife));
if (this.constructionProgress >= buildTime) {
this.constructing = false;
if (this.workerInside != null) {
this.workerInside.setHidden(false);
this.workerInside.setUpdating(true);
this.workerInside.nudgeAround(game, this);
this.workerInside = null;
this.constructionProgress = 0;
popoutWorker(game);
final Iterator<CAbility> abilityIterator = this.abilities.iterator();
while (abilityIterator.hasNext()) {
final CAbility ability = abilityIterator.next();
if (ability instanceof CAbilityBuildInProgress) {
abilityIterator.remove();
}
}
game.unitConstructFinishEvent(this);
this.stateNotifier.ordersChanged(getCurrentAbilityHandleId(), getCurrentOrderId());
}
}
else if (this.currentBehavior != null) {
final CBehavior lastBehavior = this.currentBehavior;
this.currentBehavior = this.currentBehavior.update(game);
if (this.currentBehavior.getHighlightOrderId() != lastBehavior.getHighlightOrderId()) {
this.stateNotifier.ordersChanged(getCurrentAbilityHandleId(), getCurrentOrderId());
}
}
else {
// check to auto acquire targets
autoAcquireAttackTargets(game);
final War3ID queuedRawcode = this.buildQueue[0];
if (queuedRawcode != null) {
// queue step forward
this.constructionProgress += WarsmashConstants.SIMULATION_STEP_TIME;
if (this.buildQueueTypes[0] == QueueItemType.UNIT) {
final CUnitType trainedUnitType = game.getUnitData().getUnitType(queuedRawcode);
if (this.constructionProgress >= trainedUnitType.getBuildTime()) {
this.constructionProgress = 0;
final CUnit trainedUnit = game.createUnit(queuedRawcode, this.playerIndex, getX(), getY(),
game.getGameplayConstants().getBuildingAngle());
// nudge the trained unit out around us
trainedUnit.nudgeAround(game, this);
game.unitTrainedEvent(this, trainedUnit);
for (int i = 0; i < (this.buildQueue.length - 1); i++) {
this.buildQueue[i] = this.buildQueue[i + 1];
this.buildQueueTypes[i] = this.buildQueueTypes[i + 1];
}
this.buildQueue[this.buildQueue.length - 1] = null;
this.buildQueueTypes[this.buildQueue.length - 1] = null;
this.stateNotifier.queueChanged();
}
}
else if (this.buildQueueTypes[0] == QueueItemType.RESEARCH) {
final CUnitType trainedUnitType = game.getUnitData().getUnitType(queuedRawcode);
if (this.constructionProgress >= trainedUnitType.getBuildTime()) {
this.constructionProgress = 0;
for (int i = 0; i < (this.buildQueue.length - 1); i++) {
this.buildQueue[i] = this.buildQueue[i + 1];
this.buildQueueTypes[i] = this.buildQueueTypes[i + 1];
}
this.buildQueue[this.buildQueue.length - 1] = null;
this.buildQueueTypes[this.buildQueue.length - 1] = null;
this.stateNotifier.queueChanged();
}
}
}
if (this.currentBehavior != null) {
final CBehavior lastBehavior = this.currentBehavior;
this.currentBehavior = this.currentBehavior.update(game);
if (this.currentBehavior.getHighlightOrderId() != lastBehavior.getHighlightOrderId()) {
this.stateNotifier.ordersChanged(getCurrentAbilityHandleId(), getCurrentOrderId());
}
}
else {
// check to auto acquire targets
autoAcquireAttackTargets(game);
}
}
}
return false;
}
private void popoutWorker(final CSimulation game) {
if (this.workerInside != null) {
this.workerInside.setHidden(false);
this.workerInside.setUpdating(true);
this.workerInside.nudgeAround(game, this);
this.workerInside = null;
}
}
public void autoAcquireAttackTargets(final CSimulation game) {
if (!this.unitType.getAttacks().isEmpty()) {
if (this.collisionRectangle != null) {
@ -452,7 +518,7 @@ public class CUnit extends CWidget {
simulation.unitDamageEvent(this, weaponType, this.unitType.getArmorType());
this.stateNotifier.lifeChanged();
if (isDead()) {
if (!wasDead && !this.unitType.isBuilding()) {
if (!wasDead) {
kill(simulation);
}
}
@ -474,7 +540,21 @@ public class CUnit extends CWidget {
private void kill(final CSimulation simulation) {
this.currentBehavior = null;
this.orderQueue.clear();
this.deathTurnTick = simulation.getGameTurnTick();
if (this.constructing) {
simulation.createBuildingDeathEffect(this);
}
else {
this.deathTurnTick = simulation.getGameTurnTick();
}
if (this.pathingInstance != null) {
this.pathingInstance.remove();
this.pathingInstance = null;
}
if (this.buildingShadowInstance != null) {
this.buildingShadowInstance.remove();
this.buildingShadowInstance = null;
}
popoutWorker(simulation);
}
public boolean canReach(final CWidget target, final float range) {
@ -753,5 +833,62 @@ public class CUnit extends CWidget {
}
setX(x, simulation.getWorldCollision());
setY(y, simulation.getWorldCollision());
simulation.unitRepositioned(this);
}
@Override
public void setLife(final CSimulation simulation, final float life) {
final boolean wasDead = isDead();
super.setLife(simulation, life);
if (isDead() && !wasDead) {
kill(simulation);
}
this.stateNotifier.lifeChanged();
}
private void queue(final War3ID rawcode, final QueueItemType queueItemType) {
for (int i = 0; i < this.buildQueue.length; i++) {
if (this.buildQueue[i] == null) {
this.buildQueue[i] = rawcode;
this.buildQueueTypes[i] = queueItemType;
break;
}
}
}
public War3ID[] getBuildQueue() {
return this.buildQueue;
}
public QueueItemType[] getBuildQueueTypes() {
return this.buildQueueTypes;
}
public float getBuildQueueTimeRemaining(final CSimulation simulation) {
if (this.buildQueueTypes[0] == null) {
return 0;
}
switch (this.buildQueueTypes[0]) {
case RESEARCH:
return 999; // TODO
case UNIT:
final CUnitType trainedUnitType = simulation.getUnitData().getUnitType(this.buildQueue[0]);
return trainedUnitType.getBuildTime();
default:
return 0;
}
}
public void queueTrainingUnit(final War3ID rawcode) {
queue(rawcode, QueueItemType.UNIT);
}
public void queueResearch(final War3ID rawcode) {
queue(rawcode, QueueItemType.RESEARCH);
}
public static enum QueueItemType {
UNIT,
RESEARCH;
}
}

View File

@ -9,4 +9,11 @@ public interface CUnitFilterFunction {
return true;
}
};
CUnitFilterFunction ACCEPT_ALL_LIVING = new CUnitFilterFunction() {
@Override
public boolean call(final CUnit unit) {
return !unit.isDead();
}
};
}

View File

@ -7,6 +7,8 @@ public interface CUnitStateListener {
void ordersChanged(int abilityHandleId, int orderId);
void queueChanged();
public static final class CUnitStateNotifier extends SubscriberSetNotifier<CUnitStateListener>
implements CUnitStateListener {
@Override
@ -22,5 +24,12 @@ public interface CUnitStateListener {
listener.ordersChanged(abilityHandleId, orderId);
}
}
@Override
public void queueChanged() {
for (final CUnitStateListener listener : set) {
listener.queueChanged();
}
}
}
}

View File

@ -38,6 +38,8 @@ public class CUnitType {
private final float defaultAcquisitionRange;
private final float minimumAttackRange;
private final List<War3ID> structuresBuilt;
private final List<War3ID> unitsTrained;
private final List<War3ID> researchesAvailable;
private final CUnitRace unitRace;
private final int goldCost;
private final int lumberCost;
@ -49,7 +51,8 @@ public class CUnitType {
final boolean raise, final boolean decay, final CDefenseType defenseType, final float impactZ,
final BufferedImage buildingPathingPixelMap, final float deathTime, final EnumSet<CTargetType> targetedAs,
final float defaultAcquisitionRange, final float minimumAttackRange, final List<War3ID> structuresBuilt,
final CUnitRace unitRace, final int goldCost, final int lumberCost, final int buildTime) {
final List<War3ID> unitsTrained, final List<War3ID> researchesAvailable, final CUnitRace unitRace,
final int goldCost, final int lumberCost, final int buildTime) {
this.name = name;
this.building = isBldg;
this.movementType = movementType;
@ -68,6 +71,8 @@ public class CUnitType {
this.defaultAcquisitionRange = defaultAcquisitionRange;
this.minimumAttackRange = minimumAttackRange;
this.structuresBuilt = structuresBuilt;
this.unitsTrained = unitsTrained;
this.researchesAvailable = researchesAvailable;
this.unitRace = unitRace;
this.goldCost = goldCost;
this.lumberCost = lumberCost;
@ -146,6 +151,14 @@ public class CUnitType {
return this.structuresBuilt;
}
public List<War3ID> getUnitsTrained() {
return this.unitsTrained;
}
public List<War3ID> getResearchesAvailable() {
return this.researchesAvailable;
}
public CUnitRace getRace() {
return this.unitRace;
}

View File

@ -42,7 +42,7 @@ public abstract class CWidget {
this.y = y;
}
public void setLife(final float life) {
public void setLife(final CSimulation simulation, final float life) {
this.life = life;
}

View File

@ -1,5 +1,6 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAbilityBuildInProgress;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAbilityHumanBuild;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAbilityNagaBuild;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAbilityNeutralBuild;
@ -7,6 +8,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAb
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAbilityOrcBuild;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAbilityUndeadBuild;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.combat.CAbilityColdArrows;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityQueue;
/**
* A visitor for the lowest level inherent types of an ability. It's a bit of a
@ -36,4 +38,8 @@ public interface CAbilityVisitor<T> {
T accept(CAbilityNagaBuild ability);
T accept(CAbilityNeutralBuild ability);
T accept(CAbilityBuildInProgress ability);
T accept(CAbilityQueue ability);
}

View File

@ -33,22 +33,29 @@ public abstract class AbstractCAbilityBuild extends AbstractCAbility implements
@Override
public void checkCanUse(final CSimulation game, final CUnit unit, final int orderId,
final AbilityActivationReceiver receiver) {
final CUnitType unitType = game.getUnitData().getUnitType(new War3ID(orderId));
if (unitType != null) {
final CPlayer player = game.getPlayer(unit.getPlayerIndex());
if (player.getGold() >= unitType.getGoldCost()) {
if (player.getLumber() >= unitType.getLumberCost()) {
receiver.useOk();
final War3ID orderIdAsRawtype = new War3ID(orderId);
if (this.structuresBuilt.contains(orderIdAsRawtype)) {
final CUnitType unitType = game.getUnitData().getUnitType(orderIdAsRawtype);
if (unitType != null) {
final CPlayer player = game.getPlayer(unit.getPlayerIndex());
if (player.getGold() >= unitType.getGoldCost()) {
if (player.getLumber() >= unitType.getLumberCost()) {
receiver.useOk();
}
else {
receiver.notEnoughResources(ResourceType.LUMBER);
}
}
else {
receiver.notEnoughResources(ResourceType.LUMBER);
receiver.notEnoughResources(ResourceType.GOLD);
}
}
else {
receiver.notEnoughResources(ResourceType.GOLD);
receiver.useOk();
}
}
else {
/// ???
receiver.useOk();
}
}

View File

@ -0,0 +1,85 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build;
import com.badlogic.gdx.math.Vector2;
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.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.AbstractCAbility;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityVisitor;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationReceiver;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver;
public class CAbilityBuildInProgress extends AbstractCAbility {
public CAbilityBuildInProgress(final int handleId) {
super(handleId);
}
@Override
public void onAdd(final CSimulation game, final CUnit unit) {
}
@Override
public void onRemove(final CSimulation game, final CUnit unit) {
}
@Override
public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId) {
caster.setLife(game, 0);
return false;
}
@Override
public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, final CWidget target) {
return null;
}
@Override
public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, final Vector2 point) {
return null;
}
@Override
public CBehavior beginNoTarget(final CSimulation game, final CUnit caster, final int orderId) {
return null;
}
@Override
public void checkCanUse(final CSimulation game, final CUnit unit, final int orderId,
final AbilityActivationReceiver receiver) {
receiver.useOk();
}
@Override
public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, final CWidget target,
final AbilityTargetCheckReceiver<CWidget> receiver) {
receiver.orderIdNotAccepted();
}
@Override
public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, final Vector2 target,
final AbilityTargetCheckReceiver<Vector2> receiver) {
receiver.orderIdNotAccepted();
}
@Override
public void checkCanTargetNoTarget(final CSimulation game, final CUnit unit, final int orderId,
final AbilityTargetCheckReceiver<Void> receiver) {
if (orderId == OrderIds.cancel) {
receiver.targetOk(null);
}
else {
receiver.orderIdNotAccepted();
}
}
@Override
public <T> T visit(final CAbilityVisitor<T> visitor) {
return visitor.accept(this);
}
}

View File

@ -0,0 +1,133 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import com.badlogic.gdx.math.Vector2;
import com.etheller.warsmash.util.War3ID;
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.CUnitType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.AbstractCAbility;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityVisitor;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationReceiver;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.ResourceType;
public final class CAbilityQueue extends AbstractCAbility {
private final Set<War3ID> unitsTrained;
private final Set<War3ID> researchesAvailable;
public CAbilityQueue(final int handleId, final List<War3ID> unitsTrained, final List<War3ID> researchesAvailable) {
super(handleId);
this.unitsTrained = new LinkedHashSet<>(unitsTrained);
this.researchesAvailable = new LinkedHashSet<>(researchesAvailable);
}
public Set<War3ID> getUnitsTrained() {
return this.unitsTrained;
}
public Set<War3ID> getResearchesAvailable() {
return this.researchesAvailable;
}
@Override
public void checkCanUse(final CSimulation game, final CUnit unit, final int orderId,
final AbilityActivationReceiver receiver) {
final War3ID orderIdAsRawtype = new War3ID(orderId);
if (this.unitsTrained.contains(orderIdAsRawtype) || this.researchesAvailable.contains(orderIdAsRawtype)) {
final CUnitType unitType = game.getUnitData().getUnitType(orderIdAsRawtype);
if (unitType != null) {
final CPlayer player = game.getPlayer(unit.getPlayerIndex());
if (player.getGold() >= unitType.getGoldCost()) {
if (player.getLumber() >= unitType.getLumberCost()) {
receiver.useOk();
}
else {
receiver.notEnoughResources(ResourceType.LUMBER);
}
}
else {
receiver.notEnoughResources(ResourceType.GOLD);
}
}
else {
receiver.useOk();
}
}
else {
/// ???
receiver.useOk();
}
}
@Override
public final void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, final CWidget target,
final AbilityTargetCheckReceiver<CWidget> receiver) {
receiver.orderIdNotAccepted();
}
@Override
public final void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, final Vector2 target,
final AbilityTargetCheckReceiver<Vector2> receiver) {
receiver.orderIdNotAccepted();
}
@Override
public final void checkCanTargetNoTarget(final CSimulation game, final CUnit unit, final int orderId,
final AbilityTargetCheckReceiver<Void> receiver) {
if (this.unitsTrained.contains(new War3ID(orderId)) || this.researchesAvailable.contains(new War3ID(orderId))) {
receiver.targetOk(null);
}
else {
receiver.orderIdNotAccepted();
}
}
@Override
public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId) {
return true;
}
@Override
public void onAdd(final CSimulation game, final CUnit unit) {
}
@Override
public void onRemove(final CSimulation game, final CUnit unit) {
}
@Override
public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, final CWidget target) {
return null;
}
@Override
public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, final Vector2 point) {
return null;
}
@Override
public CBehavior beginNoTarget(final CSimulation game, final CUnit caster, final int orderId) {
final War3ID rawcode = new War3ID(orderId);
if (this.unitsTrained.contains(rawcode)) {
caster.queueTrainingUnit(rawcode);
}
else if (this.researchesAvailable.contains(rawcode)) {
caster.queueResearch(rawcode);
}
return null;
}
@Override
public <T> T visit(final CAbilityVisitor<T> visitor) {
return visitor.accept(this);
}
}

View File

@ -0,0 +1,5 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.upgrade;
public class CAbilityUpgrade {
}

View File

@ -1,9 +1,11 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.build;
import com.etheller.warsmash.util.War3ID;
import com.etheller.warsmash.util.WarsmashConstants;
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.CUnitType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAbilityBuildInProgress;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CAbstractRangedPointTargetBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
@ -39,6 +41,9 @@ public class CBehaviorOrcBuild extends CAbstractRangedPointTargetBehavior {
this.targetY, simulation.getGameplayConstants().getBuildingAngle());
constructedStructure.setConstructing(true);
constructedStructure.setWorkerInside(this.unit);
constructedStructure.setLife(simulation,
constructedStructure.getMaximumLife() * WarsmashConstants.BUILDING_CONSTRUCT_START_LIFE);
constructedStructure.add(simulation, new CAbilityBuildInProgress(simulation.getHandleIdAllocator().createId()));
this.unit.setHidden(true);
this.unit.setUpdating(false);
simulation.unitConstructedEvent(this.unit, constructedStructure);

View File

@ -10,12 +10,15 @@ import java.util.Map;
import com.etheller.warsmash.units.manager.MutableObjectData;
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
import com.etheller.warsmash.util.War3ID;
import com.etheller.warsmash.viewer5.handlers.w3x.environment.BuildingShadow;
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid;
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid.RemovablePathingMapInstance;
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.CUnitClassification;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.HandleIdAllocator;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityAttack;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityMove;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAbilityHumanBuild;
@ -24,6 +27,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAb
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAbilityNightElfBuild;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAbilityOrcBuild;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAbilityUndeadBuild;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityQueue;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CAttackType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CDefenseType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType;
@ -128,6 +132,8 @@ public class CUnitData {
private static final War3ID ABILITIES_NORMAL = War3ID.fromString("uabi");
private static final War3ID STRUCTURES_BUILT = War3ID.fromString("ubui");
private static final War3ID UNITS_TRAINED = War3ID.fromString("utra");
private static final War3ID RESEARCHES_AVAILABLE = War3ID.fromString("ures");
private static final War3ID UNIT_RACE = War3ID.fromString("urac");
private static final War3ID GOLD_COST = War3ID.fromString("ugol");
@ -137,17 +143,19 @@ public class CUnitData {
private final MutableObjectData unitData;
private final Map<War3ID, CUnitType> unitIdToUnitType = new HashMap<>();
private final CAbilityData abilityData;
private SimulationRenderController simulationRenderController;
private final SimulationRenderController simulationRenderController;
public CUnitData(final MutableObjectData unitData, final CAbilityData abilityData) {
public CUnitData(final MutableObjectData unitData, final CAbilityData abilityData,
final SimulationRenderController simulationRenderController) {
this.unitData = unitData;
this.abilityData = abilityData;
this.simulationRenderController = simulationRenderController;
}
public CUnit create(final CSimulation simulation, final int playerIndex, final War3ID typeId, final float x,
final float y, final float facing, final BufferedImage buildingPathingPixelMap,
final SimulationRenderController simulationRenderController, final HandleIdAllocator handleIdAllocator) {
this.simulationRenderController = simulationRenderController;
final HandleIdAllocator handleIdAllocator, final RemovablePathingMapInstance pathingInstance,
final BuildingShadow buildingShadowInstance) {
final MutableGameObject unitType = this.unitData.get(typeId);
final int handleId = handleIdAllocator.createId();
final int life = unitType.getFieldAsInteger(HIT_POINT_MAXIMUM, 0);
@ -160,7 +168,7 @@ public class CUnitData {
final CUnitType unitTypeInstance = getUnitTypeInstance(typeId, buildingPathingPixelMap, unitType);
final CUnit unit = new CUnit(handleId, playerIndex, x, y, life, typeId, facing, manaInitial, life, manaMaximum,
speed, defense, unitTypeInstance);
speed, defense, unitTypeInstance, pathingInstance, buildingShadowInstance);
if (speed > 0) {
unit.add(simulation, new CAbilityMove(handleIdAllocator.createId()));
}
@ -193,9 +201,17 @@ public class CUnitData {
break;
}
}
final List<War3ID> unitsTrained = unitTypeInstance.getUnitsTrained();
final List<War3ID> researchesAvailable = unitTypeInstance.getResearchesAvailable();
if (!unitsTrained.isEmpty() || !researchesAvailable.isEmpty()) {
unit.add(simulation, new CAbilityQueue(handleIdAllocator.createId(), unitsTrained, researchesAvailable));
}
for (final String ability : abilityList.split(",")) {
if ((ability.length() > 0) && !"_".equals(ability)) {
unit.add(simulation, this.abilityData.createAbility(ability, handleIdAllocator.createId()));
final CAbility createAbility = this.abilityData.createAbility(ability, handleIdAllocator.createId());
if (createAbility != null) {
unit.add(simulation, createAbility);
}
}
}
return unit;
@ -338,6 +354,24 @@ public class CUnitData {
final int lumberCost = unitType.getFieldAsInteger(LUMBER_COST, 0);
final int buildTime = unitType.getFieldAsInteger(BUILD_TIME, 0);
final String unitsTrainedString = unitType.getFieldAsString(UNITS_TRAINED, 0);
final String[] unitsTrainedStringItems = unitsTrainedString.trim().split(",");
final List<War3ID> unitsTrained = new ArrayList<>();
for (final String unitsTrainedStringItem : unitsTrainedStringItems) {
if (unitsTrainedStringItem.length() == 4) {
unitsTrained.add(War3ID.fromString(unitsTrainedStringItem));
}
}
final String researchesAvailableString = unitType.getFieldAsString(RESEARCHES_AVAILABLE, 0);
final String[] researchesAvailableStringItems = researchesAvailableString.trim().split(",");
final List<War3ID> researchesAvailable = new ArrayList<>();
for (final String researchesAvailableStringItem : researchesAvailableStringItems) {
if (researchesAvailableStringItem.length() == 4) {
researchesAvailable.add(War3ID.fromString(researchesAvailableStringItem));
}
}
final String structuresBuiltString = unitType.getFieldAsString(STRUCTURES_BUILT, 0);
final String[] structuresBuiltStringItems = structuresBuiltString.split(",");
final List<War3ID> structuresBuilt = new ArrayList<>();
@ -352,8 +386,8 @@ public class CUnitData {
unitTypeInstance = new CUnitType(unitName, isBldg, movementType, moveHeight, collisionSize, classifications,
attacks, armorType, raise, decay, defenseType, impactZ, buildingPathingPixelMap, deathTime,
targetedAs, acquisitionRange, minimumAttackRange, structuresBuilt, unitRace, goldCost, lumberCost,
buildTime);
targetedAs, acquisitionRange, minimumAttackRange, structuresBuilt, unitsTrained,
researchesAvailable, unitRace, goldCost, lumberCost, buildTime);
this.unitIdToUnitType.put(typeId, unitTypeInstance);
}
return unitTypeInstance;

View File

@ -1,7 +1,10 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.players;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import com.etheller.warsmash.util.War3ID;
import com.etheller.warsmash.util.WarsmashConstants;
public class CPlayer {
@ -15,6 +18,7 @@ public class CPlayer {
private int gold = 5000;
private int lumber = 5000;
private final EnumSet<CAllianceType>[] alliances = new EnumSet[WarsmashConstants.MAX_PLAYERS];
private final Map<War3ID, Integer> rawcodeToTechtreeUnlocked = new HashMap<>();
public CPlayer(final int id, final CMapControl controlType, final String name, final CRace race,
final float[] startLocation) {
@ -106,4 +110,12 @@ public class CPlayer {
public void setColorIndex(final int colorIndex) {
this.colorIndex = colorIndex;
}
public int getTechtreeUnlocked(final War3ID rawcode) {
final Integer techtreeUnlocked = this.rawcodeToTechtreeUnlocked.get(rawcode);
if (techtreeUnlocked == null) {
return 0;
}
return techtreeUnlocked;
}
}

View File

@ -28,4 +28,10 @@ public interface SimulationRenderController {
BufferedImage getBuildingPathingPixelMap(War3ID rawcode);
void spawnUnitConstructionFinishSound(CUnit constructedStructure);
void spawnBuildingDeathEffect(CUnit cUnit);
void spawnUnitReadySound(CUnit trainedUnit);
void unitRepositioned(CUnit cUnit);
}

View File

@ -57,6 +57,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.AbilityDataU
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.IconUI;
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.commandbuttons.CommandButtonListener;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit.QueueItemType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitClassification;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitFilterFunction;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitStateListener;
@ -111,11 +112,19 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
private final Rectangle tempRect = new Rectangle();
private final Vector2 projectionTemp1 = new Vector2();
private final Vector2 projectionTemp2 = new Vector2();
private UIFrame simpleInfoPanelUnitDetail;
private StringFrame simpleNameValue;
private StringFrame simpleClassValue;
private StringFrame simpleBuildingActionLabel;
private SimpleStatusBarFrame simpleBuildTimeIndicator;
private UIFrame simpleInfoPanelBuildingDetail;
private StringFrame simpleBuildingNameValue;
private StringFrame simpleBuildingDescriptionValue;
private StringFrame simpleBuildingBuildingActionLabel;
private SimpleStatusBarFrame simpleBuildingBuildTimeIndicator;
private UIFrame attack1Icon;
private TextureFrame attack1IconBackdrop;
private StringFrame attack1InfoPanelIconValue;
@ -282,12 +291,15 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
this.unitLifeText = (StringFrame) this.rootFrame.getFrameByName("UnitPortraitHitPointText", 0);
this.unitManaText = (StringFrame) this.rootFrame.getFrameByName("UnitPortraitManaPointText", 0);
// Create Simple Info Unit Detail
this.simpleInfoPanelUnitDetail = this.rootFrame.createSimpleFrame("SimpleInfoPanelUnitDetail", this.consoleUI,
0);
this.simpleInfoPanelUnitDetail
.addAnchor(new AnchorDefinition(FramePoint.BOTTOM, 0, GameUI.convertY(this.uiViewport, 0.0f)));
this.simpleInfoPanelUnitDetail.setWidth(GameUI.convertY(this.uiViewport, 0.180f));
this.simpleInfoPanelUnitDetail.setHeight(GameUI.convertY(this.uiViewport, 0.105f));
final float infoPanelUnitDetailWidth = GameUI.convertY(this.uiViewport, 0.180f);
this.simpleInfoPanelUnitDetail.setWidth(infoPanelUnitDetailWidth);
final float infoPanelUnitDetailHeight = GameUI.convertY(this.uiViewport, 0.105f);
this.simpleInfoPanelUnitDetail.setHeight(infoPanelUnitDetailHeight);
this.simpleNameValue = (StringFrame) this.rootFrame.getFrameByName("SimpleNameValue", 0);
this.simpleClassValue = (StringFrame) this.rootFrame.getFrameByName("SimpleClassValue", 0);
this.simpleBuildingActionLabel = (StringFrame) this.rootFrame.getFrameByName("SimpleBuildingActionLabel", 0);
@ -297,8 +309,33 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
simpleBuildTimeIndicatorBar.setTexture("SimpleBuildTimeIndicator", this.rootFrame);
final TextureFrame simpleBuildTimeIndicatorBorder = this.simpleBuildTimeIndicator.getBorderFrame();
simpleBuildTimeIndicatorBorder.setTexture("SimpleBuildTimeIndicatorBorder", this.rootFrame);
this.simpleBuildTimeIndicator.setWidth(GameUI.convertX(this.uiViewport, 0.10538f));
this.simpleBuildTimeIndicator.setHeight(GameUI.convertY(this.uiViewport, 0.0103f));
final float buildTimeIndicatorWidth = GameUI.convertX(this.uiViewport, 0.10538f);
final float buildTimeIndicatorHeight = GameUI.convertY(this.uiViewport, 0.0103f);
this.simpleBuildTimeIndicator.setWidth(buildTimeIndicatorWidth);
this.simpleBuildTimeIndicator.setHeight(buildTimeIndicatorHeight);
// Create Simple Info Panel Building Detail
this.simpleInfoPanelBuildingDetail = this.rootFrame.createSimpleFrame("SimpleInfoPanelBuildingDetail",
this.consoleUI, 0);
this.simpleInfoPanelBuildingDetail
.addAnchor(new AnchorDefinition(FramePoint.BOTTOM, 0, GameUI.convertY(this.uiViewport, 0.0f)));
this.simpleInfoPanelBuildingDetail.setWidth(infoPanelUnitDetailWidth);
this.simpleInfoPanelBuildingDetail.setHeight(infoPanelUnitDetailHeight);
this.simpleBuildingNameValue = (StringFrame) this.rootFrame.getFrameByName("SimpleBuildingNameValue", 0);
this.simpleBuildingDescriptionValue = (StringFrame) this.rootFrame
.getFrameByName("SimpleBuildingDescriptionValue", 0);
this.simpleBuildingBuildingActionLabel = (StringFrame) this.rootFrame
.getFrameByName("SimpleBuildingActionLabel", 0);
this.simpleBuildingBuildTimeIndicator = (SimpleStatusBarFrame) this.rootFrame
.getFrameByName("SimpleBuildTimeIndicator", 0);
final TextureFrame simpleBuildingBuildTimeIndicatorBar = this.simpleBuildingBuildTimeIndicator.getBarFrame();
simpleBuildingBuildTimeIndicatorBar.setTexture("SimpleBuildTimeIndicator", this.rootFrame);
final TextureFrame simpleBuildingBuildTimeIndicatorBorder = this.simpleBuildingBuildTimeIndicator
.getBorderFrame();
simpleBuildingBuildTimeIndicatorBorder.setTexture("SimpleBuildTimeIndicatorBorder", this.rootFrame);
this.simpleBuildingBuildTimeIndicator.setWidth(buildTimeIndicatorWidth);
this.simpleBuildingBuildTimeIndicator.setHeight(buildTimeIndicatorHeight);
this.simpleInfoPanelBuildingDetail.setVisible(false);
this.attack1Icon = this.rootFrame.createSimpleFrame("SimpleInfoPanelIconDamage", this.simpleInfoPanelUnitDetail,
0);
@ -494,7 +531,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
this.war3MapViewer.mapPathSolver, this.war3MapViewer.solverParams);
this.cursorModelInstance = (MdxComplexInstance) model.addInstance();
this.cursorModelInstance.setVertexColor(new float[] { 1, 1, 1, 0.5f });
this.cursorModelInstance.setTeamColor(activeCommandUnit.getSimulationUnit().getPlayerIndex());
this.cursorModelInstance.setTeamColor(this.activeCommandUnit.getSimulationUnit().getPlayerIndex());
this.cursorModelInstance.rotate(RenderUnit.tempQuat.setFromAxis(RenderMathUtils.VEC3_UNIT_Z,
this.war3MapViewer.simulation.getGameplayConstants().getBuildingAngle()));
this.cursorModelInstance.setAnimationSpeed(0f);
@ -570,6 +607,14 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
.setValue(Math.min(this.selectedUnit.getSimulationUnit().getConstructionProgress()
/ this.selectedUnit.getSimulationUnit().getUnitType().getBuildTime(), 0.99f));
}
if (this.simpleBuildingBuildTimeIndicator.isVisible() && (this.selectedUnit != null)) {
this.simpleBuildingBuildTimeIndicator
.setValue(Math.min(
this.selectedUnit.getSimulationUnit().getConstructionProgress() / this.selectedUnit
.getSimulationUnit().getBuildQueueTimeRemaining(this.war3MapViewer.simulation),
0.99f));
}
final float groundHeight = Math.max(
this.war3MapViewer.terrain.getGroundHeight(this.cameraManager.target.x, this.cameraManager.target.y),
this.war3MapViewer.terrain.getWaterHeight(this.cameraManager.target.x, this.cameraManager.target.y));
@ -687,83 +732,110 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
}
private void reloadSelectedUnitUI(final RenderUnit unit) {
this.simpleNameValue.setText(unit.getSimulationUnit().getUnitType().getName());
String classText = null;
for (final CUnitClassification classification : unit.getSimulationUnit().getClassifications()) {
if ((classification == CUnitClassification.MECHANICAL)
&& unit.getSimulationUnit().getUnitType().isBuilding()) {
// buildings dont display MECHANICAL
continue;
}
if (classification.getDisplayName() != null) {
classText = classification.getDisplayName();
}
}
if (classText != null) {
this.simpleClassValue.setText(classText);
}
else {
this.simpleClassValue.setText("");
}
this.unitLifeText.setText(FastNumberFormat.formatWholeNumber(unit.getSimulationUnit().getLife()) + " / "
+ unit.getSimulationUnit().getMaximumLife());
final int maximumMana = unit.getSimulationUnit().getMaximumMana();
final CUnit simulationUnit = unit.getSimulationUnit();
this.unitLifeText.setText(
FastNumberFormat.formatWholeNumber(simulationUnit.getLife()) + " / " + simulationUnit.getMaximumLife());
final int maximumMana = simulationUnit.getMaximumMana();
if (maximumMana > 0) {
this.unitManaText.setText(
FastNumberFormat.formatWholeNumber(unit.getSimulationUnit().getMana()) + " / " + maximumMana);
this.unitManaText
.setText(FastNumberFormat.formatWholeNumber(simulationUnit.getMana()) + " / " + maximumMana);
}
else {
this.unitManaText.setText("");
}
this.simpleBuildingActionLabel.setText("");
if (simulationUnit.getBuildQueue()[0] != null) {
this.simpleInfoPanelBuildingDetail.setVisible(true);
this.simpleInfoPanelUnitDetail.setVisible(false);
this.simpleBuildingNameValue.setText(simulationUnit.getUnitType().getName());
this.simpleBuildingDescriptionValue.setText("");
final boolean anyAttacks = unit.getSimulationUnit().getUnitType().getAttacks().size() > 0;
final boolean constructing = unit.getSimulationUnit().isConstructing();
final UIFrame localArmorIcon = this.armorIcon;
final TextureFrame localArmorIconBackdrop = this.armorIconBackdrop;
final StringFrame localArmorInfoPanelIconValue = this.armorInfoPanelIconValue;
if (anyAttacks && !constructing) {
final CUnitAttack attackOne = unit.getSimulationUnit().getUnitType().getAttacks().get(0);
this.attack1Icon.setVisible(attackOne.isShowUI());
this.attack1IconBackdrop.setTexture(this.damageBackdrops.getTexture(attackOne.getAttackType()));
this.attack1InfoPanelIconValue.setText(attackOne.getMinDamage() + " - " + attackOne.getMaxDamage());
if (unit.getSimulationUnit().getUnitType().getAttacks().size() > 1) {
final CUnitAttack attackTwo = unit.getSimulationUnit().getUnitType().getAttacks().get(1);
this.attack2Icon.setVisible(attackTwo.isShowUI());
this.attack2IconBackdrop.setTexture(this.damageBackdrops.getTexture(attackTwo.getAttackType()));
this.attack2InfoPanelIconValue.setText(attackTwo.getMinDamage() + " - " + attackTwo.getMaxDamage());
this.simpleBuildingBuildTimeIndicator.setVisible(true);
this.simpleBuildTimeIndicator.setVisible(false);
if (simulationUnit.getBuildQueueTypes()[0] == QueueItemType.UNIT) {
this.simpleBuildingBuildingActionLabel
.setText(this.rootFrame.getTemplates().getDecoratedString("TRAINING"));
}
else {
this.attack2Icon.setVisible(false);
this.simpleBuildingBuildingActionLabel
.setText(this.rootFrame.getTemplates().getDecoratedString("RESEARCHING"));
}
this.armorIcon
.addSetPoint(new SetPoint(FramePoint.TOPLEFT, this.simpleInfoPanelUnitDetail, FramePoint.TOPLEFT,
GameUI.convertX(this.uiViewport, 0f), GameUI.convertY(this.uiViewport, -0.06025f)));
this.armorIcon.positionBounds(this.uiViewport);
}
else {
this.attack1Icon.setVisible(false);
this.attack2Icon.setVisible(false);
this.armorIcon.setVisible(false);
}
else {
this.simpleInfoPanelBuildingDetail.setVisible(false);
this.simpleInfoPanelUnitDetail.setVisible(true);
this.simpleNameValue.setText(simulationUnit.getUnitType().getName());
String classText = null;
for (final CUnitClassification classification : simulationUnit.getClassifications()) {
if ((classification == CUnitClassification.MECHANICAL) && simulationUnit.getUnitType().isBuilding()) {
// buildings dont display MECHANICAL
continue;
}
if (classification.getDisplayName() != null) {
classText = classification.getDisplayName();
}
}
if (classText != null) {
this.simpleClassValue.setText(classText);
}
else {
this.simpleClassValue.setText("");
}
this.armorIcon.addSetPoint(new SetPoint(FramePoint.TOPLEFT, this.simpleInfoPanelUnitDetail,
FramePoint.TOPLEFT, 0, GameUI.convertY(this.uiViewport, -0.030125f)));
this.armorIcon.positionBounds(this.uiViewport);
}
final boolean anyAttacks = simulationUnit.getUnitType().getAttacks().size() > 0;
final boolean constructing = simulationUnit.isConstructing();
final UIFrame localArmorIcon = this.armorIcon;
final TextureFrame localArmorIconBackdrop = this.armorIconBackdrop;
final StringFrame localArmorInfoPanelIconValue = this.armorInfoPanelIconValue;
if (anyAttacks && !constructing) {
final CUnitAttack attackOne = simulationUnit.getUnitType().getAttacks().get(0);
this.attack1Icon.setVisible(attackOne.isShowUI());
this.attack1IconBackdrop.setTexture(this.damageBackdrops.getTexture(attackOne.getAttackType()));
this.attack1InfoPanelIconValue.setText(attackOne.getMinDamage() + " - " + attackOne.getMaxDamage());
if (simulationUnit.getUnitType().getAttacks().size() > 1) {
final CUnitAttack attackTwo = simulationUnit.getUnitType().getAttacks().get(1);
this.attack2Icon.setVisible(attackTwo.isShowUI());
this.attack2IconBackdrop.setTexture(this.damageBackdrops.getTexture(attackTwo.getAttackType()));
this.attack2InfoPanelIconValue.setText(attackTwo.getMinDamage() + " - " + attackTwo.getMaxDamage());
}
else {
this.attack2Icon.setVisible(false);
}
localArmorIcon.setVisible(!constructing);
this.simpleBuildTimeIndicator.setVisible(constructing);
if (constructing) {
this.simpleBuildingActionLabel.setText(this.rootFrame.getTemplates().getDecoratedString("CONSTRUCTING"));
this.armorIcon.addSetPoint(
new SetPoint(FramePoint.TOPLEFT, this.simpleInfoPanelUnitDetail, FramePoint.TOPLEFT,
GameUI.convertX(this.uiViewport, 0f), GameUI.convertY(this.uiViewport, -0.06025f)));
this.armorIcon.positionBounds(this.uiViewport);
}
else {
this.attack1Icon.setVisible(false);
this.attack2Icon.setVisible(false);
this.armorIcon.addSetPoint(new SetPoint(FramePoint.TOPLEFT, this.simpleInfoPanelUnitDetail,
FramePoint.TOPLEFT, 0, GameUI.convertY(this.uiViewport, -0.030125f)));
this.armorIcon.positionBounds(this.uiViewport);
}
localArmorIcon.setVisible(!constructing);
this.simpleBuildTimeIndicator.setVisible(constructing);
this.simpleBuildingBuildTimeIndicator.setVisible(false);
if (constructing) {
this.simpleBuildingActionLabel
.setText(this.rootFrame.getTemplates().getDecoratedString("CONSTRUCTING"));
}
else {
this.simpleBuildingActionLabel.setText("");
}
final Texture defenseTexture = this.defenseBackdrops
.getTexture(simulationUnit.getUnitType().getDefenseType());
if (defenseTexture == null) {
throw new RuntimeException(simulationUnit.getUnitType().getDefenseType() + " can't find texture!");
}
localArmorIconBackdrop.setTexture(defenseTexture);
localArmorInfoPanelIconValue.setText(Integer.toString(simulationUnit.getDefense()));
}
final Texture defenseTexture = this.defenseBackdrops
.getTexture(unit.getSimulationUnit().getUnitType().getDefenseType());
if (defenseTexture == null) {
throw new RuntimeException(
unit.getSimulationUnit().getUnitType().getDefenseType() + " can't find texture!");
}
localArmorIconBackdrop.setTexture(defenseTexture);
localArmorInfoPanelIconValue.setText(Integer.toString(unit.getSimulationUnit().getDefense()));
clearAndRepopulateCommandCard();
}
@ -779,8 +851,15 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
public void commandButton(final int buttonPositionX, final int buttonPositionY, final Texture icon,
final int abilityHandleId, final int orderId, final int autoCastId, final boolean active,
final boolean autoCastActive, final boolean menuButton) {
final int x = Math.max(0, Math.min(COMMAND_CARD_WIDTH - 1, buttonPositionX));
final int y = Math.max(0, Math.min(COMMAND_CARD_HEIGHT - 1, buttonPositionY));
int x = Math.max(0, Math.min(COMMAND_CARD_WIDTH - 1, buttonPositionX));
int y = Math.max(0, Math.min(COMMAND_CARD_HEIGHT - 1, buttonPositionY));
while (this.commandCard[y][x].isVisible() && (y < COMMAND_CARD_HEIGHT) && (x < COMMAND_CARD_WIDTH)) {
x++;
if (x >= COMMAND_CARD_WIDTH) {
x = 0;
y++;
}
}
this.commandCard[y][x].setCommandButtonData(icon, abilityHandleId, orderId, autoCastId, active, autoCastActive,
menuButton);
}
@ -877,6 +956,11 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
reloadSelectedUnitUI(this.selectedUnit);
}
@Override
public void queueChanged() {
reloadSelectedUnitUI(this.selectedUnit);
}
private void clearAndRepopulateCommandCard() {
clearCommandCard();
final AbilityDataUI abilityDataUI = this.war3MapViewer.getAbilityDataUI();
@ -885,9 +969,6 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
final IconUI cancelUI = abilityDataUI.getCancelUI();
this.commandButton(cancelUI.getButtonPositionX(), cancelUI.getButtonPositionY(), cancelUI.getIcon(), 0,
menuOrderId, 0, false, false, true);
}
else if (this.selectedUnit.getSimulationUnit().isConstructing()) {
}
else {
if (menuOrderId != 0) {