kyle.game.besiege.Faction.java Source code

Java tutorial

Introduction

Here is the source code for kyle.game.besiege.Faction.java

Source

/*******************************************************************************
 * Besiege
 * by Kyle Dhillon
 * Source Code available under a read-only license. Do not copy, modify, or distribute.
 ******************************************************************************/
package kyle.game.besiege;

import kyle.game.besiege.army.Noble;
import kyle.game.besiege.location.Castle;
import kyle.game.besiege.location.City;
import kyle.game.besiege.location.Location;
import kyle.game.besiege.location.Location.LocationType;
import kyle.game.besiege.location.Village;
import kyle.game.besiege.panels.BottomPanel;
import kyle.game.besiege.voronoi.Center;

import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Polygon;
import com.badlogic.gdx.utils.Array;

public class Faction {
    public static final int INCREASE_INTERVAL = 10; // every these seconds, relations will increase btw these factions
    public static final int CLOSE_CITY_FACTOR = 10; // this times num of close cities of one faction will decrease the relation with that faction
    public static Kingdom kingdom;
    private final int CHECK_FREQ = 5; // how frequently this manages stuff
    private final float ORDER_FACTOR = .9f; // what percent of nobles will be ordered to besiege a city
    public int index; // for keeping track of relations
    public String name;
    public TextureRegion crest;
    public Color color;
    public Array<City> cities;
    public Array<Castle> castles;
    public Array<City> closeEnemyCities;
    public Array<Castle> closeEnemyCastles;
    public Array<Village> closeEnemyVillages;
    //   public Array<Location> closeEnemyLocations;
    public Array<Location> closeFriendlyLocations;
    public Array<Noble> nobles;
    public Array<Noble> unoccupiedNobles; // nobles that aren't ordered to besiege any cities
    public Array<Location> locationsToAttack; //  and sieges these nobles are currently maintaining
    public Array<Center> centers; // centers under influence of this faction
    public Array<Polygon> territory; // polygon of all centers

    private double timeSinceIncrease; // can make more efficient by moving this to Kingdom
    private boolean hasChecked;

    private final int NOBLE_COUNT = 5; //TODO

    public static final int MAX_RELATION = 100;
    public static final int MIN_RELATION = -100;
    public static final int INIT_WAR_RELATION = -40; //when war is declared, this is the relation you will have
    //   private final static int WAR_THRESHOLD = -10; //cross this and you're at war
    private final static int WAR_THRESHOLD = 10; //cross this and you're at war

    public static Array<Faction> factions;
    public static final Faction BANDITS_FACTION = new Faction("Bandits", "crestBandits", Color.BLACK);
    public static final Faction PLAYER_FACTION = new Faction("Rogue", "crestBlank", Color.WHITE);

    public static final Color BROWN = new Color(184 / 256.0f, 119 / 256.0f, 25 / 256.0f, 1);
    public static final Color OLIVE = new Color(107 / 256.0f, 138 / 256.0f, 48 / 256.0f, 1);
    public static final Color RED = new Color(148 / 256.0f, 34 / 256.0f, 22 / 256.0f, 1);
    public static final Color MAGENTA = new Color(122 / 256.0f, 41 / 256.0f, 83 / 256.0f, 1);
    public static final Color BLUE = new Color(53 / 256.0f, 128 / 256.0f, 144 / 256.0f, 1);
    public static final Color TAN = new Color(176 / 256.0f, 169 / 256.0f, 57 / 256.0f, 1);
    public static final Color PURPLE = new Color(73 / 256.0f, 54 / 256.0f, 158 / 256.0f, 1);
    public static final Color TEAL = new Color(57 / 256.0f, 170 / 256.0f, 115 / 256.0f, 1);
    public static final Color GREEN = new Color(41 / 256.0f, 72 / 256.0f, 33 / 256.0f, 1);

    private static int factionCount;

    private static Array<Array<Integer>> factionRelations; // should break into multiple arrays
    //   private static Array<Array<Integer>> factionMilitaryAction; // is this worth it?
    //   private static Array<Array<Integer>> factionNearbyCities; // not needed, calced in real time?
    //   private static Array<Array<Integer>> factionTrade;

