/* * Copyright (c) 2012. HappyDroids LLC, All rights reserved. */ package com.happydroids.droidtowers.grid; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Pools; import com.google.common.eventbus.EventBus; import com.happydroids.droidtowers.TowerConsts; import com.happydroids.droidtowers.collections.TypeInstanceMap; import com.happydroids.droidtowers.entities.GameLayer; import com.happydroids.droidtowers.entities.GridObject; import com.happydroids.droidtowers.entities.GridObjectSort; import com.happydroids.droidtowers.events.GameGridResizeEvent; import com.happydroids.droidtowers.events.GridObjectAddedEvent; import com.happydroids.droidtowers.events.GridObjectRemovedEvent; import com.happydroids.droidtowers.events.SafeEventBus; import com.happydroids.droidtowers.math.GridPoint; public class GameGrid extends GameLayer { private EventBus eventBus = new SafeEventBus(GameGrid.class.getSimpleName()); protected float gridScale; protected GridPoint gridSize; private int highestPoint; private GridPoint gridOrigin; private Vector2 worldSize; protected Rectangle worldBounds; protected GameGridRenderer gameGridRenderer; protected GridPositionCache positionCache; private TypeInstanceMap<GridObject> gridObjects; private GridObject selectedGridObject; private String towerName; public GameGrid(OrthographicCamera camera) { this(); gameGridRenderer = new GameGridRenderer(this, camera); } public GameGrid() { setTouchEnabled(true); gridObjects = new TypeInstanceMap<GridObject>(); positionCache = new GridPositionCache(this); gridSize = new GridPoint(8, 8); gridOrigin = new GridPoint(); gridScale = 1f; updateWorldSize(true); } public void updateWorldSize(boolean copyGridPositions) { worldSize = new Vector2(gridSize.x * TowerConsts.GRID_UNIT_SIZE * gridScale, gridSize.y * TowerConsts.GRID_UNIT_SIZE * gridScale); worldBounds = new Rectangle(gridOrigin.x, gridOrigin.y, worldSize.x, worldSize.y); events().post(new GameGridResizeEvent(this, copyGridPositions)); } public void setGridSize(int width, int height) { gridSize.set(width, height); } public void setGridSize(float x, float y) { setGridSize((int) x, (int) y); } public GameGridRenderer getRenderer() { return gameGridRenderer; } public Vector2 getWorldSize() { return worldSize; } public GridPoint getGridSize() { return gridSize; } public boolean addObject(GridObject gridObject) { gridObjects.add(gridObject); int objectYPos = gridObject.getPosition().y; if (objectYPos > highestPoint) { highestPoint = objectYPos; if (highestPoint + TowerConsts.GAME_GRID_EXPAND_LAND_SIZE > gridSize.y) { gridSize.y = highestPoint + TowerConsts.GAME_GRID_EXPAND_LAND_SIZE; updateWorldSize(true); } } GridObjectAddedEvent event = Pools.obtain(GridObjectAddedEvent.class); event.setGridObject(gridObject); events().post(event); // Pools.free(event); gridObjects.getInstances().sort(GridObjectSort.byZIndex); return true; } public Array<GridObject> getObjects() { return gridObjects.getInstances(); } public boolean canObjectBeAt(GridObject gridObject) { if (!gridObject.canBeAt()) { return false; } Rectangle boundsOfGridObjectToCheck = gridObject.getBounds(); Array<GridObject> instances = gridObjects.getInstances(); for (int i = 0, instancesSize = instances.size; i < instancesSize; i++) { GridObject child = instances.get(i); if (child != gridObject) { if (child.getBounds().contains(boundsOfGridObjectToCheck) && !child.canShareSpace(gridObject)) { return false; } } } return true; } public void removeObject(GridObject gridObject) { gridObjects.remove(gridObject); GridObjectRemovedEvent event = Pools.obtain(GridObjectRemovedEvent.class); event.setGridObject(gridObject); events().post(event); Pools.free(event); } public void update(float deltaTime) { super.update(deltaTime); // HACK: have to figure out a better way to clear out previously selected grid objects, until then... if (selectedGridObject != null && !Gdx.input.isTouched()) { selectedGridObject.touchUp(); selectedGridObject = null; } Array<GridObject> instances = gridObjects.getInstances(); for (GridObject gridObject : instances) { gridObject.update(deltaTime); } } public Array<GridObject> getInstancesOf(Class aClass) { return gridObjects.setForType(aClass); } public Array<GridObject> getInstancesOf(Class... classes) { Array<GridObject> found = new Array<GridObject>(); if (classes != null) { for (int i = 0, classesLength = classes.length; i < classesLength; i++) { Class otherClass = classes[i]; found.addAll(getInstancesOf(otherClass)); } } return found; } @Override public boolean tap(Vector2 worldPoint, int count, int button) { GridPoint gridPointAtFinger = closestGridPoint(worldPoint.x, worldPoint.y); Array<GridObject> objectsNear = findObjectsNear(worldPoint, gridPointAtFinger.y, gridPointAtFinger.x); if (objectsNear != null) { for (int i = 0; i < objectsNear.size; i++) { if (objectsNear.get(i).tap(gridPointAtFinger, count)) { return true; } } } return false; } @Override public boolean touchDown(Vector2 worldPoint, int pointer) { GridPoint gridPointAtFinger = closestGridPoint(worldPoint.x, worldPoint.y); Array<GridObject> objectsNear = findObjectsNear(worldPoint, gridPointAtFinger.y, gridPointAtFinger.x); if (objectsNear != null) { for (int i = 0; i < objectsNear.size; i++) { if (objectsNear.get(i).touchDown(gridPointAtFinger, worldPoint, pointer)) { selectedGridObject = objectsNear.get(i); return true; } } } selectedGridObject = null; return false; } @Override public boolean pan(Vector2 worldPoint, Vector2 deltaPoint) { if (selectedGridObject != null) { GridPoint gridPointAtFinger = closestGridPoint(worldPoint.x, worldPoint.y); GridPoint gridPointDelta = closestGridPoint(deltaPoint.x, deltaPoint.y); if (selectedGridObject.pan(gridPointAtFinger, gridPointDelta)) { return true; } } selectedGridObject = null; return false; } private Array<GridObject> findObjectsNear(Vector2 worldPoint, final int y, final int x) { Array<GridObject> objects = new Array<GridObject>(2); for (int x2 = x - 1; x2 < x + 1; x2++) { for (int y2 = y - 1; y2 < y + 1; y2++) { GridPosition position = positionCache.getPosition(x2, y2); if (position != null && !position.isEmpty()) { for (GridObject object : position.getObjects()) { if (object.getWorldBounds().contains(worldPoint.x, worldPoint.y)) { objects.add(object); } } } } } objects.sort(GridObjectSort.byZIndex); objects.reverse(); return objects; } public GridPoint closestGridPoint(float x, float y) { int gridX = (int) Math.floor((int) (x - gridOrigin.x) / TowerConsts.GRID_UNIT_SIZE); int gridY = (int) Math.floor((int) (y - gridOrigin.y) / TowerConsts.GRID_UNIT_SIZE); gridX = Math.max(0, Math.min(gridX, gridSize.x - 1)); gridY = Math.max(0, Math.min(gridY, gridSize.y - 1)); return new GridPoint(gridX, gridY); } public EventBus events() { return eventBus; } public void clearObjects() { gridObjects.clear(); positionCache = new GridPositionCache(this); } public boolean isEmpty() { return gridObjects.isEmpty(); } public GridPositionCache positionCache() { return positionCache; } public void setGridOrigin(GridPoint gridOrigin) { this.gridOrigin.set(gridOrigin); } public GridPoint getGridOrigin() { return gridOrigin; } public Rectangle getWorldBounds() { return worldBounds; } public float getGridScale() { return gridScale; } public void setGridScale(float gridScale) { this.gridScale = gridScale; } public float toWorldSpace(int gridSpaces) { return TowerConsts.GRID_UNIT_SIZE * gridSpaces * gridScale; } public String getTowerName() { return towerName; } public void setTowerName(String towerName) { this.towerName = towerName; } }