/******************************************************************************* * Copyright (c) 2015 * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. *******************************************************************************/ package jsettlers.graphics.map.draw; import java.util.ConcurrentModificationException; import go.graphics.GLDrawContext; import jsettlers.common.Color; import jsettlers.common.CommonConstants; import jsettlers.common.buildings.EBuildingType; import jsettlers.common.buildings.IBuilding; import jsettlers.common.buildings.IBuilding.IOccupied; import jsettlers.common.buildings.IBuildingOccupier; import jsettlers.common.buildings.OccupierPlace; import jsettlers.common.images.AnimationSequence; import jsettlers.common.images.EImageLinkType; import jsettlers.common.images.ImageLink; import jsettlers.common.images.OriginalImageLink; import jsettlers.common.mapobject.EMapObjectType; import jsettlers.common.mapobject.IArrowMapObject; import jsettlers.common.mapobject.IAttackableTowerMapObject; import jsettlers.common.mapobject.IMapObject; import jsettlers.common.mapobject.IStackMapObject; import jsettlers.common.material.EMaterialType; import jsettlers.common.movable.EMovableAction; import jsettlers.common.movable.EMovableType; import jsettlers.common.movable.ESoldierClass; import jsettlers.common.movable.IMovable; import jsettlers.common.player.IPlayerable; import jsettlers.common.position.ShortPoint2D; import jsettlers.common.sound.ISoundable; import jsettlers.graphics.image.Image; import jsettlers.graphics.image.SingleImage; import jsettlers.graphics.map.MapDrawContext; import jsettlers.graphics.map.draw.settlerimages.SettlerImageMap; import jsettlers.graphics.map.geometry.MapCoordinateConverter; import jsettlers.graphics.sequence.Sequence; import jsettlers.graphics.sound.SoundManager; /** * This class handles drawing of objects on the map. * * @author michael */ public class MapObjectDrawer { private static final int SOUND_MILL = 42; private static final int SOUND_BUILDING_DESTROYED = 93; private static final int SOUND_SETTLER_KILLED = 35; private static final int SOUND_FALLING_TREE = 36; private static final OriginalImageLink INSIDE_BUILDING_RIGHT = new OriginalImageLink(EImageLinkType.SETTLER, 12, 28, 1); private static final OriginalImageLink INSIDE_BUILDING_LEFT = new OriginalImageLink(EImageLinkType.SETTLER, 12, 28, 0); private static final int OBJECTS_FILE = 1; private static final int BUILDINGS_FILE = 13; private static final int TREE_TYPES = 7; private static final int[] TREE_SEQUENCES = new int[] { 1, 2, 4, 7, 8, 16, 17 }; private static final int[] TREE_CHANGING_SEQUENCES = new int[] { 3, 3, 6, 9, 9, 18, 18 }; private static final float TREE_CUT_1 = 0.03F; private static final float TREE_CUT_2 = 0.06F; private static final float TREE_CUT_3 = 0.09F; private static final float TREE_TAKEN = 0.1F; /** * First images in tree cutting sequence. */ private static final int TREE_FALL_IMAGES = 4; /** * Tree falling speed. bigger => faster. */ private static final float TREE_FALLING_SPEED = 1 / 0.001f; /** * */ private static final int TREE_ROT_IMAGES = 4; /** * */ private static final int TREE_SMALL = 12; /** * */ private static final int TREE_MEDIUM = 11; private static final int SMALL_GROWING_TREE = 22; private static final int CORN = 23; private static final int CORN_GROW_STEPS = 7; private static final int CORN_DEAD_STEP = 8; private static final int WINE = 25; private static final int WINE_GROW_STEPS = 3; private static final int WINE_DEAD_STEP = 0; private static final int WINE_BOWL_SEQUENCE = 46; private static final int WINE_BOWL_IMAGES = 9; private static final int WAVES = 26; private static final int FILE_BORDERPOST = 13; private static final int STONE = 31; private static final int SELECTMARK_SEQUENCE = 11; private static final int SELECTMARK_FILE = 4; private static final int MILL_FILE = 13; private static final int MILL_SEQ = 15; private static final int PIG_SEQ = 0; private static final int ANIMALS_FILE = 6; private static final int FISH_SEQ = 7; private static final AnimationSequence TREE_TEST_SEQUENCE = new AnimationSequence("tree_test", 0, 5); private static final int MOVE_TO_MARKER_SEQUENCE = 0; private static final int MARKER_FILE = 3; private static final float CONSTRUCTION_MARK_Z = 0.92f; private static final float PLACEMENT_BUILDING_Z = 0.91f; private static final float MOVABLE_SELECTION_MARKER_Z = 0.9f; private static final float BUILDING_SELECTION_MARKER_Z = 0.9f; private static final float FLAG_ROOF_Z = 0.89f; /** * An animation counter, used for trees and other waving/animated things. */ private int animationStep = 0; /** * The image provider that supplies us with the images we need. */ private ImageProvider imageProvider; private final SoundManager sound; private final MapDrawContext context; private SettlerImageMap imageMap; private float betweenTilesY; /** * Creates a new {@link MapObjectDrawer}. * * @param context * The context to use for computing the positions. * @param sound * The sound manager to send sounds to play to. */ public MapObjectDrawer(MapDrawContext context, SoundManager sound) { this.context = context; this.sound = sound; } /** * Draws a map object at a given position. * * @param x * THe position to draw the object. * @param y * THe position to draw the object. * @param object * The object (tree, ...) to draw. */ public void drawMapObject(int x, int y, IMapObject object) { forceSetup(); byte fogstatus = context.getVisibleStatus(x, y); if (fogstatus == 0) { return; // break } float color = getColor(fogstatus); drawObject(x, y, object, color); if (object.getNextObject() != null) { drawMapObject(x, y, object.getNextObject()); } } private void drawObject(int x, int y, IMapObject object, float color) { EMapObjectType type = object.getObjectType(); float progress = object.getStateProgress(); switch (type) { case ARROW: drawArrow(context, (IArrowMapObject) object, color); break; case TREE_ADULT: if (context.ENABLE_ORIGINAL) { drawTree(x, y, color); } else { drawTreeTest(x, y, color); } break; case TREE_DEAD: // TODO: falling tree sound. playSound(object, SOUND_FALLING_TREE, x, y); drawFallingTree(x, y, progress, color); break; case TREE_GROWING: drawGrowingTree(x, y, progress, color); break; case CORN_GROWING: drawGrowingCorn(x, y, object, color); break; case CORN_ADULT: drawCorn(x, y, color); break; case CORN_DEAD: drawDeadCorn(x, y, color); break; case WINE_GROWING: drawGrowingWine(x, y, object, color); break; case WINE_HARVESTABLE: drawHarvestableWine(x, y, color); break; case WINE_DEAD: drawDeadWine(x, y, color); break; case WINE_BOWL: drawWineBowl(x, y, object, color); break; case WAVES: drawWaves(x, y, color); break; case STONE: drawStones(x, y, (int) object.getStateProgress(), color); break; case CUT_OFF_STONE: drawStones(x, y, 0, color); break; case GHOST: drawPlayerableByProgress(x, y, 12, 27, object, color); playSound(object, SOUND_SETTLER_KILLED, x, y); break; case BUILDING_DECONSTRUCTION_SMOKE: drawByProgress(x, y, 13, 38, object.getStateProgress(), color); playSound(object, SOUND_BUILDING_DESTROYED, x, y); break; case FOUND_COAL: drawByProgress(x, y, OBJECTS_FILE, 94, object.getStateProgress(), color); break; case FOUND_GEMSTONE: drawByProgress(x, y, OBJECTS_FILE, 95, object.getStateProgress(), color); break; case FOUND_GOLD: drawByProgress(x, y, OBJECTS_FILE, 96, object.getStateProgress(), color); break; case FOUND_IRON: drawByProgress(x, y, OBJECTS_FILE, 97, object.getStateProgress(), color); break; case FOUND_BRIMSTONE: drawByProgress(x, y, OBJECTS_FILE, 98, object.getStateProgress(), color); break; case FOUND_NOTHING: drawByProgress(x, y, OBJECTS_FILE, 99, object.getStateProgress(), color); break; case BUILDINGSITE_SIGN: drawByProgress(x, y, OBJECTS_FILE, 93, object.getStateProgress(), color); break; case BUILDINGSITE_POST: drawByProgress(x, y, OBJECTS_FILE, 92, object.getStateProgress(), color); break; case WORKAREA_MARK: drawByProgress(x, y, OBJECTS_FILE, 91, object.getStateProgress(), color); break; case FLAG_DOOR: drawPlayerableWaving(x, y, 13, 63, object, color); break; case CONSTRUCTION_MARK: drawConstructionMark(x, y, object, color); break; case FLAG_ROOF: drawRoofFlag(x, y, object, color); break; case BUILDING: drawBuilding(x, y, (IBuilding) object, color); break; case PLACEMENT_BUILDING: drawPlacementBuilding(x, y, object, color); break; case STACK_OBJECT: drawStack(x, y, (IStackMapObject) object, color); break; case SMOKE: drawByProgress(x, y, 13, 42, progress, color); break; case PLANT_DECORATION: drawPlantDecoration(x, y, color); break; case DESERT_DECORATION: drawDesertDecoration(x, y, color); break; case PIG: drawPig(x, y, color); break; case DONKEY: drawDonkey(x, y, object, color); break; case FISH_DECORATION: drawDecorativeFish(x, y, color); break; case ATTACKABLE_TOWER: drawAttackableTower(x, y, object); break; default: break; } } private void drawConstructionMark(int x, int y, IMapObject object, float color) { float z = context.getDrawBuffer().getZ(); context.getDrawBuffer().setZ(CONSTRUCTION_MARK_Z); drawByProgress(x, y, 4, 6, object.getStateProgress(), color); context.getDrawBuffer().setZ(z); } private void drawRoofFlag(int x, int y, IMapObject object, float color) { float z = context.getDrawBuffer().getZ(); context.getDrawBuffer().setZ(FLAG_ROOF_Z); drawPlayerableWaving(x, y, 13, 64, object, color); context.getDrawBuffer().setZ(z); } private void drawPlacementBuilding(int x, int y, IMapObject object, float color) { float z = context.getDrawBuffer().getZ(); context.getDrawBuffer().setZ(PLACEMENT_BUILDING_Z); drawBuilding(x, y, (IBuilding) object, color); context.getDrawBuffer().setZ(z); } private void drawPlantDecoration(int x, int y, float color) { int step = (x * 13 + y * 233) % 8; Sequence<? extends Image> seq = this.imageProvider.getSettlerSequence(1, 27); draw(seq.getImageSafe(step), x, y, color); } private void drawDesertDecoration(int x, int y, float color) { int step = (x * 13 + y * 233) % 5 + 10; Sequence<? extends Image> seq = this.imageProvider.getSettlerSequence(1, 27); draw(seq.getImageSafe(step), x, y, color); } private void drawPig(int x, int y, float color) { Sequence<? extends Image> seq = this.imageProvider.getSettlerSequence(ANIMALS_FILE, PIG_SEQ); if (seq.length() > 0) { int i = getAnimationStep(x, y) / 2; int step = i % seq.length(); draw(seq.getImageSafe(step), x, y, color); } } private void drawDonkey(int x, int y, IMapObject object, float color) { int i = (getAnimationStep(x, y) / 20) % 6; Image image = imageProvider.getImage(new OriginalImageLink(EImageLinkType.SETTLER, 6, 17, 72 + i)); draw(image, x, y, getColor(object), color); } private void drawDecorativeFish(int x, int y, float color) { int step = getAnimationStep(x, y); Sequence<? extends Image> seq = this.imageProvider.getSettlerSequence(ANIMALS_FILE, FISH_SEQ); int substep = step % 1024; if (substep < 15) { int subseq = (step / 1024) % 4; draw(seq.getImageSafe(subseq * 15 + substep), x, y, color); } } private void drawAttackableTower(int x, int y, IMapObject object) { IMovable movable = ((IAttackableTowerMapObject) object).getMovable(); if (movable != null) { drawMovableAt(movable, x, y); playMovableSound(movable); } } private void forceSetup() { if (imageProvider == null) { imageProvider = ImageProvider.getInstance(); imageMap = SettlerImageMap.getInstance(); } } /** * Draws any type of movable. * * @param movable * The movable. */ public void draw(IMovable movable) { forceSetup(); final ShortPoint2D pos = movable.getPos(); drawMovableAt(movable, pos.x, pos.y); playMovableSound(movable); } private void playMovableSound(IMovable movable) { if (!movable.isSoundPlayed()) { final EMovableAction action = movable.getAction(); if (action == EMovableAction.ACTION1) { playSoundAction1(movable.getMovableType(), movable.getPos()); movable.setSoundPlayed(); } else if (action == EMovableAction.ACTION2) { playSoundAction2(movable.getMovableType(), movable.getPos()); movable.setSoundPlayed(); } } } private void playSoundAction1(EMovableType type, ShortPoint2D position) { switch (type) { case BRICKLAYER: sound.playSound(1, 1, position); break; case LUMBERJACK: sound.playSound(0, 1, position); break; case SAWMILLER: sound.playSound(5, 1, position); break; case STONECUTTER: sound.playSound(3, 1, position); break; case DIGGER: sound.playSound(2, 1, position); break; case SMITH: sound.playSound(6, 1, position); break; case FARMER: sound.playSound(12, 1, position); break; case SWORDSMAN_L1: case SWORDSMAN_L2: case SWORDSMAN_L3: sound.playSound(30, 1, position); break; case BOWMAN_L1: case BOWMAN_L2: case BOWMAN_L3: sound.playSound(33, 1, position); break; case CHARCOAL_BURNER: sound.playSound(45, 1, position); break; } } private void playSoundAction2(EMovableType type, ShortPoint2D position) { switch (type) { case LUMBERJACK: sound.playSound(36, 1, position); break; } } private void drawMovableAt(IMovable movable, int x, int y) { byte fogstatus = context.getVisibleStatus(x, y); if (fogstatus <= CommonConstants.FOG_OF_WAR_EXPLORED) { return; // break } final float moveProgress = movable.getMoveProgress(); final Image image = this.imageMap.getImageForSettler(movable, moveProgress); Color color = context.getPlayerColor(movable.getPlayerId()); float shade = MapObjectDrawer.getColor(fogstatus); float viewX; float viewY; if (movable.getAction() == EMovableAction.WALKING) { int originx = x - movable.getDirection().getGridDeltaX(); int originy = y - movable.getDirection().getGridDeltaY(); viewX = betweenTilesX(originx, originy, x, y, moveProgress); viewY = betweenTilesY; } else { int height = context.getHeight(x, y); viewX = context.getConverter().getViewX(x, y, height); viewY = context.getConverter().getViewY(x, y, height); } image.drawAt(context.getGl(), context.getDrawBuffer(), viewX, viewY, color, shade); if (movable.isSelected()) { drawSelectionMark(viewX, viewY, movable.getHealth() / movable.getMovableType().getHealth()); } } private float betweenTilesX(int startx, int starty, int destinationx, int destinationy, float progress) { float theight = context.getHeight(startx, starty); float dheight = context.getHeight(destinationx, destinationy); MapCoordinateConverter converter = context.getConverter(); float x = (1 - progress) * converter.getViewX(startx, starty, theight) + progress * converter.getViewX(destinationx, destinationy, dheight); betweenTilesY = (1 - progress) * converter.getViewY(startx, starty, theight) + progress * converter.getViewY(destinationx, destinationy, dheight); return x; } private void drawSelectionMark(float viewX, float viewY, float healthPercentage) { float z = context.getDrawBuffer().getZ(); context.getDrawBuffer().setZ(MOVABLE_SELECTION_MARKER_Z); Image image = ImageProvider.getInstance().getSettlerSequence(4, 7) .getImageSafe(0); image.drawAt(context.getGl(), context.getDrawBuffer(), viewX, viewY + 20, -1); Sequence<? extends Image> sequence = ImageProvider.getInstance().getSettlerSequence(4, 6); int healthId = Math.min((int) ((1 - healthPercentage) * sequence.length()), sequence.length() - 1); Image healthImage = sequence.getImageSafe(healthId); healthImage.drawAt(context.getGl(), context.getDrawBuffer(), viewX, viewY + 38, -1); context.getDrawBuffer().setZ(z); } private void playSound(IMapObject object, int soundid, int x, int y) { if (object instanceof ISoundable) { ISoundable soundable = (ISoundable) object; if (!soundable.isSoundPlayed()) { sound.playSound(soundid, 1, x, y); soundable.setSoundPlayed(); } } } private void drawArrow(MapDrawContext context, IArrowMapObject object, float color) { int sequence = 0; switch (object.getDirection()) { case SOUTH_WEST: sequence = 100; break; case WEST: sequence = 101; break; case NORTH_WEST: sequence = 102; break; case NORTH_EAST: sequence = 103; break; case EAST: sequence = 104; break; case SOUTH_EAST: sequence = 104; break; } float progress = object.getStateProgress(); int index = Math.round(progress * 2); float x = betweenTilesX(object.getSourceX(), object.getSourceY(), object.getTargetX(), object.getTargetY(), progress); int iColor = Color.getABGR(color, color, color, 1); boolean onGround = progress >= 1; float z = 0; if (onGround) { z = context.getDrawBuffer().getZ(); context.getDrawBuffer().setZ(-.1f); iColor &= 0x7fffffff; } Image image = this.imageProvider.getSettlerSequence(OBJECTS_FILE, sequence) .getImageSafe(index); image.drawAt(context.getGl(), context.getDrawBuffer(), x, betweenTilesY + 20 * progress * (1 - progress) + 20, iColor); if (onGround) { context.getDrawBuffer().setZ(z); } } private void drawStones(int x, int y, int availableStones, float color) { Sequence<? extends Image> seq = this.imageProvider.getSettlerSequence(OBJECTS_FILE, STONE); int stones = seq.length() - availableStones - 1; draw(seq.getImageSafe(stones), x, y, color); } private void drawWaves(int x, int y, float color) { Sequence<? extends Image> seq = this.imageProvider.getSettlerSequence(OBJECTS_FILE, WAVES); int len = seq.length(); int step = (animationStep / 2 + x / 2 + y / 2) % len; if (step < len) { draw(seq.getImageSafe(step), x, y, color); } } private void drawGrowingCorn(int x, int y, IMapObject object, float color) { Sequence<? extends Image> seq = this.imageProvider.getSettlerSequence(OBJECTS_FILE, CORN); int step = (int) (object.getStateProgress() * CORN_GROW_STEPS); draw(seq.getImageSafe(step), x, y, color); } private void drawCorn(int x, int y, float color) { Sequence<? extends Image> seq = this.imageProvider.getSettlerSequence(OBJECTS_FILE, CORN); int step = CORN_GROW_STEPS; draw(seq.getImageSafe(step), x, y, color); } private void drawDeadCorn(int x, int y, float color) { Sequence<? extends Image> seq = this.imageProvider.getSettlerSequence(OBJECTS_FILE, CORN); draw(seq.getImageSafe(CORN_DEAD_STEP), x, y, color); } private void drawGrowingWine(int x, int y, IMapObject object, float color) { Sequence<? extends Image> seq = this.imageProvider.getSettlerSequence(OBJECTS_FILE, WINE); int step = (int) (object.getStateProgress() * WINE_GROW_STEPS); draw(seq.getImageSafe(step), x, y, color); } private void drawHarvestableWine(int x, int y, float color) { Sequence<? extends Image> seq = this.imageProvider.getSettlerSequence(OBJECTS_FILE, WINE); int step = WINE_GROW_STEPS; draw(seq.getImageSafe(step), x, y, color); } private void drawDeadWine(int x, int y, float color) { Sequence<? extends Image> seq = this.imageProvider.getSettlerSequence(OBJECTS_FILE, WINE); draw(seq.getImageSafe(WINE_DEAD_STEP), x, y, color); } private void drawWineBowl(int x, int y, IMapObject object, float color) { Sequence<? extends Image> seq = this.imageProvider.getSettlerSequence(BUILDINGS_FILE, WINE_BOWL_SEQUENCE); int step = (int) (object.getStateProgress() * (WINE_BOWL_IMAGES - 1)); draw(seq.getImageSafe(step), x, y, color); } private void drawGrowingTree(int x, int y, float progress, float color) { Image image; if (progress < 0.33) { Sequence<? extends Image> seq = this.imageProvider.getSettlerSequence(OBJECTS_FILE, SMALL_GROWING_TREE); image = seq.getImageSafe(0); } else { int treeType = getTreeType(x, y); Sequence<? extends Image> seq = this.imageProvider.getSettlerSequence(OBJECTS_FILE, TREE_CHANGING_SEQUENCES[treeType]); if (progress < 0.66) { image = seq.getImageSafe(TREE_SMALL); } else { image = seq.getImageSafe(TREE_MEDIUM); } } draw(image, x, y, color); } private void drawFallingTree(int x, int y, float progress, float color) { int treeType = getTreeType(x, y); int imageStep = 0; if (progress < TREE_CUT_1) { imageStep = (int) (progress * TREE_FALLING_SPEED); if (imageStep >= TREE_FALL_IMAGES) { imageStep = TREE_FALL_IMAGES - 1; } } else if (progress < TREE_CUT_2) { // cut image 1 imageStep = TREE_FALL_IMAGES; } else if (progress < TREE_CUT_3) { // cut image 2 imageStep = TREE_FALL_IMAGES + 1; } else if (progress < TREE_TAKEN) { // cut image 3 imageStep = TREE_FALL_IMAGES + 2; } else { int relativeStep = (int) ((progress - TREE_TAKEN) / (1 - TREE_TAKEN) * TREE_ROT_IMAGES); imageStep = relativeStep + TREE_FALL_IMAGES + 3; } Sequence<? extends Image> seq = this.imageProvider.getSettlerSequence(OBJECTS_FILE, TREE_CHANGING_SEQUENCES[treeType]); draw(seq.getImageSafe(imageStep), x, y, color); } private void drawTree(int x, int y, float color) { int treeType = getTreeType(x, y); Sequence<? extends Image> seq = this.imageProvider.getSettlerSequence(OBJECTS_FILE, TREE_SEQUENCES[treeType]); int step = getAnimationStep(x, y) % seq.length(); draw(seq.getImageSafe(step), x, y, color); } private void drawTreeTest(int x, int y, float color) { int step = getAnimationStep(x, y) % TREE_TEST_SEQUENCE.getLength(); draw(imageProvider.getImage(TREE_TEST_SEQUENCE.getImage(step)), x, y, color); } /** * gets a 0 or a 1. * * @param pos * @return */ // private static int get01(int x, int y) { // return (x * 677 + y) % 2; // } /** * Draws a player border at a given position. * * @param x * X position * @param y * Y position * @param player * The player. */ public void drawPlayerBorderObject(int x, int y, byte player) { forceSetup(); byte fogstatus = context.getVisibleStatus(x, y); if (fogstatus <= CommonConstants.FOG_OF_WAR_EXPLORED) { return; // break } float base = getColor(fogstatus); Color color = context.getPlayerColor(player); draw(imageProvider.getSettlerSequence(FILE_BORDERPOST, 65) .getImageSafe(0), x, y, color, base); } private static int getTreeType(int x, int y) { return (x + x / 5 + y / 3 + y + y / 7) % TREE_TYPES; } private int getAnimationStep(int x, int y) { return 0xfffffff & (this.animationStep + x * 167 + y * 1223); } /** * Increases the animation step for trees and other stuff. */ public void increaseAnimationStep() { this.animationStep = ((int) System.currentTimeMillis() / 100) & 0x7fffffff; } /** * Draws a stack * * @param context * The context to draw with * @param object * The stack to draw. */ public void drawStack(int x, int y, IStackMapObject object, float color) { forceSetup(); byte elements = object.getSize(); if (elements > 0) { drawStackAtScreen(x, y, object.getMaterialType(), elements, color); } } /** * Draws the stack directly to the screen. * * @param glDrawContext * The gl context to draw at. * @param material * The material the stack should have. * @param count * The number of elements on the stack */ private void drawStackAtScreen(int x, int y, EMaterialType material, int count, float color) { int stackIndex = material.getStackIndex(); Sequence<? extends Image> seq = this.imageProvider.getSettlerSequence(OBJECTS_FILE, stackIndex); draw(seq.getImageSafe(count - 1), x, y, color); } /** * Gets the gray color for a given fog. * * @param fogstatus * @return */ public static float getColor(int fogstatus) { return (float) fogstatus / CommonConstants.FOG_OF_WAR_VISIBLE; } /** * Draws a given buildng to the context. * * @param context * @param building * @param color * Gray color shade */ private void drawBuilding(int x, int y, IBuilding building, float color) { EBuildingType type = building.getBuildingType(); float state = building.getStateProgress(); if (state >= 0.99) { if (type == EBuildingType.MILL && ((IBuilding.IMill) building).isRotating()) { Sequence<? extends Image> seq = this.imageProvider.getSettlerSequence(MILL_FILE, MILL_SEQ); if (seq.length() > 0) { int i = getAnimationStep(x, y); int step = i % seq.length(); draw(seq.getImageSafe(step), x, y, color); } playSound(building, SOUND_MILL, x, y); } else { ImageLink[] images = type.getImages(); if (images.length > 0) { Image image = imageProvider.getImage(images[0]); draw(image, x, y, color, building.getBuildingType() == EBuildingType.MARKET_PLACE); } if (building instanceof IOccupied && context.getVisibleStatus(x, y) > CommonConstants.FOG_OF_WAR_EXPLORED) { drawOccupiers(x, y, (IOccupied) building, color); } for (int i = 1; i < images.length; i++) { Image image = imageProvider.getImage(images[i]); draw(image, x, y, color); } } } else if (state >= .01f) { drawBuildingConstruction(x, y, color, type, state); } if (building.isSelected()) { drawBuildingSelectMarker(x, y); } } private void drawBuildingConstruction(int x, int y, float color, EBuildingType type, float state) { boolean hasTwoConstructionPhases = type.getBuildImages().length > 0; boolean isInBuildPhase = hasTwoConstructionPhases && state < .5f; if (!isInBuildPhase && hasTwoConstructionPhases) { // draw the base build image for (ImageLink link : type.getBuildImages()) { Image image = imageProvider.getImage(link); draw(image, x, y, color); } } ImageLink[] constructionImages = isInBuildPhase ? type.getBuildImages() : type.getImages(); float maskState = hasTwoConstructionPhases ? (state * 2) % 1 : state; for (ImageLink link : constructionImages) { Image image = imageProvider.getImage(link); drawWithConstructionMask(x, y, maskState, image, color); } } /** * Draws the occupiers of a building * * @param x * The x coordinate of the building * @param y * @param building * The occupyed building * @param basecolor * The base color (gray shade). */ private void drawOccupiers(int x, int y, IOccupied building, float basecolor) { // this can cause a ConcurrentModificationException when // a soldier enters the tower! try { int height = context.getHeight(x, y); float towerX = context.getConverter().getViewX(x, y, height); float towerY = context.getConverter().getViewY(x, y, height); GLDrawContext gl = context.getGl(); for (IBuildingOccupier occupyer : building.getOccupiers()) { OccupierPlace place = occupyer.getPlace(); IMovable movable = occupyer.getMovable(); Color color = context.getPlayerColor(movable.getPlayerId()); Image image; switch (place.getSoldierClass()) { case INFANTRY: OriginalImageLink imageLink = place.looksRight() ? INSIDE_BUILDING_RIGHT : INSIDE_BUILDING_LEFT; image = imageProvider.getImage(imageLink); break; case BOWMAN: default: image = this.imageMap.getImageForSettler(movable, movable.getMoveProgress()); } float viewX = towerX + place.getOffsetX(); float viewY = towerY + place.getOffsetY(); image.drawAt(gl, context.getDrawBuffer(), viewX, viewY, color, basecolor); if (place.getSoldierClass() == ESoldierClass.BOWMAN) { playMovableSound(movable); if (movable.isSelected()) { drawSelectionMark(viewX, viewY, movable.getHealth()); } } } } catch (ConcurrentModificationException e) { // happens sometime, just ignore it. } } private void drawBuildingSelectMarker(int x, int y) { float z = context.getDrawBuffer().getZ(); context.getDrawBuffer().setZ(BUILDING_SELECTION_MARKER_Z); Image image = imageProvider.getSettlerSequence(SELECTMARK_FILE, SELECTMARK_SEQUENCE).getImageSafe(0); draw(image, x, y, -1); context.getDrawBuffer().setZ(z); } private void drawWithConstructionMask(int x, int y, float maskState, Image unsafeimage, float color) { if (!(unsafeimage instanceof SingleImage)) { return; // should not happen } int height = context.getHeight(x, y); float viewX = context.getConverter().getViewX(x, y, height); float viewY = context.getConverter().getViewY(x, y, height); int iColor = Color.getABGR(color, color, color, 1); SingleImage image = (SingleImage) unsafeimage; // number of tiles in x direction, can be adjusted for performance int tiles = 6; float toplineBottom = 1 - maskState; float toplineTop = Math.max(0, toplineBottom - .1f); image.drawTriangle(context.getGl(), context.getDrawBuffer(), viewX, viewY, 0, 1, 1, 1, 0, toplineBottom, iColor); image.drawTriangle(context.getGl(), context.getDrawBuffer(), viewX, viewY, 1, 1, 1, toplineBottom, 0, toplineBottom, iColor); for (int i = 0; i < tiles; i++) { image.drawTriangle(context.getGl(), context.getDrawBuffer(), viewX, viewY, 1.0f / tiles * i, toplineBottom, 1.0f / tiles * (i + 1), toplineBottom, 1.0f / tiles * (i + .5f), toplineTop, iColor); } } private void drawPlayerableByProgress(int x, int y, int file, int sequenceIndex, IMapObject object, float basecolor) { Sequence<? extends Image> sequence = this.imageProvider.getSettlerSequence(file, sequenceIndex); int index = Math.min((int) (object.getStateProgress() * sequence.length()), sequence.length() - 1); Color color = getColor(object); draw(sequence.getImage(index), x, y, color, basecolor); } private Color getColor(IMapObject object) { Color color = null; if (object instanceof IPlayerable) { color = context.getPlayerColor(((IPlayerable) object).getPlayerId()); } return color; } private void drawPlayerableWaving(int x, int y, int file, int sequenceIndex, IMapObject object, float basecolor) { Sequence<? extends Image> sequence = this.imageProvider.getSettlerSequence(file, sequenceIndex); int index = animationStep % sequence.length(); Color color = getColor(object); draw(sequence.getImageSafe(index), x, y, color, basecolor); } private void drawByProgress(int x, int y, int file, int sequenceIndex, float progress, float color) { Sequence<? extends Image> sequence = this.imageProvider.getSettlerSequence(file, sequenceIndex); int index = Math.min((int) (progress * sequence.length()), sequence.length() - 1); draw(sequence.getImageSafe(index), x, y, color); } private void draw(Image image, int x, int y, Color color, float basecolor) { int height = context.getHeight(x, y); float viewX = context.getConverter().getViewX(x, y, height); float viewY = context.getConverter().getViewY(x, y, height); image.drawAt(context.getGl(), context.getDrawBuffer(), viewX, viewY, color, basecolor); } private void draw(Image image, int x, int y, float color, boolean background) { float z = 0; if (background) { z = context.getDrawBuffer().getZ(); context.getDrawBuffer().setZ(z - .1f); } draw(image, x, y, color); if (background) { context.getDrawBuffer().setZ(z); } } private void draw(Image image, int x, int y, float color) { int iColor = Color.getABGR(color, color, color, 1); draw(image, x, y, iColor); } private void draw(Image image, int x, int y, int color) { int height = context.getHeight(x, y); float viewX = context.getConverter().getViewX(x, y, height); float viewY = context.getConverter().getViewY(x, y, height); image.drawAt(context.getGl(), context.getDrawBuffer(), viewX, viewY, color); } public void drawMoveToMarker(ShortPoint2D moveToMarker, float progress) { forceSetup(); drawByProgress(moveToMarker.x, moveToMarker.y, MARKER_FILE, MOVE_TO_MARKER_SEQUENCE, progress, 1); } public void drawGotoMarker(ShortPoint2D gotoMarker, Image image) { float z = context.getDrawBuffer().getZ(); context.getDrawBuffer().setZ(FLAG_ROOF_Z); draw(image, gotoMarker.x, gotoMarker.y, 1f); context.getDrawBuffer().setZ(z); } }