    public Faction(String name, String textureRegion, Color color) {
        this.name = name;
        crest = Assets.atlas.findRegion(textureRegion);
        this.color = color;

        nobles = new Array<Noble>();
        unoccupiedNobles = new Array<Noble>();
        cities = new Array<City>();
        castles = new Array<Castle>();
        centers = new Array<Center>();
        territory = new Array<Polygon>();
        //      closeEnemyLocations = new Array<Location>();
        closeEnemyCities = new Array<City>();
        closeEnemyCastles = new Array<Castle>();
        closeEnemyVillages = new Array<Village>();
        closeFriendlyLocations = new Array<Location>();
        locationsToAttack = new Array<Location>();
        timeSinceIncrease = 0;
    }

    public static void initializeFactions(Kingdom kingdom) {
        factions = new Array<Faction>();

        factionRelations = new Array<Array<Integer>>();
        //      factionMilitaryAction = new Array<Array<Integer>>();

        // add player faction (index 0) 
        createFaction(PLAYER_FACTION);

        // add bandits faction (index 1)
        createFaction(BANDITS_FACTION);

        createFaction("Geinever", "crestWhiteLion", Color.DARK_GRAY);
        createFaction("Weyvel", "crestGreenTree", OLIVE);
        createFaction("Rolade", "crestOrangeCross", BROWN);
        createFaction("Myrnfar", "crestYellowStar", TAN);
        createFaction("Corson", "crestRedCross", RED);
        createFaction("Selven", "crestGreenStripe", GREEN);
        createFaction("Halmera", "crestBlueRose", BLUE);

        createFaction("Fernel", "crestBlank", Color.LIGHT_GRAY);
        createFaction("Draekal", "crestBlank", Color.MAGENTA);

        for (Faction f : factions) {
            f.kingdom = kingdom;
        }

        factions.get(2).declareWar(factions.get(3));

        //      factionRelations = new int[factionCount][factionCount];
        for (int i = 0; i < factionCount; i++) {
            for (int j = 0; j < factionCount; j++) {
                //            factionRelations[i][j] = -30;
                factionRelations.get(i).set(j, -30);
                factionRelations.get(j).set(i, -30);
            }
        }
        for (int i = 0; i < factionCount; i++) {
            //         factionRelations[i][i] = 100;
            factionRelations.get(i).set(i, 100);
        }
    }

    public static void factionAct(float delta) {
        factions.shrink();
        for (int i = 0; i < factions.size; i++)
            factions.get(i).act(delta);
    }

    public static void createFaction(String name, String textureRegion, Color color) {
        Faction faction = new Faction(name, textureRegion, color);
        factions.add(faction);
        faction.index = factions.indexOf(faction, true);
        for (int i = 0; i < factions.size; i++) {
            //         factionRelations[faction.index][i] = 0; // resets faction relations
            //         factionRelations[i][faction.index] = 0;
            if (factionRelations.size <= faction.index)
                factionRelations.add(new Array<Integer>());

            if (factionRelations.get(i).size <= faction.index)
                factionRelations.get(i).add(0);
            else
                factionRelations.get(i).set(faction.index, 0);

            if (factionRelations.get(faction.index).size <= i)
                factionRelations.get(faction.index).add(0);
            else
                factionRelations.get(faction.index).set(i, 0);
        }
        if (faction.index >= 1) {
            faction.declareWar(BANDITS_FACTION);
        }
    }

    public static void createFaction(Faction faction) {
        factions.add(faction);
        faction.index = factions.indexOf(faction, true);
        for (int i = 0; i < factions.size; i++) {
            //         factionRelations[faction.index][i] = 0; // resets faction relations
            //         factionRelations[i][faction.index] = 0;
            if (factionRelations.size <= faction.index)
                factionRelations.add(new Array<Integer>());

            if (factionRelations.get(i).size <= faction.index)
                factionRelations.get(i).add(0);
            else
                factionRelations.get(i).set(faction.index, 0);

            if (factionRelations.get(faction.index).size <= i)
                factionRelations.get(faction.index).add(0);
            else
                factionRelations.get(faction.index).set(i, 0);
        }
        if (faction.index >= 1) {
            faction.declareWar(BANDITS_FACTION);
        }
    }

