public bool FollowPath() { if (EntityPath == null) { return(false); } Vec2i nexti_ = EntityPath.CurrentIndex(); if (nexti_ == null) { return(true); } Vector2 next = nexti_.AsVector2(); if (PositionWithinDistance(Entity.GetLoadedEntity().transform.position, next + new Vector2(0.5f, 0.5f), 0.1f)) { Vec2i nexti = EntityPath.NextIndex(); if (nexti == null) { return(true); } next = nexti.AsVector2(); } if (next != null && Entity.GetLoadedEntity() != null) { Entity.GetLoadedEntity().MoveTowards(next + new Vector2(0.5f, 0.5f)); Entity.GetLoadedEntity().LookTowardsPoint(next + new Vector2(0.5f, 0.5f)); return(false); } return(false); }
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> /// Default RunFromCombat results in the entity moving in directly the /// opposite direction from the current target. /// </summary> protected virtual void RunFromCombat(Vec2i combatPosition = null) { currentCombatTask = "running from combat"; //If we are currently running, we don't need to update if (IsRunningFromCombat) { return; } Entity.GetLoadedEntity()?.SpeechBubble.PushMessage(Entity + " is running from combat"); WorldManager.Instance.StartCoroutine(RunFromCombatCoolDown(5)); //We now define that we are running from combat IsRunningFromCombat = true; //If the combats position is not defined, then we set it to the entities position if (combatPosition == null) { combatPosition = Entity.TilePos; } Debug.Log("Running"); if (Entity.GetSubworld() != null) { Subworld sub = Entity.GetSubworld(); //TODO - sub if small? Debug.Log("In subworld, exit: " + (sub.Exit as WorldObjectData).Position); Entity.GetLoadedEntity().SpeechBubble.PushMessage("Running to subworld exit"); //WorldObject obj = (sub.Exit as WorldObjectData).LoadedObject; Entity.GetLoadedEntity().LEPathFinder.SetTarget((sub.Exit as WorldObjectData).Position, ExitThroughDoor, callbackArgs: new object[] { sub.Entrance, 0.5f }); } else { Debug.Log("not in subworld"); Vector2 movementDirection = Entity.Position2 - combatPosition.AsVector2(); //If our movement is 0, we define it to be in a random direction. if (movementDirection == Vector2.zero) { movementDirection = GameManager.RNG.RandomVector2(-1, 1).normalized; } //Define the target position as at least 2 chunks away Vector2 targetPosition = Entity.Position2 + movementDirection * 32; //Set the AI target Entity.GetLoadedEntity().LEPathFinder.SetTarget(targetPosition); } }
/// <summary> /// Creates a entity group that aims to travel from the start chunk to the end chunk /// </summary> /// <param name="startChunk"></param> /// <param name="endChunk"></param> /// <param name="entities"></param> public EntityGroup(Vec2i startChunk, List <Entity> entities = null, EconomicInventory inventory = null) { ShouldDestroy = false; StartChunk = startChunk; CurrentPosition = startChunk.AsVector2(); GroupEntityIDs = new List <int>(); GroupInventory = new Inventory(); EconomicInventory = inventory == null?new EconomicInventory(): inventory; if (entities != null) { foreach (Entity e in entities) { GroupEntityIDs.Add(e.ID); GroupInventory.AddAll(e.Inventory); CombatStrength += e.CombatManager.CalculateEntityCombatStrength(); } } }
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> /// Finds the next point on this entity groups path by travelling a distance /// based on /// </summary> /// <param name="movement">The amount of movement the entity should do. If this is -1, we set it to their current movement speed</param> /// <returns>The position the entity group will be at after this tick. /// Returns null if we are at the end of the path</returns> public Vec2i NextPathPoint(float movement = -1) { if (Path == null) { return(null); } Vec2i finalPoint = FinalPathPoint(); /* * if (Vec2i.QuickDistance(finalPoint, CurrentChunk) < 4) * { * CurrentPosition = finalPoint.AsVector2(); * return null; * }*/ if (movement <= 0) { movement = CurrentMovementSpeed; } if (movement < 0.5f) { //If the current chunk is the last point, we return null to imply we have ended our travels if (CurrentChunk == finalPoint) { return(null); } return(CurrentChunk); } //If the next point is out of bounds, then that means we are at the final point if (CurrentPathIndex + 1 >= Path.Count) { //This means there is no next path point return(null); } //If the next path point exists, we find it, and the direction from our current position to it Vec2i nextPoint = Path[CurrentPathIndex + 1]; Vector2 direction = (nextPoint.AsVector2() - CurrentPosition); //We find the size, and then normalise the direction float mag = direction.magnitude; direction /= mag; //If the next position is within our current movement if (mag + 0.3f < movement) { float rem = movement - mag; //We incriment to the next path point CurrentPathIndex++; //And find the path accordingly return(NextPathPoint(rem)); } //We find the new position CurrentPosition = CurrentPosition + direction * movement; return(CurrentChunk); }
protected override void InternalTick() { if (Entity.TilePos == TargetTile) { IsComplete = true; return; } int quickDist = QuickDistanceToPlayer(Entity); //If we are currently far from the player if (quickDist > (World.ChunkSize * 6) * (World.ChunkSize * 6)) { Debug.Log(Entity + " is far from player, can tp?"); //And the target position is far from the player if (Vec2i.QuickDistance(TargetTile, PlayerManager.Instance.Player.TilePos) > (World.ChunkSize * 6) * (World.ChunkSize * 6)) { Debug.Log(Entity + " target from player, will tp"); if (HasTaskLocation) { if (Location.SubworldWorldID != Entity.CurrentSubworldID) { //Then we teleport to our target position and world Entity.MoveEntity(TargetTile, Location.SubworldWorldID); } else { //Then we teleport to our target position. Entity.MoveEntity(TargetTile); } } else { //Then we teleport to our target position. Entity.MoveEntity(TargetTile); } IsComplete = true; return; } } if (HasTarget) { Debug.Log(Entity + " has GOTO target"); return; } //if no task location is set, we simply go to the desired tile position if (!HasTaskLocation) { Debug.Log(Entity + " Target set: " + TargetTile); Entity.GetLoadedEntity().LEPathFinder.SetTarget(TargetTile.AsVector2()); HasTarget = true; } else { Debug.Log("not far, with task loc"); //if we do have a location, we check the subworldID //if it is the current world, we simply walk there if (Entity.CurrentSubworldID == Location.SubworldWorldID) { Entity.GetLoadedEntity().LEPathFinder.SetTarget(TargetTile.AsVector2()); HasTarget = true; Debug.Log("In same world, going now"); } else { //If we are not in the current target subworld, we have 3 possible cases: //1) We are in the main World (-1) travelling to a subworld //2) We are in a subworld, travelling to the main world (-1) //3) We are in a subworld, and wish to travel to another subworld via the main world //Case 3 and 2 can be ignored, as we already automatically take entities from their subworlds //And place them outside if their AI brings them to a a target outside their subworld if (Entity.CurrentSubworldID == -1 && Location.SubworldWorldID != -1) { Subworld sub = WorldManager.Instance.World.GetSubworld(Location.SubworldWorldID); Debug.Log(Entity + " travelling from world to subworld at : " + (sub.Entrance as WorldObjectData).Position); Entity.GetLoadedEntity().LEPathFinder.SetTarget((sub.Entrance as WorldObjectData).Position, TravelThroughDoor, new object[] { sub.Entrance, 0.5f }); HasTarget = true; } } } if (!HasTaskLocation || Location.SubworldWorldID == Entity.CurrentSubworldID) { if (TargetTile.QuickDistance(Entity.TilePos) < 4) { Entity.GetLoadedEntity()?.SpeechBubble.PushMessage("At Pathfinding target"); IsComplete = true; } } }
/* * 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); } } }
public bool GeneratePath(Vec2i target) { Entity.GetLoadedEntity().LEPathFinder?.SetTarget(target.AsVector2()); Target = target; return(true); //if no target, null previous paths and return. if (target == null) { Debug.Log("Target is null - no path"); EntityPathTarget = null; EntityPath = null; return(false); } if (EPF == null) { //TODO - get free path finder from parent EPF = new EntityPathFinder(GameManager.PathFinder); } Vec2i tilePos = Vec2i.FromVector3(Entity.Position); /*if(GameManager.PathFinder.NodeValue(tilePos) > 100) * { * tilePos = Vec2i.FromVector3(Entity.Position + new Vector3(0.5f, 0, 0.5f)); * if(GameManager.PathFinder.NodeValue(tilePos) > 100) * { * Debug.Log("hm"); * } * }*/ //if we have no path if (EntityPath == null) { Debug.Log("[PathFinding] Attempting path from " + tilePos + " to " + target); //We check if one is being generated if (!EPF.IsRunning) { //If not, we start generating it EPF.FindPath(tilePos, target); return(false); } //Check if the path finder has completed its work if (EPF.IsComplete()) { //If the path is found, we set it and return true if (EPF.FoundPath) { EntityPathTarget = target; EntityPath = new EntityPath(EPF.GetPath()); return(true); } else { Debug.Log("Path from " + tilePos + " to " + target + " not found"); return(false); } } return(false); /* * //If there is no path, Attempt to generate it * List<Vec2i> path = GameManager.PathFinder.GeneratePath(tilePos, target); * if (path==null || path.Count == 0) * { * Debug.Log("Path from " + tilePos + " to " + target + " not found"); * return false; * } * EntityPathTarget = target; * EntityPath = new EntityPath(path); * return true;*/ } else if (EntityPath.Target == target) {//If the current path has the same target, return true return(true); } else { Debug.Log("[PathFinding] Attempting updated path " + tilePos + " to " + target); if (Vec2i.QuickDistance(EntityPath.Target, target) < 5) { //If the path exists but doesn't have the same target, but targets are close, we attempt to re-calculate the path return(EntityPath.UpdateTarget(target, EPF)); } else { EntityPath = null; //We check if one is being generated if (!EPF.IsRunning) { //If not, we start generating it EPF.FindPath(tilePos, target); return(false); }/* * List<Vec2i> newPath = GameManager.PathFinder.GeneratePath(tilePos, target); * if(newPath==null || newPath.Count == 0) * { * Debug.Log("No path found from " + tilePos + " to " + target + " for entity " + Entity); * return false; * } * EntityPath = new EntityPath(newPath); * return true;*/ } } return(false); }