public static Vec2i Rotate(Vec2i initial, Vec2i rotate) { float angle = Vector2.SignedAngle(Vector2.up, rotate.AsVector2()); Vector2 final = Quaternion.Euler(0, 0, angle) * initial.AsVector2(); return(Vec2i.FromVector2(final)); }
/// <summary> /// The main generation function for the whole game /// Creates a <see cref="GameGenerator"/> and then creates a world using <see cref="GameGenerator.GenerateWorld"/> /// Then generates all quests using <see cref="GameGenerator.GenerateQuests(World)"/> /// </summary> /// <param name="seed">Seed fed to the <see cref="GameGenerator"/> that defines the generation</param> private void GenerateGame(int seed) { //Initiate GameGenerator, then generate and set world //Debug.BeginDeepProfile("generate_world"); GameGenerator = new GameGenerator(seed); GameGenerator.GenerateWorld(WorldManager); GameGenerator.GenerateEntities(WorldManager.World); GameGenerator.GenerateDungeons(); GameGenerator.GenerateWorldMap(); QuestManager.SetQuests(GameGenerator.GenerateQuests(WorldManager.World)); Vec2i wpos = Vec2i.FromVector2(QuestManager.Unstarted[0].Initiator.GetNPC().Position2); //Vec2i wpos = WorldManager.World.GetChunkStructure(0).Position * World.ChunkSize + new Vec2i(2, 2); Vec2i wEntr = WorldManager.World.GetSubworld(1).WorldEntrance; TestSettle = QuestManager.Unstarted[0].Initiator.GetNPC().NPCKingdomData.GetSettlement(); Debug.Log(TestSettle); Vec2i playerStartreg = World.GetRegionCoordFromChunkCoord(World.GetChunkPosition(wpos)); ChunkRegionGenerator = GameGenerator.GenerateChunks(playerStartreg); GeneratePlayer(wpos); }
// Start is called before the first frame update void Start() { EntityFaction bandits = new EntityFaction("Bandits!"); for (int i = 0; i < 5; i++) { Vector2 pos = GameManager.RNG.RandomVector2(15, 25); Bandit b = new Bandit(); b.SetEntityFaction(bandits); b.MoveEntity(Vec2i.FromVector2(pos)); EntityManager.Instance.LoadEntity(b); } }
public Inventory GetSecondInventory() { if (SecondInventory.Inventory == null) { Vec2i playerPos = Vec2i.FromVector2(Player.Position2); //Get current object on player position WorldObjectData currentSpace = GameManager.WorldManager.World.GetWorldObject(playerPos); if (currentSpace == null) { //if there is no object here, we create one LootSack lootsack = new LootSack(playerPos); GameManager.WorldManager.AddNewObject(lootsack); SecondInventory.SetInventory(lootsack.GetInventory()); SecondInventory.gameObject.SetActive(true); return(SecondInventory.Inventory); } //If current tile is taken, we check all the surrounding tiles Vec2i[] surrounding = GameManager.WorldManager.World.EmptyTilesAroundPoint(playerPos); foreach (Vec2i v in surrounding) { WorldObjectData surSpace = GameManager.WorldManager.World.GetWorldObject(v); if (surSpace == null) { //if there is no object here, we create one LootSack lootsack = new LootSack(v); GameManager.WorldManager.AddNewObject(lootsack); SecondInventory.SetInventory(lootsack.GetInventory()); SecondInventory.gameObject.SetActive(true); return(SecondInventory.Inventory); } } //If there is no space near, we return null return(null); } else { //If the inventory is not null, we add to it return(SecondInventory.Inventory); } }
protected override void InternalTick() { if (Entity.EntityAI.CombatAI.AngleBetweenLookAndEntity(DamageSource) > Entity.fov * 0.7f) { Entity.GetLoadedEntity().LookTowardsPoint(DamageSource.Position2); } float timePassed = Time.time - StartTime; //if we have spent too long looking if (timePassed > LookTime) { //We check if we have already stopped looking if (CurrentTileTarget != StartPosition) { //if not, we set our target as the start position. CurrentTileTarget = StartPosition; Entity.GetLoadedEntity().LEPathFinder.SetTarget(CurrentTileTarget.AsVector2()); } else { //If our current position is close to our original position, then we are done if (Entity.TilePos.QuickDistance(StartPosition) < 4) { IsComplete = true; } } } else if (CurrentTileTarget == null || Entity.TilePos == CurrentTileTarget) { Vector2 direction = (DamageSource.Position2 - Entity.Position2).normalized; float dist = GameManager.RNG.RandomInt(2, 5); Vector2 target = Entity.Position2 + direction * dist; CurrentTileTarget = Vec2i.FromVector2(target); Entity.GetLoadedEntity().LEPathFinder.SetTarget(target); } }
/// <summary> /// Places a small amount of large paths /// </summary> private void PlaceMainPaths(int initialPathCount = 9) { //Calculate node map size MapNodeSize = TileSize / NodeSize; NodeMap = new bool[MapNodeSize, MapNodeSize]; BuildingMap = new bool[MapNodeSize, MapNodeSize]; Path = new bool[TileSize, TileSize]; AllNodes = new List <Vec2i>(); Vec2i startDir; Vec2i entranceToMid = Middle - Entrance; //Find the entrance direction if (Mathf.Abs(entranceToMid.x) > Mathf.Abs(entranceToMid.z)) { startDir = new Vec2i((int)Mathf.Sign(entranceToMid.x), 0); } else { startDir = new Vec2i(0, (int)Mathf.Sign(entranceToMid.z)); } //The start direction for our path EntranceSide = startDir; //Calculate a random length int length = GenerationRandom.RandomInt(Shell.Type.GetSize() / 2 - 1, Shell.Type.GetSize() - 1) * NodeSize; Vec2i nodeEntr = Vec2i.FromVector2(new Vector2((float)Entrance.x / NodeSize, (float)Entrance.z / NodeSize)) * NodeSize; //Place 'main road' AddPath(nodeEntr, startDir, length, 5, NodeMap, AllNodes, NodeSize, true); //We generate other initial paths for (int i = 0; i < initialPathCount - 1; i++) { GeneratePathBranch(AllNodes, width: 3); } }
private void OnDrawGizmos() { if (doPath) { if (epf == null) { epf = new EntityPathFinder(GameManager.PathFinder); } Path_ = null; if (epf.IsRunning) { epf.ForceStop(); } else { epf.FindPath(Player.TilePos, Player.TilePos + GameManager.RNG.RandomVec2i(-100, 100)); } /*Debug.Log("calculating path"); * GenerationRandom genRan = new GenerationRandom(Time.frameCount); * Path_ = GameManager.PathFinder.GeneratePath(Vec2i.FromVector3(Player.Position), Vec2i.FromVector3(Player.Position) + genRan.RandomVec2i(-100, 100)); */ return; Settlement t = GameManager.TestSettle; if (t == null) { return; } int curDist = -1; Vec2i pPos = Vec2i.FromVector2(Player.Position2); foreach (SettlementPathNode pn in t.tNodes) { if (pn == null) { continue; } if (NearestNode == null) { NearestNode = pn; curDist = Vec2i.QuickDistance(pn.Position + t.BaseCoord, pPos); } else { if (Vec2i.QuickDistance(pn.Position + t.BaseCoord, pPos) < curDist) { curDist = Vec2i.QuickDistance(pn.Position + t.BaseCoord, pPos); NearestNode = pn; } } } path = new List <SettlementPathNode>(); if (SettlementPathFinder.SettlementPath(NearestNode, t.IMPORTANT, out path, debug: true)) { Debug.Log("Path found!"); } Debug.Log("Path len: " + path.Count); } if (Path_ == null && epf != null) { if (epf.IsComplete()) { Path_ = epf.GetPath(); } } if (Path_ != null && Path_.Count > 1) { Color old = Gizmos.color; Gizmos.color = Color.yellow; //Debug.Log(Vec2i.ToVector3(path[0].Position + t.BaseCoord)); //Gizmos.DrawCube(Vec2i.ToVector3(path[0].Position + GameManager.TestSettle.BaseCoord), Vector3.one * 2); Gizmos.DrawCube(Vec2i.ToVector3(Path_[0]), Vector3.one * 5); Gizmos.DrawCube(Vec2i.ToVector3(Path_[Path_.Count - 1]), Vector3.one * 5); for (int i = 0; i < Path_.Count - 1; i++) { Gizmos.DrawLine(Vec2i.ToVector3(Path_[i]), Vec2i.ToVector3(Path_[i + 1])); Gizmos.DrawCube(Vec2i.ToVector3(Path_[i]), Vector3.one * 0.5f); } Gizmos.color = old; } return; if (NearestNode == null) { Settlement t = GameManager.TestSettle; if (t == null) { return; } int curDist = -1; Vec2i pPos = Vec2i.FromVector2(Player.Position2); foreach (SettlementPathNode pn in t.tNodes) { if (pn == null) { continue; } if (NearestNode == null) { NearestNode = pn; curDist = Vec2i.QuickDistance(pn.Position + t.BaseCoord, pPos); } else { if (Vec2i.QuickDistance(pn.Position + t.BaseCoord, pPos) < curDist) { curDist = Vec2i.QuickDistance(pn.Position + t.BaseCoord, pPos); NearestNode = pn; } } } path = new List <SettlementPathNode>(); if (SettlementPathFinder.SettlementPath(NearestNode, t.IMPORTANT, out path, debug: true)) { Debug.Log("Path found!"); } Debug.Log("Path len: " + path.Count); } }
public Vec2i[] ChooseStartChunks(int count, int minSep) { //The angle between map middle that each capital should approximately //be seperated by float thetaSep = 360f / count; float thetaOffset = GenRan.Random(0, 90); Vector2 middle = new Vector2(World.WorldSize / 2, World.WorldSize / 2); Vec2i[] caps = new Vec2i[count]; //We iterate each of the kingdom capital positions we wish to calculate for (int i = 0; i < count; i++) { //Theta compared to (1,0) anticlockwise float theta = thetaOffset + thetaSep * i; float dx = Mathf.Cos(theta * Mathf.Deg2Rad); float dz = Mathf.Sin(theta * Mathf.Deg2Rad); Vector2 delta = new Vector2(dx, dz); //We start at middle Vector2 current = middle; int minLength = 32; int maxLength = -1; //iterate out for (int l = minLength; l < World.WorldSize / 2; l++) { current = middle + delta * l; Vec2i curChunk = Vec2i.FromVector2(current); ChunkBase2 cb = GameGen.TerGen.ChunkBases[curChunk.x, curChunk.z]; if (cb.Biome == ChunkBiome.ocean) { //When we reach the ocean, we define this as the max distance away maxLength = l - 1; break; } } //Capital is random point between min length, and max length Vec2i capPoint = Vec2i.FromVector2(middle + GenRan.RandomInt(minLength, maxLength) * delta); caps[i] = capPoint; } return(caps); Vec2i[] dirs = new Vec2i[] { new Vec2i(1, 1), new Vec2i(-1, 1), new Vec2i(-1, -1), new Vec2i(1, -1) }; Vec2i mid = new Vec2i(World.WorldSize / 2, World.WorldSize / 2); for (int i = 100; i < 400; i++) { for (int j = 0; j < dirs.Length; j++) { if (caps[j] == null) { Vec2i p = mid + dirs[j] * i; ChunkBase2 b = GameGen.TerGen.ChunkBases[p.x, p.z]; if (b.Biome == ChunkBiome.ocean) { Vec2i p_ = mid + dirs[j] * (int)(0.75f * i); caps[j] = p_; GridPoint gp = GameGen.GridPlacement.GetNearestPoint(p_); } } } } return(caps); for (int i = 0; i < count; i++) { bool validPoint = false; while (!validPoint) { Vec2i pos = GenRan.RandomVec2i(0, World.WorldSize - 1); GridPoint p = GameGen.GridPlacement.GetNearestPoint(pos); if (p != null && p.IsValid) { if (i == 0) { caps[0] = p.ChunkPos; validPoint = true; } else { int minDist = -1; for (int j = 0; j < i; j++) { int sqrDist = p.ChunkPos.QuickDistance(caps[j]); if (minDist == -1 || sqrDist < minDist) { minDist = sqrDist; } } if (minDist < minSep * minSep) { caps[i] = p.ChunkPos; validPoint = true; } } } } Debug.Log(caps[i]); } return(caps); }
/* * private void FromRiverSource(Vec2i source, Vec2i mainDir) * { * * int i = 0; * Vec2i end = null; * Vec2i current = source; * //Ray cast from the source to find the ocean point at this rivers end * while(end == null) * { * i++; * current += mainDir; * if(ChunkBases[current.x, current.z].Biome == ChunkBiome.ocean) * { * end = current; * } * if (i > World.WorldSize) * return; * * } * i = 0; * * Vec2i last = source; * * current = source + mainDir; * bool isDone = false; * * Vector2 lastDir = (end - current).AsVector2().normalized; * Vector2 exactCurrent = current.AsVector2(); * * List<Vec2i> river = new List<Vec2i>(); * * while (!isDone) * { * * * * i++; * // Vector2 currentFlow = FlowField[current.x, current.z]; * * float fx = PerlinNoise(current.x, current.z, 36)*2 - 1; * float fz = PerlinNoise(current.x, current.z, 37)*2 - 1; * Vector2 noiseFlow = new Vector2(fx, fz); * Vector2 flowField = FlowField[current.x, current.z]; * Vector2 targetFlow = (end - current).AsVector2().normalized; * * Vector2 flow = (noiseFlow + 4 * targetFlow + 3 * flowField).normalized; * exactCurrent += flow; * current = Vec2i.FromVector2(exactCurrent); * int check = Mathf.Min(river.Count, 5); * bool isValid = true; * for(int j=0; j< check; j++) * { * if (river[river.Count - j - 1] == current) * isValid = false; * } * if (!isValid) * { * current += mainDir; * exactCurrent = current.AsVector2(); * } * * * if (ChunkBases[current.x, current.z].Biome == ChunkBiome.ocean) * isDone = true; * ChunkBases[current.x, current.z].SetChunkFeature(new ChunkRiverNode()); * river.Add(current); * if (i > 2048) * return; * * } * * * }*/ private void ReverseFlowRiver(Vec2i start, int length, int distSinceFork = 0) { //Give rivers a slight biase towards the middle of the map Vector2 bias = -(new Vector2(World.WorldSize / 2, World.WorldSize / 2) - start.AsVector2()).normalized; Vec2i current = start; Vector2 fullCurrent = current.AsVector2(); Vec2i last = Vec2i.FromVector2(fullCurrent + bias); List <FlowPoint> inputFlowPoints = new List <FlowPoint>(5); for (int i = 0; i < length; i++) { distSinceFork++; inputFlowPoints.Clear(); //if this chunk is a river node already, we have reached the end of this river branch if (ChunkBases[current.x, current.z].ChunkFeature is ChunkRiverNode) { //return; } //Add a river node here ChunkBases[current.x, current.z].SetChunkFeature(new ChunkRiverNode(current)); //We iterate each of the near chunks foreach (Vec2i v in Vec2i.OCT_DIRDIR) { //Coordinate of point of interest Vec2i p = v + current; //If this is the point we just came from, ignore it if (last == p) { continue; } Vector2 vFlow = FlowField[p.x, p.z]; //Find the coordinate that this point flows into Vec2i pointFlowPos = Vec2i.FromVector2(p.AsVector2() + vFlow); //Check if this point flows into our current point if (pointFlowPos == current) { FlowPoint fp = new FlowPoint(); fp.Pos = p; fp.Dir = vFlow; inputFlowPoints.Add(fp); } } //If no points flow here, then the river has reached an end (add lakes?) if (inputFlowPoints.Count == 0) { Debug.Log("zero flow"); Vector2 currentToLast = (current - last).AsVector2(); fullCurrent = fullCurrent - currentToLast; //Debug.Log("zero error..."); //return; } else if (inputFlowPoints.Count == 1) { Debug.Log("single flow"); fullCurrent = fullCurrent - inputFlowPoints[0].Dir; } else { if (distSinceFork < 40) { fullCurrent = fullCurrent - GenRan.RandomFromList(inputFlowPoints).Dir; Debug.Log("No fork - dist"); } else { Debug.Log("fork"); //If we are over 40, then we can create a fork //only 2 forks maximum while (inputFlowPoints.Count > 2) { inputFlowPoints.RemoveAt(GenRan.RandomInt(0, inputFlowPoints.Count)); } ReverseFlowRiver(inputFlowPoints[0].Pos, length - i, 0); ReverseFlowRiver(inputFlowPoints[1].Pos, length - i, 0); Debug.Log("forks"); return; } } last = new Vec2i(current.x, current.z); current = Vec2i.FromVector2(fullCurrent); /* * //We iterate all directions * Vector2 grad = (FlowField[current.x, current.z] + 0.1f * bias).normalized; * fullCurrent = fullCurrent - grad; * current = Vec2i.FromVector2(fullCurrent); */ } }
private void FromRiverSource(Vec2i source, Vec2i end, float startHeight = -1, int distSinceFork = 0, bool riverRaySearch = true) { int i = 0; if (startHeight < 0) { startHeight = ChunkBases[source.x, source.z].Height; } i = 0; Vec2i last = source; Vec2i mainDir = CalcDir(source, end); Vec2i current = source + mainDir; bool isDone = false; Vector2 exactCurrent = current.AsVector2(); List <RiverPoint> river = new List <RiverPoint>(); ChunkRiverNode previousNode = null; while (!isDone) { int distToEnd = end.QuickDistance(current); i++; /* * if(i%16 == 0 && riverRaySearch) * { * bool success = false; * //search up to 16 chunks away * for(int j=1; j<16; j++) * { * if (success) * break; * //search all 8 directions * foreach(Vec2i v in Vec2i.OCT_DIRDIR) * { * Vec2i p = current + v * j; * * if(ChunkBases[p.x,p.z].Biome == ChunkBiome.ocean || ChunkBases[p.x, p.z].ChunkFeature is ChunkRiverNode) * { * end = p; * success = true; * break; * } * } * } * }*/ // Vector2 currentFlow = FlowField[current.x, current.z]; float fx = PerlinNoise(current.x, current.z, 36) * 2 - 1; float fz = PerlinNoise(current.x, current.z, 37) * 2 - 1; Vector2 noiseFlow = new Vector2(fx, fz); Vector2 flowField = FlowField[current.x, current.z]; Vector2 targetFlow = (end - current).AsVector2().normalized; float targetFlowMult = distToEnd < 400 ? 4 * Mathf.Exp((400f - distToEnd) / 200f) : 4; Vector2 flow = (noiseFlow + targetFlowMult * targetFlow + 3.5f * flowField).normalized; exactCurrent += flow; current = Vec2i.FromVector2(exactCurrent); int check = Mathf.Min(river.Count, 5); bool isValid = true; for (int j = 0; j < check; j++) { if (river[river.Count - j - 1].Pos == current) { isValid = false; } } if (!isValid) { current += mainDir; exactCurrent = current.AsVector2(); } if (ChunkBases[current.x, current.z].Biome == ChunkBiome.ocean) { isDone = true; } if (ChunkBases[current.x, current.z].ChunkFeature is ChunkRiverNode) { isDone = true; //Shouldn't be null, but lets do a check anyway if (previousNode != null) { //Get the river node ChunkRiverNode endNode = ChunkBases[current.x, current.z].ChunkFeature as ChunkRiverNode; //Inform river nodes of flow endNode.FlowIn.Add(previousNode); previousNode.FlowOut.Add(endNode); ModifyRiverHeight(endNode); } if (GenRan.Random() < 0.5f) { PlaceLake(current, 8); } } if (current == end) { isDone = true; } ChunkRiverNode nextNode = new ChunkRiverNode(current); ChunkBases[current.x, current.z].SetChunkFeature(nextNode); if (previousNode != null) { nextNode.FlowIn.Add(previousNode); previousNode.FlowOut.Add(nextNode); } previousNode = nextNode; //If this chunk is too high, we modify it and the surrounding area if (ChunkBases[current.x, current.z].Height > startHeight) { ModifyRiverValleyHeight(current, startHeight); } else if (ChunkBases[current.x, current.z].Height < startHeight) { startHeight = ChunkBases[current.x, current.z].Height; } RiverPoint rp = new RiverPoint(); rp.Pos = current; rp.Flow = flow; river.Add(rp); if (i > 4096) { PlaceLake(current, 12); return; } /* * distSinceFork++; * * if(distSinceFork > 256) * { * * float p = Mathf.Exp(-distSinceFork/200f); * if(p < GenRan.Random()) * { * * Vec2i delta = GenRan.RandomVec2i(-64, 64); * * FromRiverSource(current + delta, current); * distSinceFork = 0; * } * * }*/ mainDir = CalcDir(current, end); } return; if (river.Count > 128) { int forkCount = Mathf.CeilToInt(river.Count / 128f); int jump = (int)(((float)river.Count) / forkCount); int index = GenRan.RandomInt(0, jump); for (int j = 0; j < forkCount; j++) { if (index > river.Count - 30) { return; } RiverPoint rp = river[index]; Vec2i forkEnd = rp.Pos; Vec2i delta = GenRan.RandomVec2i(-32, 32); Vector2 endToForkDir = (forkEnd - end).AsVector2().normalized; Vec2i forkStart = Vec2i.FromVector2(forkEnd.AsVector2() + endToForkDir * GenRan.Random(64, 128)) + delta; FromRiverSource(forkStart, forkEnd); index += jump + GenRan.RandomInt(-32, 32); } } }
/// <summary> /// Used to move the entity. We call <see cref="EntityManager.MoveEntity(Entity, int, Vec2i)"/>. /// If <paramref name="newWorldID"/> is 0, then we stay in the same world. /// </summary> /// <param name="position"></param> /// <param name="newWorldID"></param> public void MoveEntity(Vector2 position, int newWorldID = 0) { EntityManager.Instance.MoveEntity(this, newWorldID == 0 ? CurrentSubworldID : newWorldID, Vec2i.FromVector2(position)); }