    public void removeFaction(Faction faction) {
        factions.removeValue(faction, true);
        for (int i = 0; i < factions.size; i++) {
            //         factionRelations[faction.index][i] = -999; // 'deletes' faction relations
            //         factionRelations[i][faction.index] = -999;
            factionRelations.get(i).set(faction.index, null);
            factionRelations.get(faction.index).set(i, null);
        }
    }

    /** Initialize each faction's list of hostile cities, faction control map 
     *  of centers, and village control. 
     */
    public static void initializeFactionCityInfo() {
        System.out.println("initializing faction city info");
        for (Faction f : factions) {
            f.initializeCloseLocations();
            f.centers.clear();
        }
        // update each faction's centers
        updateFactionCenters();
    }

    /** Update each faction's list of hostile cities, faction control map 
     *  of centers, and village control. 
     */
    public static void updateFactionCityInfo() {
        System.out.println("updating faction city info");
        for (Faction f : factions) {
            f.updateCloseLocations();
            f.centers.clear();
        }
        updateFactionCenters();
    }

    /** updates faction centers and village factions
     */
    private static void updateFactionCenters() {
        // update each faction's centers
        for (Center c : kingdom.getMap().connected)
            calcInfluence(c);
        kingdom.getMap().calcBorderEdges();
        for (Faction f : factions) {
            f.territory.clear();
            Array<Array<Center>> aaCenters = Map.calcConnectedCenters(f.centers);
            for (Array<Center> centers : aaCenters) {
                //            System.out.println("working");
                f.territory.add(Map.centersToPolygon(centers));
            }
        }
        for (Village v : kingdom.villages) {
            v.changeFaction(getInfluenceAt(v.center));
        }
    }

    /**
     * figures out which faction has the most influence on this center,
     * and adds it to that faction's "centers" array
     * @param center
     */
    private static void calcInfluence(Center center) {
        Point centerPoint = new Point(center.loc.x, Map.HEIGHT - center.loc.y);
        Faction bestFaction = null;
        double bestScore = 0;

        // go through factions and calc influence, saving it if it's the highest
        for (Faction faction : factions) {
            double score = 0; // score is inverse of city distance
            for (City city : faction.cities) {
                double dist = Kingdom.distBetween(city, centerPoint);
                score += 1 / dist;
            }
            for (Castle castle : faction.castles) {
                double dist = Kingdom.distBetween(castle, centerPoint);
                score += 0.8 / dist;
            }
            if (score > bestScore) {
                bestScore = score;
                bestFaction = faction;
            }
        }

        if (bestFaction != null) {
            bestFaction.centers.add(center);
            center.faction = bestFaction;
        }
    }

    public static Faction getInfluenceAt(Center center) {
        for (Faction f : factions) {
            if (f.centers.contains(center, true))
                return f;
        }

        System.out.println("no one controls that center");
        return null;

    }

    public void act(float delta) {
        timeSinceIncrease += delta;

        //      if (this == PLAYER_FACTION)
        //         System.out.println(this.name + " " + BANDITS_FACTION.name + getRelations(this, BANDITS_FACTION));

        if (timeSinceIncrease >= INCREASE_INTERVAL) {
            //         System.out.println(timeSinceIncrease);
            for (Faction f : factions)
                changeRelation(this, f, 0); // factor to increase relations by
            timeSinceIncrease = 0;
        }

        if (this != PLAYER_FACTION)
            autoManage(delta);
    }

    public void autoManage(float delta) {
        // send armies to capture/raid enemy cities/castles/villages
        // negotiate diplomacy, declare war/peace
        // that's it for now :D
        if (Kingdom.getTotalHour() % CHECK_FREQ == 0 && !hasChecked) {
            manageNobles();
            hasChecked = true;
        } else if (Kingdom.getTotalHour() % CHECK_FREQ != 0)
            hasChecked = false;
    }

    // reallocate nobles from this city if it's taken by an enemy
    public void allocateNoblesFrom(City city) {
        for (Noble noble : city.nobles)
            this.getRandomCity().addNoble(noble);
    }

    // create new nobles for this city
    public void allocateNoblesFor(City city) {
        assert (this.cities.contains(city, true));
        this.createNobleAt(city);
        // new noble created everytime a city is captured... but also nobles will die when they lose big battles
    }

