public SettlementBuilder2(HeightFunction heightFunc, SettlementShell shell) : base(shell.ChunkPosition, new Vec2i(1, 1) * shell.Type.GetSize(), heightFunc, shell.ChunkBases) { Shell = shell; Debug.Log(shell + "," + shell.LocationData); GenerationRandom = new GenerationRandom(0); TileSize = shell.Type.GetSize() * World.ChunkSize; Middle = new Vec2i(TileSize / 2, TileSize / 2); //Tiles = new Tile[TileSize, TileSize]; //SettlementObjects = new WorldObjectData[TileSize, TileSize]; Buildings = new List <Building>(); //PathNodes = new List<Vec2i>(); BuildingPlots = new List <Recti>(); SettlementType = shell.Type; }
/// <summary> /// Generates all roads in the game /// We do this by first starting at each kingdom capital, and iterativly searching /// near grid points to find near by settlements and tactical locations. /// We then build roads to these locations, /// </summary> public void GenerateRoads() { Dictionary <int, Shell> kingdomCapitals = new Dictionary <int, Shell>(); //iterate each shell to find all kingdoms foreach (Shell s in SetAndTactShells) { Kingdom king = s.GetKingdom(); if (s is SettlementShell && (s as SettlementShell).Type == SettlementType.CAPITAL) { //Capitals are added to SetAndTacShells first, so should all be at start kingdomCapitals.Add(king.KingdomID, s); } } //Iterate each set of capitals and ensure there is a road conenction foreach (KeyValuePair <int, Shell> kvp1 in kingdomCapitals) { foreach (KeyValuePair <int, Shell> kvp2 in kingdomCapitals) { if (kvp1.Key == kvp2.Key) { continue; } BuildRoad(kvp1.Value.GridPoint, kvp2.Value.GridPoint); } } foreach (Shell s in SetAndTactShells) { int king = s.KingdomID; if (s is SettlementShell) { SettlementShell ss = s as SettlementShell; if (ss.Type == SettlementType.CAPITAL) { continue; } BuildRoad(ss.GridPoint, kingdomCapitals[king].GridPoint); } else if (s is TacticalLocationShell) { TacticalLocationShell tls = s as TacticalLocationShell; BuildRoad(tls.GridPoint, kingdomCapitals[king].GridPoint); } } }
public SettlementEconomy(SettlementShell shell, EconomyData data) { shell.Economy = this; Inventory = data.Inventory; UsePerTick = data.UsePerTick; RawProductionPerTick = data.RawProductionPerTick; ProductionAbility = data.ProductionAbility; DesiredItemStock = data.DesiredItemStock; EconomicProduction = data.EconomicProduction; RequiredImports = new Dictionary <EconomicItem, int>(); // DesiredImports = new Dictionary<EconomicItem, int>(); Surplus = new Dictionary <EconomicItem, int>(); SurlusByExportType = new Dictionary <EconomicItemType, Dictionary <EconomicItem, int> >(); SurplusByTypeCount = new Dictionary <EconomicItemType, int>(); CurrentActiveGroups = new List <EntityGroup>(); }
/// <summary> /// Finds the type of resources near the supplied settlement, and then generates an economy based /// onthis /// </summary> private void CalculateSettlementEconomy(SettlementShell shell) { int size = shell.Type == SettlementType.CAPITAL ? 16 : shell.Type == SettlementType.CITY ? 14 : shell.Type == SettlementType.TOWN ? 10 : 8; //The total amount of resources within the settlement bounds Dictionary <ChunkResource, float> settlementResources = new Dictionary <ChunkResource, float>(); Vec2i baseChunk = shell.ChunkPosition; //We iterate every chunk within this settlement for (int x = 0; x < size; x++) { for (int z = 0; z < size; z++) { ChunkBase2 cb = GameGen.TerGen.ChunkBases[baseChunk.x + x, baseChunk.z + z]; //We iterate each possible resource the chunk could have, foreach (ChunkResource cr in MiscUtils.GetValues <ChunkResource>()) { //We find the amount of this resource in the chunk, and add this to the settlement total float am = cb.GetResourceAmount(cr); if (!settlementResources.ContainsKey(cr)) { settlementResources.Add(cr, 0); } settlementResources[cr] += am; } } } //We now know the raw resources in the area, if (shell.Type == SettlementType.VILLAGE) { GenerateVillageEconomy(settlementResources, shell); } else if (shell.Type == SettlementType.TOWN) { GenerateTownEconomy(settlementResources, shell); } else { GenerateTownEconomy(settlementResources, shell); } }
/// <summary> /// We decide the placement /// </summary> public void GenerateAllSettlementShells() { //Datastructure ordering the KingdomTotalSettlements by relative kingdom Dictionary <int, KingdomTotalSettlements> KTS = new Dictionary <int, KingdomTotalSettlements>(); foreach (Kingdom k in GameGen.KingdomGen.Kingdoms) { //Calculate the maximum number of settlements each kingdom should have KTS.Add(k.KingdomID, CalculateSettlementCount(k)); } SetAndTactShells = new List <Shell>(); //Add settlement shells for kingdom capitals foreach (Kingdom king in GameGen.KingdomGen.Kingdoms) { GridPoint gp = GameGen.GridPlacement.GetNearestPoint(king.CapitalChunk); SettlementShell cap = new SettlementShell(gp, king.KingdomID, SettlementType.CAPITAL); SetAndTactShells.Add(cap); gp.Shell = cap; } //Create weighted random list for possible settlement placements FindGridPointsSettlementDesirability(); //Decide the placement of all settlements Dictionary <int, Dictionary <SettlementType, List <SettlementShell> > > setPlace = DecideSettlementPlacements(KTS); foreach (KeyValuePair <int, Dictionary <SettlementType, List <SettlementShell> > > kingdomSets in setPlace) { foreach (KeyValuePair <SettlementType, List <SettlementShell> > setTypes in kingdomSets.Value) { foreach (SettlementShell ss in setTypes.Value) { CalculateSettlementEconomy(ss); SetAndTactShells.Add(ss); } } } }
private Settlement GenerateSettlement(SettlementShell ss) { SettlementBuilder2 builder = new SettlementBuilder2(GameGen.TerGen.GetWorldHeightAt, ss); GenerationRandom genRan = new GenerationRandom(GameGen.Seed + ss.ChunkPosition.GetHashCode()); builder.Generate(genRan); List <ChunkData> data = builder.ToChunkData(); lock (ChunkLock) { foreach (ChunkData cd in data) { PreGeneratedChunks.Add(cd.Position, cd); } } Settlement set = new Settlement(ss.GetKingdom(), "test set", builder); return(set); }
// Start is called before the first frame update void Start() { TestMain.SetupTest(); Player = TestMain.CreatePlayer(new Vector3(0, 0, 0)); Kingdom king = new Kingdom("kingdom", new Vec2i(0, 0)); World.Instance.AddKingdom(king); SettlementShell shell = new SettlementShell(new GridPoint(new Vec2i(0, 0), new Vec2i(0, 0)), 0, SettlementType.CAPITAL); bool[] entraceDir = new bool[8]; entraceDir[0] = true; entraceDir[3] = true; // entraceDir[4] = true; // entraceDir[6] = true; SettlementGenerator2.LocationData ld = new SettlementGenerator2.LocationData() { OnRiver = false, OnBorder = false, OnLake = false, EntranceDirections = entraceDir }; shell.SetLocationData(ld); int size = shell.Type.GetSize(); ChunkBase2[,] bases = new ChunkBase2[size, size]; for (int x = 0; x < size; x++) { for (int z = 0; z < size; z++) { bases[x, z] = new ChunkBase2(new Vec2i(x, z), WorldHeightChunk(x, z), ChunkBiome.grassland); if (z == 8) { bases[x, z].SetChunkFeature(new ChunkRiverNode(new Vec2i(x, z))); } } } List <BuildingPlan> reqBuild = new List <BuildingPlan>() { Building.VEGFARM, Building.WHEATFARM, Building.VEGFARM, Building.WHEATFARM }; shell.RequiredBuildings = reqBuild; shell.SetChunkBases(bases); Debug.BeginDeepProfile("Setgen"); SettlementBuilder2 setB = new SettlementBuilder2(WorldHeight, shell); setB.Generate(new GenerationRandom(0)); foreach (Building b in setB.Buildings) { if (b.BuildingSubworld != null) { World.Instance.AddSubworld(b.BuildingSubworld); } } Settlement set = new Settlement(king, "set", setB); World.Instance.AddLocation(set); EntityGenerator eg = new EntityGenerator(null, EntityManager.Instance); eg.GenerateAllKingdomEntities(); SettlementWall = setB.GenerateWall(); //wall = SettlementWall.WallPath.CalculateEvenlySpacedPoints(5, 1); Debug.EndDeepProfile("Setgen"); /* * int seed = 0; * SettlementBuilder setB = new SettlementBuilder(WorldHeight, new SettlementBase(new Vec2i(9, 9), 8, SettlementType.CAPITAL)); * setB.Generate(GameManager.RNG); * * Kingdom k = new Kingdom("king", new Vec2i(9, 9)); * Settlement set = new Settlement(k, "set", setB); * k.AddSettlement(set); * KingdomNPCGenerator npcGen = new KingdomNPCGenerator(null, k, EntityManager.Instance); * npcGen.GenerateSettlementNPC(set); */ List <ChunkData> chunks = setB.ToChunkData(); Chunks = new ChunkData[20, 20]; foreach (ChunkData cd in chunks) { if (cd == null) { continue; } Chunks[cd.X, cd.Z] = cd; EntityManager.Instance.LoadChunk(new Vec2i(cd.X, cd.Z)); } SetChunks(Chunks); return; }
public void SetPoint(GridPoint p) { bool isActive = false; this.p = p; Text = GetComponentInChildren <Text>(); if (p.Shell != null) { Button = GetComponent <Button>(); isActive = true; ColorBlock b = Button.colors; Color c = Color.black; if (p.Shell is SettlementShell) { SettlementShell ss = p.Shell as SettlementShell; if (ss.Type == SettlementType.CAPITAL) { c = Color.yellow; Text.text = "CA"; } else if (ss.Type == SettlementType.CITY) { c = Color.magenta; Text.text = "CI"; } else if (ss.Type == SettlementType.TOWN) { c = Color.blue; Text.text = "TO"; } else if (ss.Type == SettlementType.VILLAGE) { c = Color.green; Text.text = "VI"; } } else if (p.Shell is TacticalLocationShell) { TacticalLocationShell ss = p.Shell as TacticalLocationShell; if (ss.Type == TacLocType.fort) { c = Color.red; Text.text = "FO"; } else if (ss.Type == TacLocType.tower) { c = Color.cyan; Text.text = "TO"; } } else if (p.Shell is ChunkStructureShell) { ChunkStructureShell css = p.Shell as ChunkStructureShell; if (css.Type == ChunkStructureType.banditCamp) { c = Color.red; Text.text = "BA"; } else if (css.Type == ChunkStructureType.vampireNest) { c = new Color(0.5f, 0, 0.5f); Text.text = "VA"; } else if (css.Type == ChunkStructureType.kithenaCatacomb) { c = new Color(0, 0.5f, 0.5f); Text.text = "KI"; } else if (css.Type == ChunkStructureType.ancientTemple) { c = new Color(0, 0.5f, 0.5f); Text.text = "AT"; } else { c = Color.red; Text.text = "DA"; } } b.normalColor = c; Button.colors = b; } this.gameObject.SetActive(isActive); }
/// <summary> /// Calculates the economic item production per tick for this village based on the resources in the eclcosed chunks /// Adds building plans based on these production amounts /// Calculates the economic items used per tick /// Calculates the start inventory, and the desired amounts to keep in the inventory /// </summary> /// <param name="settlementResources"></param> /// <param name="shell"></param> private void GenerateVillageEconomy(Dictionary <ChunkResource, float> settlementResources, SettlementShell shell) { List <BuildingPlan> reqBuildings = new List <BuildingPlan>(); //A measure of how much of each resource is produced per tick Dictionary <EconomicItem, int> producePerTick = new Dictionary <EconomicItem, int>(); //How much of each item is used per tick Dictionary <EconomicItem, int> usePerTick = new Dictionary <EconomicItem, int>(); EconomicInventory economicInventory = new EconomicInventory(); Dictionary <EconomicItem, int> DesiredInventoryAmounts = new Dictionary <EconomicItem, int>(); //Villages only take raw production (of farms and wood) if (settlementResources.TryGetValue(ChunkResource.wheatFarm, out float v) && v > 1) { reqBuildings.Add(Building.WHEATFARM); foreach (EconomicItem it in ChunkResource.wheatFarm.GetEconomicItem()) { if (!producePerTick.ContainsKey(it)) { producePerTick.Add(it, 0); } producePerTick[it] += ((int)v * 10); } } if (settlementResources.TryGetValue(ChunkResource.vegetableFarm, out float v0) && v0 > 1) { reqBuildings.Add(Building.VEGFARM); foreach (EconomicItem it in ChunkResource.vegetableFarm.GetEconomicItem()) { if (!producePerTick.ContainsKey(it)) { producePerTick.Add(it, 0); } producePerTick[it] += ((int)v * 10); } } if (settlementResources.TryGetValue(ChunkResource.silkFarm, out float v1) && v1 > 1) { reqBuildings.Add(Building.SILKFARM); foreach (EconomicItem it in ChunkResource.silkFarm.GetEconomicItem()) { if (!producePerTick.ContainsKey(it)) { producePerTick.Add(it, 0); } producePerTick[it] += ((int)v1 * 4); } } if (settlementResources.TryGetValue(ChunkResource.cattleFarm, out float v2) && v2 > 1) { reqBuildings.Add(Building.CATTLEFARM); foreach (EconomicItem it in ChunkResource.cattleFarm.GetEconomicItem()) { if (!producePerTick.ContainsKey(it)) { producePerTick.Add(it, 0); } producePerTick[it] += ((int)v2); } } if (settlementResources.TryGetValue(ChunkResource.sheepFarm, out float v3) && v3 > 1) { reqBuildings.Add(Building.SHEEPFARM); foreach (EconomicItem it in ChunkResource.sheepFarm.GetEconomicItem()) { if (!producePerTick.ContainsKey(it)) { producePerTick.Add(it, 0); } producePerTick[it] += ((int)v3); } } if (settlementResources.TryGetValue(ChunkResource.wood, out float v4) && v4 > 1) { reqBuildings.Add(Building.WOODCUTTER); foreach (EconomicItem it in ChunkResource.wood.GetEconomicItem()) { if (!producePerTick.ContainsKey(it)) { producePerTick.Add(it, 0); } producePerTick[it] += ((int)v4); } } if (settlementResources.TryGetValue(ChunkResource.ironOre, out float v5) && v5 > 1) { reqBuildings.Add(Building.IRONMINE); foreach (EconomicItem it in ChunkResource.ironOre.GetEconomicItem()) { if (!producePerTick.ContainsKey(it)) { producePerTick.Add(it, 0); } producePerTick[it] += ((int)v5); } } if (settlementResources.TryGetValue(ChunkResource.silverOre, out float v6) && v6 > 1) { reqBuildings.Add(Building.SILVERMINE); foreach (EconomicItem it in ChunkResource.silverOre.GetEconomicItem()) { if (!producePerTick.ContainsKey(it)) { producePerTick.Add(it, 0); } producePerTick[it] += ((int)v6); } } if (settlementResources.TryGetValue(ChunkResource.goldOre, out float v7) && v7 > 1) { reqBuildings.Add(Building.GOLDMINE); foreach (EconomicItem it in ChunkResource.goldOre.GetEconomicItem()) { if (!producePerTick.ContainsKey(it)) { producePerTick.Add(it, 0); } producePerTick[it] += ((int)v7); } } usePerTick.Add(Economy.Bread, 5); usePerTick.Add(Economy.Vegetables, 5); usePerTick.Add(Economy.Clothes, 1); //Calculates the amount foreach (KeyValuePair <EconomicItem, int> kvp in usePerTick) { DesiredInventoryAmounts.Add(kvp.Key, kvp.Value * Economy.KEEP_IN_INVENTORY_MULT); economicInventory.AddItem(kvp.Key, DesiredInventoryAmounts[kvp.Key]); } shell.RequiredBuildings = reqBuildings; EconomyData data = new EconomyData(); data.Inventory = economicInventory; data.UsePerTick = usePerTick; data.RawProductionPerTick = producePerTick; data.DesiredItemStock = DesiredInventoryAmounts; data.EconomicProduction = new Dictionary <EconomicProduction, int>(); SettlementEconomy econ = new SettlementEconomy(shell, data); }
/// <summary> /// Decides the placement for all non capital settlements. /// /// <list type="bullet"> /// <item> /// We choose a valid grid point by accessing <see cref="DesireWeightedGridpoints"/>, /// and taking a weight random element from it. /// </item> /// <item>We then check which kingdom owns the territory of the point.</item> /// <item>We check the number of capitals, towns, and villages allowed by the kingdom, based on <paramref name="kts"/></item> /// <item>If they have not reached their maximum number of a settlement type, we place one here</item> /// <item>We continuously do this until all settlements for all kingdoms are found,</item> /// </list> /// </summary> /// <param name="kts"></param> private Dictionary <int, Dictionary <SettlementType, List <SettlementShell> > > DecideSettlementPlacements(Dictionary <int, KingdomTotalSettlements> kts) { //Key = kingdom ID //Value[0] = Dictionary ordered by settlement type, containing values representing the desired settlement placement Dictionary <int, Dictionary <SettlementType, List <SettlementShell> > > setPlace = new Dictionary <int, Dictionary <SettlementType, List <SettlementShell> > >(); //We create a data structure to hold each settlement foreach (Kingdom k in GameGen.KingdomGen.Kingdoms) { setPlace.Add(k.KingdomID, new Dictionary <SettlementType, List <SettlementShell> >()); setPlace[k.KingdomID].Add(SettlementType.CITY, new List <SettlementShell>()); setPlace[k.KingdomID].Add(SettlementType.TOWN, new List <SettlementShell>()); setPlace[k.KingdomID].Add(SettlementType.VILLAGE, new List <SettlementShell>()); } bool shouldContinue = true; while (shouldContinue) { //if no points remain, we break if (DesireWeightedGridpoints.Count == 0) { shouldContinue = false; break; } //We a random point GridPoint gp = DesireWeightedGridpoints.GetRandom(true); ChunkBase2 cb = GameGen.TerGen.ChunkBases[gp.ChunkPos.x, gp.ChunkPos.z]; int kingdomID = cb.KingdomID; if (kingdomID == -1) { continue; } //We have now got to a valid settlement point so we check what type of settlement we need; SettlementType setType = SettlementType.CITY; if (setPlace[kingdomID][SettlementType.CITY].Count < kts[kingdomID].CityCount) { setType = SettlementType.CITY; } else if (setPlace[kingdomID][SettlementType.TOWN].Count < kts[kingdomID].TownCount) { setType = SettlementType.TOWN; } else if (setPlace[kingdomID][SettlementType.VILLAGE].Count < kts[kingdomID].VillageCount) { setType = SettlementType.VILLAGE; } else { Debug.Log("Already completed set placement for " + kingdomID); continue; } //Find the shorest distance int distSqr = FindShortestSquareDistance(setPlace[kingdomID], gp); //the maximum distance int minDistSqr = GridPlacement.GridPointSize * GridPlacement.GridPointSize * 5; if (distSqr >= minDistSqr || distSqr < 0) { gp.HasSet = true; gp.SETYPE = setType; SettlementShell sp = new SettlementShell(gp, kingdomID, setType); gp.Shell = sp; setPlace[kingdomID][setType].Add(sp); } } return(setPlace); }
private void GenerateTownEconomy(Dictionary <ChunkResource, float> settlementResources, SettlementShell shell) { List <BuildingPlan> reqBuildings = new List <BuildingPlan>(); //A measure of how much of each resource is produced per tick Dictionary <EconomicItem, int> rawProductionPerTick = new Dictionary <EconomicItem, int>(); //A measure of how much can be produced by industry Dictionary <EconomicProduction, int> productionPerTick = new Dictionary <EconomicProduction, int>(); //How much of each item is used per tick (excluding economy) Dictionary <EconomicItem, int> usePerTick = new Dictionary <EconomicItem, int>(); EconomicInventory economicInventory = new EconomicInventory(); Dictionary <EconomicItem, int> desiredStock = new Dictionary <EconomicItem, int>(); usePerTick.Add(Economy.Bread, 15); usePerTick.Add(Economy.Vegetables, 10); usePerTick.Add(Economy.Clothes, 3); //Use a small amount of weapons and armour per tick usePerTick.Add(Economy.LeatherArmour, 1); usePerTick.Add(Economy.IronWeapons, 1); SettlementProductionAbility productionAbility = new SettlementProductionAbility(); //All towns will have a bakery, this will produce bread from wheat reqBuildings.Add(Building.BAKERY); productionPerTick.Add(Economy.WheatToBread, 50); economicInventory.AddItem(Economy.Wheat, 500); productionAbility.HasButcher = true; productionPerTick.Add(Economy.CowToBeef, 5); productionPerTick.Add(Economy.SheepToMutton, 5); //If we are in a forrest, then the town till cut wood & make into planks if (settlementResources.TryGetValue(ChunkResource.wood, out float v) && v > 1) { reqBuildings.Add(Building.WOODCUTTER); reqBuildings.Add(Building.LUMBERMILL); int rawProduce = (int)v * 2; rawProductionPerTick.Add(ChunkResource.wood.GetEconomicItem()[0], rawProduce); //Takes 1 log and make 3 planks //We set the production such that it wished to import as much wood as possible productionPerTick.Add(Economy.WoodLogToPlank, (int)(rawProduce * GenRan.Random(2, 4))); productionAbility.HasLumberMill = true; } bool hasSmelt = false; if (settlementResources.TryGetValue(ChunkResource.ironOre, out float v1) && v1 > 1) { hasSmelt = true; reqBuildings.Add(Building.SMELTER); reqBuildings.Add(Building.BLACKSMITH); //We add some planks to start so we can produce weapons from start economicInventory.AddItem(Economy.WoodPlank, 5000); rawProductionPerTick.Add(Economy.IronOre, (int)v1); productionAbility.HasSmelter = true; productionPerTick.Add(Economy.Iron_OreToBar, (int)(v1 * GenRan.Random(2, 4))); productionPerTick.Add(Economy.IronToWeapon, 3); productionPerTick.Add(Economy.LeatherToArmour, 3); } if (settlementResources.TryGetValue(ChunkResource.silverOre, out float v2) && v2 > 1) { if (!hasSmelt) { reqBuildings.Add(Building.SMELTER); } rawProductionPerTick.Add(Economy.SilverOre, (int)v2); productionAbility.HasSmelter = true; productionPerTick.Add(Economy.Silver_OreToBar, (int)(v2 * GenRan.Random(2, 4))); } if (settlementResources.TryGetValue(ChunkResource.goldOre, out float v3) && v3 > 1) { if (!hasSmelt) { reqBuildings.Add(Building.SMELTER); } rawProductionPerTick.Add(Economy.GoldOre, (int)v3); productionAbility.HasSmelter = true; productionPerTick.Add(Economy.Gold_OreToBar, (int)(v3 * GenRan.Random(2, 4))); } //Calculates the amount foreach (KeyValuePair <EconomicItem, int> kvp in usePerTick) { int amount = kvp.Value * Economy.KEEP_IN_INVENTORY_MULT; desiredStock.Add(kvp.Key, amount); economicInventory.AddItem(kvp.Key, amount); } EconomyData data = new EconomyData(); data.Inventory = economicInventory; data.UsePerTick = usePerTick; data.RawProductionPerTick = rawProductionPerTick; data.DesiredItemStock = desiredStock; data.EconomicProduction = new Dictionary <EconomicProduction, int>(); data.ProductionAbility = productionAbility; SettlementEconomy econ = new SettlementEconomy(shell, data); shell.RequiredBuildings = reqBuildings; //Set the economy }