Java tutorial
/******************************************************************************* * Besiege * by Kyle Dhillon * Source Code available under a read-only license. Do not copy, modify, or distribute. ******************************************************************************/ // group of actors, contains bg, all cities and all armies package kyle.game.besiege; import java.util.Scanner; import kyle.game.besiege.army.Army; import kyle.game.besiege.army.ArmyPlayer; import kyle.game.besiege.army.Bandit; import kyle.game.besiege.battle.Battle; import kyle.game.besiege.location.Castle; import kyle.game.besiege.location.City; import kyle.game.besiege.location.Location; import kyle.game.besiege.location.Village; import kyle.game.besiege.panels.Panel; import kyle.game.besiege.voronoi.Center; import kyle.game.besiege.voronoi.Corner; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType; import com.badlogic.gdx.math.Polygon; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.scenes.scene2d.Group; import com.badlogic.gdx.utils.Array; public class Kingdom extends Group { public static BitmapFont arial; public static final double DECAY = .1; public static final float HOUR_TIME = 2.5f; public static final int CITY_START_WEALTH = 500; public static final int VILLAGE_START_WEALTH = 10; public static final int CASTLE_START_WEALTH = 100; public static final int BANDIT_FREQ = 1000; public static final int MAX_BANDITS = 5; public static boolean drawCrests = true; public static boolean drawArmyCrests = true; private final float LIGHT_ADJUST_SPEED = .005f; //adjust this every frame when changing daylight private final float NIGHT_FLOAT = .6f; private final float MOUSE_DISTANCE = 10; // distance destination must be from mouse to register private final int DAWN = 7; private final int DUSK = 21; public static float clock; private int timeOfDay; // 24 hour day is 60 seconds, each hour is 2.5 seconds private int day; public boolean night; // is nighttime? public float currentDarkness; // can be used for LOS private float targetDarkness; // for fading private Map map; private MapScreen mapScreen; private Array<Army> armies; public Array<City> cities; public Array<Castle> castles; public Array<Village> villages; private Array<Battle> battles; private ArmyPlayer player; public int banditCount; private Destination currentPanel; private boolean mouseOver; // is mouse over Kingdom screen? private boolean paused; private boolean leftClicked; private boolean rightClicked; private Point mouse; private ShapeRenderer sr = new ShapeRenderer(); public Kingdom(MapScreen mapScreen) { map = new Map(); clock = 0; // set initial clock timeOfDay = 0; day = 0; // currentDarkness = NIGHT_FLOAT; currentDarkness = 0; // fade in this.mapScreen = mapScreen; addActor(map); armies = new Array<Army>(); cities = new Array<City>(); castles = new Array<Castle>(); villages = new Array<Village>(); battles = new Array<Battle>(); Faction.initializeFactions(this); initializeCities(); initializeVillages(); initializeCastles(); Faction.initializeFactionCityInfo(); for (int i = 0; i < cities.size; i++) cities.get(i).findCloseLocations(); arial = new BitmapFont(); mouse = new Point(0, 0); banditCount = 0; currentPanel = new Point(0, 0); } @Override public void act(float delta) { if (mouseOver) { if (leftClicked) leftClick(mouse); else if (rightClicked) rightClick(mouse); else if (BesiegeMain.appType != 1) mouseOver(mouse); } if (!paused) { time(delta); super.act(delta); manageBandits(); Faction.factionAct(delta); } if (leftClicked) leftClicked = false; if (rightClicked) rightClicked = false; } public void manageBandits() { if (banditCount <= MAX_BANDITS && cities.size != 0) { if (Math.random() < 1.0 / BANDIT_FREQ) { City originCity = cities.random(); // if (originCity.getVillages().size == 0) createBandit(originCity); } } } // just check adjacent centers! public void updateArmyPolygon(Army army) { if (army.containing != null) { // first check if it's left it's previous polygon // if not, return, if so, remove from previous container if (army.containing.polygon != null && army.containing.polygon.contains(army.getCenterX(), army.getCenterY())) { if (!army.containing.armies.contains(army, true)) army.containing.armies.add(army); return; } army.containing.armies.removeValue(army, true); for (Center adjacent : army.containing.neighbors) { // checks if in connected (all connected have polygon) if (adjacent.polygon != null) { Polygon p = adjacent.polygon; if (p.contains(army.getCenterX(), army.getCenterY())) { adjacent.armies.add(army); army.containing = adjacent; return; } } } army.containing = null; // System.out.println("nothing adjacent found for " + army.getName()); } // container is null (slow during initialization stages - plan is to initialize armies with accurate center) for (Center center : map.connected) { Polygon p = center.polygon; if (p.contains(army.getCenterX(), army.getCenterY())) { center.armies.add(army); army.containing = center; // System.out.println("completely differnt polygon"); return; } } } // update time public void time(float delta) { clock += delta; timeOfDay = (int) ((clock - day * 60) / HOUR_TIME); if (timeOfDay == 24) { dailyRoutine(); day++; } if (timeOfDay >= DAWN && timeOfDay <= DUSK) night = false; if (timeOfDay <= DAWN || timeOfDay >= DUSK) night = true; } // Events that occur every day private void dailyRoutine() { System.out.println("day is done"); // increase village wealth for (Village v : villages) { v.dailyWealthIncrease(); v.dailyPopIncrease(); } // increase city wealth for (City c : cities) { c.dailyWealthIncrease(); c.dailyPopIncrease(); } } private void mouseOver(Point mouse) { Destination d = getDestAt(mouse); // if (d.getType() != 0) this.setPanelTo(d); // d.setMouseOver(true); } private void setPanelTo(Destination newPanel) { // if (currentPanel == null) System.out.println("currentPanel is null"); // makes sure not to set the same panel a lot, and makes sure not to return to previous for every single point if (newPanel != currentPanel && (newPanel.getType() != 0 || currentPanel.getType() != 0)) { mapScreen.getSidePanel().setActiveDestination(newPanel); currentPanel = newPanel; } } private void leftClick(Point mouse) { Destination d = getDestAt(mouse); if (d != player && !player.isInBattle()) { paused = false; player.setTarget(d); mapScreen.shouldCenter = true; //if (player.getTarget() != null) System.out.println("target = " + player.getTarget().getName()); } } private void rightClick(Point mouse) { Destination d = getDestAt(mouse); if (d.getType() == 1) { Location location = (Location) d; mapScreen.getSidePanel().setActiveLocation(location); } if (d.getType() == 2) { // army Army destinationArmy = (Army) d; mapScreen.getSidePanel().setActiveArmy(destinationArmy); } if (d.getType() == 4) { //battle Battle battle = (Battle) d; mapScreen.getSidePanel().setActiveBattle(battle); } } public void click(int pointer) { if (pointer == 0) leftClicked = true; else if (pointer == 1) rightClicked = true; else if (pointer == 4) writeCity(); } public void writeCity() { float x = mouse.getCenterX(); float y = mouse.getCenterY(); // mapScreen.out.println(x + " " + y); } private Destination getDestAt(Point mouse) { Destination dest = new Point(mouse.getCenterX(), mouse.getCenterY()); for (City city : cities) { if (Kingdom.distBetween(city, mouse) <= MOUSE_DISTANCE) dest = city; } for (Village village : villages) { if (Kingdom.distBetween(village, mouse) <= MOUSE_DISTANCE) dest = village; } for (Castle castle : castles) { if (Kingdom.distBetween(castle, mouse) <= MOUSE_DISTANCE) dest = castle; } for (Army army : armies) { if (army.isVisible() && Kingdom.distBetween(army, mouse) <= MOUSE_DISTANCE) if (mapScreen.losOn) { if (Kingdom.distBetween(army, player) <= player.getLineOfSight()) dest = army; } else dest = army; } for (Battle battle : battles) { if (Kingdom.distBetween(battle, mouse) <= MOUSE_DISTANCE) { dest = battle; } } return dest; } @Override public void draw(SpriteBatch batch, float parentAlpha) { //batch.setColor(Color.WHITE); if (night) targetDarkness = NIGHT_FLOAT; else targetDarkness = 1f; if (currentDarkness != targetDarkness) adjustDarkness(); batch.setColor(currentDarkness, currentDarkness, currentDarkness, 1f); map.draw(batch, parentAlpha); // leak is not here super.draw(batch, parentAlpha); arial.setColor(Color.WHITE); arial.setScale(2 * (mapScreen.getCamera().zoom)); if (drawCrests) { for (City c : cities) c.drawCrest(batch); for (Village v : villages) v.drawCrest(batch); for (Castle c : castles) c.drawCrest(batch); } if (drawArmyCrests) for (Army a : armies) a.drawCrest(batch); for (City c : cities) c.drawText(batch); for (Village v : villages) v.drawText(batch); for (Castle c : castles) c.drawText(batch); } public void addCity(City newCity) { cities.add(newCity); newCity.getFaction().createNobleAt(newCity); addActor(newCity); } public void initializeCities() { Array<String> cityArray = Assets.cityArray; // Scanner scanner = Assets.cityList; // int currentFaction = 2; // no bandits or player // int factionRepeats = 0; // int maxRepeats = (int) (Math.random()*3) + 1; // max 2, min 0 int maxRepeats = 4; // while (scanner.hasNextLine() && (map.cityCorners.size > 0 && map.cityCenters.size > 0)) { while (cityArray.size > 0 && (map.cityCorners.size > 0 && map.cityCenters.size > 0)) { // System.out.println("adding city"); City city; int x, y; boolean useCorner = true; // later allow to use center if (Math.random() > .5) useCorner = false; // later allow to use center // make with corner if (useCorner) { Corner corner; corner = map.cityCorners.random(); x = (int) corner.getLoc().x; y = (int) corner.getLoc().y; map.cityCorners.removeValue(corner, true); map.availableCorners.removeValue(corner, true); // remove neighboring corners 2 levels deep! for (Corner adj : corner.adjacent) { if (adj != null) { map.cityCorners.removeValue(adj, true); map.availableCorners.removeValue(adj, true); for (int i = 0; i < adj.adjacent.size(); i++) { // two levels deep if (adj.adjacent.get(i) != null) map.cityCorners.removeValue(adj.adjacent.get(i), true); } } } // remove neighboring centers for (Center adj : corner.touches) { if (adj != null) { map.cityCenters.removeValue(adj, true); map.availableCenters.removeValue(adj, true); } } int index = -1; // calculate the closest city double closestDistance = 9999999; City closestCity = null; for (City c : cities) { double distance = c.distTo(x, Map.HEIGHT - y); if (distance < closestDistance) { closestCity = c; closestDistance = distance; } } Faction faction = null; if (closestCity == null || closestCity.getFaction().cities.size > maxRepeats) { int factionIndex = 2; while (Faction.get(factionIndex).cities.size > maxRepeats) factionIndex++; faction = Faction.get(factionIndex); } else faction = closestCity.getFaction(); city = new City(this, cityArray.pop(), index, faction, x, Map.HEIGHT - y, CITY_START_WEALTH); // city = new City(this, cityArray.pop(), index, Faction.get(currentFaction), x, Map.HEIGHT-y, CITY_START_WEALTH); city.setCorner(corner); } // make with center else { Center center; center = map.cityCenters.random(); x = (int) center.loc.x; y = (int) center.loc.y; map.cityCenters.removeValue(center, true); map.availableCenters.removeValue(center, true); for (Corner adj : center.corners) { if (adj != null) { map.cityCorners.removeValue(adj, true); // map.availableLocationSites.removeValue(adj.loc, false); for (int i = 0; i < adj.adjacent.size(); i++) { // two levels deep if (adj.adjacent.get(i) != null) { map.cityCorners.removeValue(adj.adjacent.get(i), true); // map.availableLocationSites.removeValue(adj.adjacent.get(i).loc, false); } } } } for (Center adjCenter : center.neighbors) { if (adjCenter != null) { map.cityCenters.removeValue(adjCenter, true); // map.availableLocationSites.removeValue(adjCenter.loc, false); } } int index = -1; // city = new City(this, scanner.next(), index, Faction.get(currentFaction), x, Map.HEIGHT-y, CITY_START_WEALTH); // calculate the closest city double closestDistance = 9999999; City closestCity = null; for (City c : cities) { double distance = c.distTo(x, Map.HEIGHT - y); if (distance < closestDistance) { closestCity = c; closestDistance = distance; } } Faction faction = null; if (closestCity == null || closestCity.getFaction().cities.size > maxRepeats) { // TODO set faction to be lowest numbered faction without repeats int factionIndex = 2; while (Faction.get(factionIndex).cities.size > maxRepeats) factionIndex++; faction = Faction.get(factionIndex); } else faction = closestCity.getFaction(); city = new City(this, cityArray.pop(), index, faction, x, Map.HEIGHT - y, CITY_START_WEALTH); // city = new City(this, cityArray.pop(), index, Faction.get(currentFaction), x, Map.HEIGHT-y, CITY_START_WEALTH); city.setCenter(center); } addCity(city); // factionRepeats++; // if (factionRepeats > maxRepeats) { // currentFaction++; // factionRepeats = 0; // maxRepeats = (int) (Math.random()*3) + 1; // } } // assign factions to cities // for (City c : cities) { // c.changeFaction(Faction.get(2)); // } // best solution: calculate city locations in advance, then assign factions, then create cities. System.out.println("Number cities: " + cities.size); } // villages only spawn at centers (for influence purposes) public void initializeVillages() { // Scanner scanner = Assets.villageList; Array<String> villageArray = Assets.villageArray; // while (scanner.hasNextLine() && map.availableCenters.size > 0) { while (villageArray.size > 0 && map.availableCenters.size > 0) { Center center = map.availableCenters.random(); // Village village = new Village(this, scanner.next(), -1, null, (float) center.loc.x, (float) (Map.HEIGHT-center.loc.y), VILLAGE_START_WEALTH); Village village = new Village(this, villageArray.pop(), -1, null, (float) center.loc.x, (float) (Map.HEIGHT - center.loc.y), VILLAGE_START_WEALTH); villages.add(village); village.center = center; addActor(village); } System.out.println("Number villages: " + villages.size); } public void initializeCastles() { Array<String> castleArray = Assets.castleArray; int currentFaction = 2; // no bandits or player int factionRepeats = 0; int maxRepeats = (int) (Math.random() * 3) + 1; // max 2, min 0 // while (scanner.hasNextLine() && map.availableCenters.size > 0) { while (castleArray.size > 0 && map.availableCorners.size > 0) { Corner corner; do { System.out.println(map.availableCorners.size); corner = map.availableCorners.random(); map.availableCorners.removeValue(corner, true); } while (corner == null && map.availableCorners.size > 0); Castle castle = new Castle(this, castleArray.pop(), -1, Faction.get(currentFaction), (float) corner.loc.x, (float) (Map.HEIGHT - corner.loc.y), CASTLE_START_WEALTH); castles.add(castle); castle.corner = corner; // if (corner == null) // System.out.println("ADDING NULL CORNER"); addActor(castle); factionRepeats++; if (factionRepeats > maxRepeats) { currentFaction++; factionRepeats = 0; maxRepeats = (int) (Math.random() * 3) + 1; } } System.out.println("Number castles: " + villages.size); } public void addArmy(Army add) { armies.add(add); addActor(add); } public void addPlayer() { player = new ArmyPlayer(this, mapScreen.getCharacter(), Faction.PLAYER_FACTION, (int) map.reference.loc.x, (int) (Map.HEIGHT - map.reference.loc.y), 6); player.getParty().player = true; player.containing = map.reference; addArmy(player); // player.initializeBox(); // otherwise line of sight will be 0! //mapScreen.center(); doesn't do anything bc of auto resize //player.getParty().wound(player.getParty().getHealthy().random()); // mapScreen.getFog().updateFog(); } public void createBandit(City originCity) { //get a good bandit location, out of player's LOS, away from other armies, etc. Bandit bandit = new Bandit(this, "Bandit", 0, 0); float posX, posY; // float posX = 2048; // float posY = 2048; Point p = new Point(0, 0); int count = 0; do { count++; posX = (float) (originCity.getCenterX() + (Math.random() + 0.5) * bandit.getLineOfSight()); posY = (float) (originCity.getCenterY() + (Math.random() + 0.5) * bandit.getLineOfSight()); p.setPos(posX, posY); // System.out.println("creating bandit spot"); } while ((map.isInWater(p) || Kingdom.distBetween(p, player) <= player.getLineOfSight()) && count < 10); // makes sure bandit is out of sight of player! if (count == 10) return; bandit.setDefaultTarget(cities.random()); bandit.setPosition(posX, posY); // System.out.println("new bandit created at " + origin.getName() + posX + " " + posY); this.addArmy(bandit); banditCount++; } public void removeArmy(Army remove) { if (remove.getParty().player) System.out.println("kingdom removing player"); armies.removeValue(remove, true); if (remove.containing != null) remove.containing.armies.removeValue(remove, true); this.removeActor(remove); } public Array<Army> getArmies() { return armies; } public void addBattle(Battle battle) { battles.add(battle); } public void removeBattle(Battle battle) { battles.removeValue(battle, true); } public Array<City> getCities() { return cities; } public ArmyPlayer getPlayer() { return player; } public void setPaused(boolean paused) { this.paused = paused; } public float getMouseX() { return mouse.getX(); } public float getMouseY() { return mouse.getY(); } public void setMouse(Vector2 mousePos) { mouse.setPos(mousePos.x, mousePos.y); } public MapScreen getMapScreen() { return mapScreen; } public float clock() { return clock; } public void toggleNight() { night = !night; } // public void setToNight(SpriteBatch b) { // b.setColor(NIGHT_FLOAT, NIGHT_FLOAT, NIGHT_FLOAT, 1f); // } // public void setToDay(SpriteBatch b) { // b.setColor(1f, 1f, 1f, 1f); // } private void adjustDarkness() { if (targetDarkness - currentDarkness > LIGHT_ADJUST_SPEED) currentDarkness += LIGHT_ADJUST_SPEED; else if (currentDarkness - targetDarkness > LIGHT_ADJUST_SPEED) currentDarkness -= LIGHT_ADJUST_SPEED; } public int getTime() { return timeOfDay; } public static int getTotalHour() { return (int) (clock / HOUR_TIME); } public int getDay() { return day; } public float getZoom() { return getMapScreen().getCamera().zoom; } public void setMouseOver(boolean b) { mouseOver = b; } public boolean isPaused() { return paused; } public Map getMap() { return map; } public static double distBetween(Destination d1, Destination d2) { // TODO optimize by computing getCenter only once per return Math.sqrt((d1.getCenterX() - d2.getCenterX()) * (d1.getCenterX() - d2.getCenterX()) + (d1.getCenterY() - d2.getCenterY()) * (d1.getCenterY() - d2.getCenterY())); } // should be slightly faster than above public static double sqDistBetween(Destination d1, Destination d2) { return (d1.getCenterX() - d2.getCenterX()) * (d1.getCenterX() - d2.getCenterX()) + (d1.getCenterY() - d2.getCenterY()) * (d1.getCenterY() - d2.getCenterY()); } }