    public void manageNobles() {
        // if a city doesn't have a noble, create a baron or earl
        // when a noble is upgraded to the next level (later, if a city is upgraded) add a fresh noob to replace them.

        //      while (nobles.size < NOBLE_COUNT && cities.size >= 1) {
        //         createNobleAt(cities.random());
        //      }
        manageSieges();

        // figure out whether or not to organize a siege or something!
    }

    public void manageSieges() {
        if (locationsToAttack.size < 1 && unoccupiedNobles.size > 1 && closeEnemyCities.size > 1) {
            Location randomLocation = closeEnemyCities.random();
            orderSiegeOf(randomLocation);
        }
        //      if (nobles.size > 1 && closeEnemyCities.size > 1) {
        //         Noble random = nobles.random();
        //         if (!random.hasSpecialTarget()) {
        //            Location randomLoc = closeEnemyCities.random();
        //            random.setSpecialTarget(randomLoc);
        //            System.out.println("giving " + random.getName() + " special target " + randomLoc.getName());
        //         }
        //      }
    }

    public void orderSiegeOf(Location location) {
        locationsToAttack.add(location);
        int noblesToOrder = Math.max((int) (unoccupiedNobles.size * ORDER_FACTOR), 1);
        System.out.println(this.name + " is ordering a siege of " + location.getName() + " involving "
                + noblesToOrder + " nobles");
        BottomPanel.log(this.name + " is ordering a siege of " + location.getName() + " involving " + noblesToOrder
                + " nobles", "magenta");
        while (noblesToOrder > 0) {
            Noble randomNoble = unoccupiedNobles.random();
            setTask(randomNoble, location);
            noblesToOrder--;
        }
    }

    public void setTask(Noble noble, Location location) {
        noble.setSpecialTarget(location);
        this.unoccupiedNobles.removeValue(noble, true);
    }

    public void endTask(Noble noble) {
        noble.setSpecialTarget(null);
        this.unoccupiedNobles.add(noble);
    }

    public void createNobleAt(Location location) {
        Noble noble = new Noble(location.getKingdom(), location);
        // randomize size
        this.addNoble(noble);
        ((City) location).nobles.add(noble);
        noble.goToNewTarget();
        location.setContainerForArmy(noble);
    }

    public void addNoble(Noble noble) {
        this.nobles.add(noble);
        this.unoccupiedNobles.add(noble);
    }

    public void removeNoble(Noble noble) {
        this.nobles.removeValue(noble, true);
        if (unoccupiedNobles.contains(noble, true))
            unoccupiedNobles.removeValue(noble, true);
    }

    /** First updates each city's lists of close friendly and 
     *  hostile cities, then updates this faction's lists based on 
     *  that list */
    public void initializeCloseLocations() {
        //      System.out.println(this.name + " initializing cities: ");
        this.closeEnemyCities.clear();
        this.closeEnemyCastles.clear();
        // find hostile locations near cities
        for (City c : cities) {
            //         System.out.println("  close to " + c.getName() + ":");
            c.findCloseLocations();
            //         System.out.println("  f  " + c.closestEnemyCities.size);
            for (City hostileCity : c.closestEnemyCities) {
                if (!closeEnemyCities.contains((City) hostileCity, true)) {
                    //               System.out.println("    " + hostileCity.getName());
                    closeEnemyCities.add((City) hostileCity);
                }
            }
            for (Castle hostileCastle : c.closestEnemyCastles) {
                if (!closeEnemyCastles.contains((Castle) hostileCastle, true)) {
                    //               System.out.println("    " + hostileCastle.getName());
                    closeEnemyCastles.add((Castle) hostileCastle);
                }
            }
        }
        // find hostile locations near castles
        for (Castle c : castles) {
            //         System.out.println("  close to " + c.getName() + ":");
            c.findCloseLocations();
            //         System.out.println("  f  " + c.closestEnemyCities.size);
            for (City hostile : c.closestEnemyCities) {
                if (!closeEnemyCities.contains((City) hostile, true)) {
                    //               System.out.println("    " + hostile.getName());
                    closeEnemyCities.add((City) hostile);
                }
            }
            for (Castle hostile : c.closestEnemyCastles) {
                if (!closeEnemyCastles.contains((Castle) hostile, true)) {
                    //               System.out.println("    " + hostile.getName());
                    closeEnemyCastles.add((Castle) hostile);
                }
            }
        }
    }

