package com.galvarez.ttw.rendering; import static com.badlogic.gdx.graphics.g2d.Batch.C1; import static com.badlogic.gdx.graphics.g2d.Batch.C2; import static com.badlogic.gdx.graphics.g2d.Batch.C3; import static com.badlogic.gdx.graphics.g2d.Batch.C4; import static com.badlogic.gdx.graphics.g2d.Batch.U1; import static com.badlogic.gdx.graphics.g2d.Batch.U2; import static com.badlogic.gdx.graphics.g2d.Batch.U3; import static com.badlogic.gdx.graphics.g2d.Batch.U4; import static com.badlogic.gdx.graphics.g2d.Batch.V1; import static com.badlogic.gdx.graphics.g2d.Batch.V2; import static com.badlogic.gdx.graphics.g2d.Batch.V3; import static com.badlogic.gdx.graphics.g2d.Batch.V4; import static com.badlogic.gdx.graphics.g2d.Batch.X1; import static com.badlogic.gdx.graphics.g2d.Batch.X2; import static com.badlogic.gdx.graphics.g2d.Batch.X3; import static com.badlogic.gdx.graphics.g2d.Batch.X4; import static com.badlogic.gdx.graphics.g2d.Batch.Y1; import static com.badlogic.gdx.graphics.g2d.Batch.Y2; import static com.badlogic.gdx.graphics.g2d.Batch.Y3; import static com.badlogic.gdx.graphics.g2d.Batch.Y4; import java.util.ArrayList; import java.util.List; import com.artemis.ComponentMapper; import com.artemis.Entity; import com.artemis.annotations.Wire; import com.artemis.utils.ImmutableBag; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.Texture.TextureWrap; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.math.Vector2; import com.galvarez.ttw.model.DiplomaticSystem.State; import com.galvarez.ttw.model.components.Diplomacy; import com.galvarez.ttw.model.data.Empire; import com.galvarez.ttw.model.map.MapPosition; import com.galvarez.ttw.model.map.MapTools; import com.galvarez.ttw.utils.FloatPair; /** * Displays arrows from vassals to overlords. * * @author Guillaume Alvarez */ @Wire public final class DiplomacyRenderSystem extends AbstractRendererSystem { /** Arrow height in pixels at standard zoom. */ private static final int ARROW_HEIGHT = 4; /** Do not mask terrain with arrow. */ private static final float ALPHA = 0.5f; private ComponentMapper<Empire> empires; private ComponentMapper<Diplomacy> relations; private ComponentMapper<MapPosition> positions; private final float textureRatio; private final Texture arrow; public DiplomacyRenderSystem(OrthographicCamera camera, SpriteBatch batch) { super(with(Diplomacy.class), camera, batch); arrow = new Texture("textures/arrow.png"); arrow.setWrap(TextureWrap.Repeat, TextureWrap.ClampToEdge); textureRatio = arrow.getHeight() / ARROW_HEIGHT; } @Override protected boolean checkProcessing() { return true; } static final class Link { final MapPosition start; final MapPosition end; final Color color; Link(MapPosition start, MapPosition end, Color color) { this.start = start; this.end = end; this.color = color; } } private Link createLink(Entity start, Entity end) { return new Link(positions.get(start), positions.get(end), empires.get(end).color); } private final List<Link> links = new ArrayList<>(); /** * Collect the borders for every influence source. Should be done only when * the borders changed, meaning after processing a turn. */ public void preprocess() { links.clear(); ImmutableBag<Entity> players = getActives(); for (int i = 0; i < players.size(); i++) { Entity e1 = players.get(i); Diplomacy r1 = relations.get(e1); for (int j = i + 1; j < players.size(); j++) { Entity e2 = players.get(j); Diplomacy r2 = relations.get(e2); if (r1.getRelationWith(e2) == State.TRIBUTE) links.add(createLink(e1, e2)); else if (r2.getRelationWith(e1) == State.TRIBUTE) links.add(createLink(e2, e1)); } } } @Override protected void processEntities(ImmutableBag<Entity> entities) { Color c = batch.getColor(); // draw all the pre-processed links for (Link l : links) { // use source color batch.setColor(l.color); FloatPair start = MapTools.world2window(l.start); FloatPair end = MapTools.world2window(l.end); batch.draw(arrow, vertices(start, end, l.color), 0, 20); } // revert to previous (may be it is the last source?) batch.setColor(c); } private static final Vector2 tmp = new Vector2(); /** * Grossly inspired by * {@link ShapeRenderer#rectLine(float, float, float, float, float)} and * {@link SpriteBatch#draw(Texture, float, float, float, float)}. */ private float[] vertices(FloatPair start, FloatPair end, Color color) { // http://stackoverflow.com/questions/11954964/libgdx-spritebatch-draw-specifying-4-vertices // https://gist.github.com/mattdesl/4255476 float c = Color.toFloatBits(color.r, color.g, color.b, ALPHA); float x1 = start.x; float y1 = start.y; float x2 = end.x; float y2 = end.y; tmp.set(y2 - y1, x1 - x2); float length = tmp.len(); Vector2 t = tmp.nor(); float tx = t.x * ARROW_HEIGHT; float ty = t.y * ARROW_HEIGHT; final float u1 = 0; final float v1 = 1; // number of times it is repeated, have it a bit longer than high final float u2 = (length / arrow.getWidth()) * textureRatio / 2; final float v2 = 0; float[] vertices = new float[20]; vertices[X1] = x1 + tx; vertices[Y1] = y1 + ty; vertices[C1] = c; vertices[U1] = u1; vertices[V1] = v1; vertices[X2] = x1 - tx; vertices[Y2] = y1 - ty; vertices[C2] = c; vertices[U2] = u1; vertices[V2] = v2; vertices[X4] = x2 + tx; vertices[Y4] = y2 + ty; vertices[C4] = c; vertices[U4] = u2; vertices[V4] = v1; vertices[X3] = x2 - tx; vertices[Y3] = y2 - ty; vertices[C3] = c; vertices[U3] = u2; vertices[V3] = v2; return vertices; } }