    /** First updates each city's lists of close friendly and 
     *  hostile cities, then updates this faction's lists based on 
     *  that list
     *  TODO review this process and make sure it works */
    public void updateCloseLocations() {
        System.out.println(this.name + " updating cities: ");
        this.closeEnemyCities.clear();
        this.closeEnemyCastles.clear();
        // find hostile locations near cities
        for (City c : cities) {
            System.out.println("  close to " + c.getName() + ":");
            c.updateCloseLocations();
            System.out.println("  f  " + c.closestEnemyCities.size);
            for (City hostileCity : c.closestEnemyCities) {
                if (!closeEnemyCities.contains((City) hostileCity, true)) {
                    System.out.println("    " + hostileCity.getName());
                    closeEnemyCities.add((City) hostileCity);
                }
            }
            for (Castle hostileCastle : c.closestEnemyCastles) {
                if (!closeEnemyCastles.contains((Castle) hostileCastle, true)) {
                    System.out.println("    " + hostileCastle.getName());
                    closeEnemyCastles.add((Castle) hostileCastle);
                }
            }
        }
        // find hostile locations near castles
        for (Castle c : castles) {
            System.out.println("  close to " + c.getName() + ":");
            c.updateCloseLocations();
            System.out.println("  f  " + c.closestEnemyCities.size);
            for (City hostile : c.closestEnemyCities) {
                if (!closeEnemyCities.contains((City) hostile, true)) {
                    System.out.println("    " + hostile.getName());
                    closeEnemyCities.add((City) hostile);
                }
            }
            for (Castle hostile : c.closestEnemyCastles) {
                if (!closeEnemyCastles.contains((Castle) hostile, true)) {
                    System.out.println("    " + hostile.getName());
                    closeEnemyCastles.add((Castle) hostile);
                }
            }
        }
    }

    //   /** First updates each city's lists of close friendly and 
    //    *  hostile cities, then updates this faction's lists based on 
    //    *  that list
    //    *  TODO review this process and make sure it works */
    //   public void updateCloseHostileCities() {
    //      Array<City> tempCloseEnemyCities = new Array<City>();
    //      Array<City> tempCloseFriendlyCities = new Array<City>();
    //      //System.out.println(this.name + ":");
    //
    //
    //      for (City c: cities) {
    //         c.updateCloseLocations();
    //         for (City hostile : c.getClosestHostileLocations()) {
    //            tempCloseEnemyCities.add(hostile);
    //            //this makes factions dislike factions with nearby cities
    //            // compares new array with old array (can be better)
    //            if (!closeEnemyLocations.contains(hostile, true)) {
    //               if (hostile.getFaction() == this) System.out.println("hostile: " + hostile.getName());
    //               changeRelation(this, hostile.getFaction(), -1*CLOSE_CITY_FACTOR);
    //            }   
    //         }
    //         for (City friendly : c.getClosestFriendlyCities()) {
    //            if (friendly.getFaction() != this) {
    //               tempCloseFriendlyCities.add(friendly);
    //               //this makes factions dislike factions with nearby cities
    //               if (!closeFriendlyLocations.contains(friendly, true) && friendly.getFaction().index != this.index) {
    //                  //                  System.out.println("new friendly city: " + friendly.getName());
    //                  changeRelation(this, friendly.getFaction(), -1*CLOSE_CITY_FACTOR);
    //               }   
    //            }
    //         }
    //      }
    //      closeEnemyLocations = new Array<City>(tempCloseEnemyCities);
    //      closeFriendlyLocations = new Array<City>(tempCloseFriendlyCities);
    //   }

    //   /** Calculates negative effect of close cities on the relations
    //    *  between two factions
    //    * 
    //    * @param that
    //    * @return effect on relations
    //    */
    //   public int getCloseCityEffect(Faction that) {
    //      if (this.index == that.index) return 0;
    //      int totalEffect = 0;
    //      if (isAtWar(this, that)) {
    //         for (City c : closeEnemyLocations)
    //            if (c.getFaction() == that)   totalEffect += -1*CLOSE_CITY_FACTOR;
    //      }
    //      else {
    //         for (City c : closeFriendlyLocations)
    //            if (c.getFaction() == that) totalEffect += -1*CLOSE_CITY_FACTOR;
    //      }
    //      return totalEffect;
    //   }

    public int getRelationsWith(Faction that) {
        return getRelations(this, that);
    }

    public void declareWar(Faction that) {
        if (!Faction.isAtWar(this, that))
            declareWar(this, that);
    }

    public void goRogue() { // just for testing, declares war on all factions other than this one
        for (int i = 0; i < factions.size; i++)
            if (i != index)
                declareWar(this, factions.get(i));
    }

    public int getTotalWealth() {
        int total = 0;
        for (City c : cities)
            total += c.getParty().wealth;
        return total;
    }

    public static int getRelations(Faction faction1, Faction faction2) {
        return factionRelations.get(faction1.index).get(faction2.index);
    }

    /** return whether or not these two factions are at war. 
     * note that the same faction can't be at war with itself.
     * @param faction1
     * @param faction2
     * @return
     */
    public static boolean isAtWar(Faction faction1, Faction faction2) {
        if (faction1 == faction2)
            return false;
        return (getRelations(faction1, faction2) < WAR_THRESHOLD);
    }

    public static void setAtWar(Faction faction1, Faction faction2) {
        //      factionRelations[faction1][faction2] = WAR_THRESHOLD-1;
        //      factionRelations[faction2][faction1] = WAR_THRESHOLD-1;
        factionRelations.get(faction1.index).set(faction2.index, INIT_WAR_RELATION);
        factionRelations.get(faction2.index).set(faction1.index, INIT_WAR_RELATION);
    }

    public static void setNeutral(Faction faction1, Faction faction2) {
        factionRelations.get(faction1.index).set(faction2.index, 0);
        factionRelations.get(faction2.index).set(faction1.index, 0);

        //      factionRelations[faction1][faction2] = 0;
        //      factionRelations[faction2][faction1] = 0;
    }

    public static void makePeace(Faction faction1, Faction faction2) {
        //      BottomPanel.log(faction1.name + " and " + faction2.name + " have signed a peace agreement!", "magenta");
        setNeutral(faction1, faction2);
    }

    public static void declareWar(Faction faction1, Faction faction2) {
        //      BottomPanel.log(faction1.name + " and " + faction2.name + " have declared war!", "magenta");
        setAtWar(faction1, faction2);
    }

    public static void changeRelation(Faction faction1, Faction faction2, int delta) {
        int initialRelation = factionRelations.get(faction1.index).get(faction2.index);
        int newRelation;
        if (initialRelation + delta >= MAX_RELATION)
            newRelation = MAX_RELATION;
        else if (initialRelation + delta <= MIN_RELATION)
            newRelation = MIN_RELATION;
        else
            newRelation = initialRelation + delta;
        if (initialRelation >= WAR_THRESHOLD && newRelation < WAR_THRESHOLD)
            ;
        //         BottomPanel.log(faction1.name + " and " + faction2.name + " have declared war!", "magenta");
        else if (initialRelation < WAR_THRESHOLD && newRelation >= WAR_THRESHOLD)
            makePeace(faction1, faction2);
        factionRelations.get(faction1.index).set(faction2.index, newRelation);
    }

    //   public static void calcAllRelations() {
    //      for (Faction f : factions) { 
    //         for (int i = 0; i < factions.size; i++) {
    //            int base = 0;
    ////            base += factionMilitaryAction.get(f.index).get(i); // Military actions
    //            base += f.getCloseCityEffect(factions.get(i));      // Borders
    //            base += factions.get(i).getCloseCityEffect(f);      // (Borders is 2-way)
    //            factionRelations.get(i).set(f.index, base);       // can make more efficient
    //            factionRelations.get(f.index).set(i, base);
    //         }
    //      }
    //   }

    public static Faction get(int index) {
        return factions.get(index);
    }

    public City getRandomCity() {
        if (cities.size > 0) {
            return cities.random();
        } else
            return null;
    }
}