protected TileFunctions FindEndTile(TileFunctions t) { Stack<TileFunctions> tempPath = new Stack<TileFunctions>(); TileFunctions next = t.parent; while (next != null) { tempPath.Push(next); next = next.parent; } if (tempPath.Count <= move) { //if the tile is within range this is the tile we move towards return t.parent; } TileFunctions endTile = null; for (int i = 0; i <= move; i++) { //if the tile is out of range use this tile endTile = tempPath.Pop(); } return endTile; }
/* public void DetectCharacter() * { * Collider2D DetectedObject = Physics2D.OverlapBox(transform.position, new Vector2(0.8f, 0.8f), 1f); * * if (DetectedObject.gameObject.tag != "Tile") * { * occupied = true; * detectedEnemy = DetectedObject; * } * else * { * occupied = false; * detectedEnemy = null; * } * }*/ public void CheckTile(Vector3 direction, TileFunctions target, bool attackable) { Vector3 halfExtents = new Vector3(0.25f, 0.25f); //creates a box to detect any objects with colliders, not sure what angle parameter does Collider2D[] colliders = Physics2D.OverlapBoxAll(transform.position + direction, halfExtents, 1f); foreach (Collider2D item in colliders) { TileFunctions tile = item.GetComponent <TileFunctions>(); if (tile != null && tile.walkable) { RaycastHit2D hit = Physics2D.Raycast(tile.transform.position, new Vector3(0, 0, -1), 1); if (hit.collider.tag == "Tile" || tile == target) { adjacencyList.Add(tile); } else { tile.occupied = true; adjacencyList.Add(tile); } } } }
public void FindNeighbors(TileFunctions target, bool attackable) { //TODO need to change how it resets so you can click on an enemy and player unit at the same time Reset(); CheckTile(new Vector2(0, 1), target, attackable); CheckTile(new Vector2(0, -1), target, attackable); CheckTile(Vector3.right, target, attackable); CheckTile(Vector3.left, target, attackable); }
public void ShowAttackRange(bool detectable, bool enemyRangeTile) { float rng = ((float)move + (float)attackRange) * 2.4f; Collider2D[] tilesInRange = Physics2D.OverlapBoxAll(gameObject.transform.position, new Vector2(rng, rng), 1f); Queue <TileFunctions> borderTiles = new Queue <TileFunctions>(); foreach (Collider2D tile in tilesInRange) { if (tile.gameObject.GetComponent <TileFunctions>()) { if (tile.gameObject.GetComponent <TileFunctions>().borderTile == true) { borderTiles.Enqueue(tile.gameObject.GetComponent <TileFunctions>()); } } } while (borderTiles.Count > 0) { TileFunctions dequeuedTile = borderTiles.Dequeue(); selectableTiles.Add(dequeuedTile); foreach (TileFunctions tile in dequeuedTile.adjacencyList) { if (!tile.visited) { tile.selectAttackable = true; if (dequeuedTile.borderTile || dequeuedTile.distance < attackRange) { if (tile.detectedEnemy && detectable && tile.detectedEnemy.gameObject.tag != gameObject.tag) { enemyDetected = true; tile.colorAttackable = true; } //TODO needs to be refactored if (dequeuedTile.borderTile == true) { TileFunctions ModifiedTile = TileSetFlags(tile, dequeuedTile, 1, true, false, true); borderTiles.Enqueue(ModifiedTile); } else { TileFunctions ModifiedTile = TileSetFlags(tile, dequeuedTile, 1 + dequeuedTile.distance, true, false, true); borderTiles.Enqueue(ModifiedTile); } } } } } }
public TileFunctions TileSetFlags(TileFunctions t, TileFunctions dequeuedTile, int distance, bool attackable = false, bool selectable = false, bool showAttackableTiles = false) { TileFunctions tile = t; tile.selectable = selectable; tile.attackable = attackable; tile.colorAttackable = showAttackableTiles; tile.visited = true; tile.parent = dequeuedTile; tile.distance = distance; return(tile); }
public void ShowEnemyAttackRangeTiles() { float rng = ((float)move + (float)attackRange) * 2.4f; Collider2D[] tilesInRange = Physics2D.OverlapBoxAll(gameObject.transform.position, new Vector2(rng, rng), 1f); Queue<TileFunctions> borderTiles = new Queue<TileFunctions>(); foreach (Collider2D tile in tilesInRange) { if (tile.gameObject.GetComponent<TileFunctions>()) { if (tile.gameObject.GetComponent<TileFunctions>().erBorderTile == true) { borderTiles.Enqueue(tile.gameObject.GetComponent<TileFunctions>()); } } } while (borderTiles.Count > 0) { TileFunctions dequeuedTile = borderTiles.Dequeue(); EnemyRangeTiles.Add(dequeuedTile); foreach (TileFunctions tile in dequeuedTile.adjacencyList) { if (!tile.erVisited) { if (dequeuedTile.erBorderTile || dequeuedTile.distance < attackRange) { if (dequeuedTile.erBorderTile == true) { tile.colorEnemyRange = true; tile.erVisited = true; tile.distance = 1; //tile.enemiesUsingTile.Add(gameObject); borderTiles.Enqueue(tile); } else { tile.colorEnemyRange = true; tile.erVisited = true; tile.distance = 1 + dequeuedTile.distance; //tile.enemiesUsingTile.Add(gameObject); borderTiles.Enqueue(tile); } } } } } }
public void RemoveAttackableTiles() { if (currentTile != null) { currentTile.current = false; currentTile = null; } foreach (TileFunctions tile in attackableTiles) { tile.ResetAttackable(); } attackableTiles.Clear(); }
public void RemoveSelectableTiles() { if (currentTile != null) { currentTile.current = false; currentTile = null; } foreach (TileFunctions tile in selectableTiles) { tile.Reset(); } selectableTiles.Clear(); }
public void MoveToTile(TileFunctions tile) { path.Clear(); tile.target = true; moving = true; TileFunctions next = tile; while (next != null) { path.Push(next); next = next.parent; } }
public void ShowEnemyRangeTiles(int range) { ComputeAdjacencyLists(null, false); GetCurrentTile(); Queue<TileFunctions> process = new Queue<TileFunctions>(); process.Enqueue(currentTile); currentTile.erVisited = true; while (process.Count > 0) { TileFunctions dequeuedTile = process.Dequeue(); EnemyRangeTiles.Add(dequeuedTile); if (dequeuedTile.distance < range && !dequeuedTile.borderTile) { foreach (TileFunctions tile in dequeuedTile.adjacencyList) { if (!tile.erVisited) { if (tile.occupied) { dequeuedTile.erBorderTile = true; } if (!tile.occupied) { tile.colorEnemyRange = true; tile.erVisited = true; tile.distance = 1 + dequeuedTile.distance; if (tile.distance == move) { tile.erBorderTile = true; } process.Enqueue(tile); } } } } } if (process.Count == 0) { ShowEnemyAttackRangeTiles(); } }
public void ComputeAdjacencyLists(TileFunctions target, bool attackable) { float rng = ((float)move + (float)attackRange) * 2.4f; Collider2D[] tilesInRange = Physics2D.OverlapBoxAll(gameObject.transform.position, new Vector2(rng, rng), 1f); foreach (Collider2D tile in tilesInRange) { if (tile.gameObject.GetComponent <TileFunctions>()) { TileFunctions startTile = tile.gameObject.GetComponent <TileFunctions>(); startTile.FindNeighbors(target, attackable); } } }
protected TileFunctions FindLowestF(List<TileFunctions> list) { TileFunctions lowest = list[0]; foreach (TileFunctions t in list) { if (t.f < lowest.f) { lowest = t; } } list.Remove(lowest); //returns tile with shortest distance return lowest; }
public TileFunctions GetTargetTile(GameObject target) { Vector3 playerTilePos = new Vector3(target.transform.position.x, target.transform.position.y, target.transform.position.z + 1); //Casts a ray from under the board to tile player character is standing on. //Only works because it ignores character layer, cant get raycast to not hit player character RaycastHit2D hit = Physics2D.Raycast(playerTilePos, new Vector3(0, 0, 1), 1f, 9); TileFunctions tile = null; if (hit.collider != null) { tile = hit.collider.GetComponent <TileFunctions>(); } return(tile); }
public void Reset() { actionColor.GetComponent <SpriteRenderer>().color = new Color32(0, 0, 0, 0); adjacencyList.Clear(); walkable = true; current = false; target = false; selectable = false; if (selectAttackable) { ResetAttackable(); } selectAttackable = false; borderTile = false; visited = false; parent = null; distance = 0; f = g = h = 0; }
public void TargetTileToTravel() { if (Input.GetMouseButtonUp(0) && !actionCompleted) { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit2D hit = Physics2D.Raycast(ray.origin, Vector2.up); if (hit) { if (hit.collider.tag == "Tile") { TileFunctions clickedTile = hit.collider.GetComponent <TileFunctions>(); if (clickedTile.selectable) { MoveToTile(clickedTile); } } } } }
//public bool selectableReady = true; public void SetTileDetectCharacter() { //reset = false; if (detectedTile == null) { //TODO give tiles their own layer detectedTile = Physics2D.OverlapBox(transform.position, new Vector3(0.8f, 0.8f, 0), 1f, 1); TileFunctions tile = detectedTile.gameObject.GetComponent <TileFunctions>(); tile.detectedEnemy = gameObject.GetComponent <Collider2D>(); } else if (new Vector2(detectedTile.transform.position.x, detectedTile.transform.position.y) != new Vector2(gameObject.transform.position.x, gameObject.transform.position.y)) { TileFunctions tileDetected = detectedTile.gameObject.GetComponent <TileFunctions>(); //needed for ai to turn occupied false for some reason tileDetected.occupied = false; tileDetected.detectedEnemy = null; detectedTile = null; } reset = true; }
public void Move() { if (attacking) { return; } //FindAttackAbleTiles(); //works just as well as AIDetectPlayers, but I think AiDetectPlayers might be less costly DetectPcsInAttackRange(); if (path.Count > 0) { //returns obj at the top of the stack without removing; TileFunctions nextTileInPath = path.Peek(); Vector3 target = nextTileInPath.transform.position; if (Vector3.Distance(transform.position, target) >= 0.05f) { //TODO find a way to travel to point without affecting z axis var targetPos = new Vector3(target.x, target.y, -1f); gameObject.transform.position = Vector2.MoveTowards(gameObject.transform.position, targetPos, moveSpeed * Time.deltaTime); } else { //temporary fix for z axis transform.position = new Vector3(target.x, target.y, -1f); path.Pop(); //so ai attacks player when in range if (detectedEnemies.Count > 0) { AiAttackAction(); } } } else { RemoveSelectableTiles(); moving = false; EndTurn(); } }
public void Move() { if (path.Count > 0) { //returns obj at the top of the stack without removing; TileFunctions nextTileInPath = path.Peek(); Vector3 target = nextTileInPath.transform.position; if (Vector3.Distance(transform.position, target) >= 0.05f) { //TODO find a way to travel to point without affecting z axis var targetPos = new Vector3(target.x, target.y, -1f); gameObject.transform.position = Vector2.MoveTowards(gameObject.transform.position, targetPos, moveSpeed * Time.deltaTime); } else { //temporary fix for z axis transform.position = new Vector3(target.x, target.y, -1f); path.Pop(); } } else { RemoveSelectableTiles(); moving = false; var unitMenu = unitMenuController.transform.GetChild(0).GetComponent <UnitMenu>(); turnManager.characterSelected = true; if (!attacking) { ToggleUnitMenu(true); } unitMenu.SetUnit(gameObject); } }
public void BFSTileMap(int range, bool detectable = false, bool selectable = false, bool attackable = false, bool enemyRangeTile = false) { if (detectable && enemyDetected) { return; } ComputeAdjacencyLists(null, attackable); GetCurrentTile(); Queue <TileFunctions> process = new Queue <TileFunctions>(); process.Enqueue(currentTile); currentTile.visited = true; while (process.Count > 0) { //remove and return the tile TileFunctions dequeuedTile = process.Dequeue(); selectableTiles.Add(dequeuedTile); if (attackable) { attackableTiles.Add(dequeuedTile); } if (dequeuedTile.distance < range && !dequeuedTile.borderTile) { //if unit is on border tile need to check both of its adjacency lists foreach (TileFunctions tile in dequeuedTile.adjacencyList) { if (!tile.visited) { if (tile.detectedEnemy != null && tile.detectedEnemy.tag != gameObject.tag && !tile.enemyAdded) { if (detectable) { enemyDetected = true; tile.colorAttackable = true; } if (attackable) { tile.enemyAdded = true; detectedEnemies.Add(tile); } } if (tile.occupied) { dequeuedTile.borderTile = true; } if (!tile.occupied || attackable) { TileFunctions ModifiedTile = TileSetFlags(tile, dequeuedTile, 1 + dequeuedTile.distance, attackable, selectable, false); if (ModifiedTile.distance == move) { ModifiedTile.borderTile = true; } process.Enqueue(ModifiedTile); } } } } } if (process.Count == 0) { ShowAttackRange(detectable, enemyRangeTile); } }
private void OnGameUpdate(EventArgs args) { Utils.TrySpawnForEachPlayer(TrySpawnCustomNpc); foreach (var npc in Main.npc.Where(n => n?.active == true)) { var customNpc = GetCustomNpc(npc); if (customNpc?.Definition.ShouldAggressivelyUpdate == true) { npc.netUpdate = true; } if (customNpc?.HasChildren == true) { customNpc?.UpdateChildren(); } if (customNpc?.HasTransformed == true) { var id = customNpc.Npc.whoAmI; customNpc.HasTransformed = false; var definition = customNpc.Definition; if (definition.OnTransformed != null) { try { CustomIDFunctions.CurrentID = definition.Name; definition.OnTransformed(customNpc); } catch (Exception ex) { Utils.LogScriptRuntimeError(ex); definition.OnTransformed = null; } TSPlayer.All.SendData(PacketTypes.NpcUpdate, "", id); TSPlayer.All.SendData(PacketTypes.UpdateNPCName, "", id); } } var npcId = npc.whoAmI; if (_checkNpcForReplacement[npcId]) { TryReplaceNpc(npc); _checkNpcForReplacement[npcId] = false; } } //player - npc collisions foreach (var player in TShock.Players.Where(p => p?.Active == true)) { var tplayer = player.TPlayer; var playerHitbox = tplayer.Hitbox; if (!tplayer.immune) { player.SetData(IgnoreCollisionKey, false); } foreach (var npc in Main.npc.Where(n => n?.active == true)) { var customNpc = GetCustomNpc(npc); if (customNpc != null) { var definition = customNpc.Definition; if (definition.OnCollision != null) { if (npc.Hitbox.Intersects(playerHitbox) && !player.GetData <bool>(IgnoreCollisionKey)) { try { CustomIDFunctions.CurrentID = definition.Name; definition.OnCollision(customNpc, player); } catch (Exception ex) { Utils.LogScriptRuntimeError(ex); definition.OnCollision = null; } //player.SetData(IgnoreCollisionKey, true); //break;//should this be a continue instead?? } } } } if (!tplayer.immune) { player.SetData(IgnoreCollisionKey, true); } } //npc - tile collisions foreach (var npc in Main.npc.Where(n => n?.active == true)) { var customNpc = GetCustomNpc(npc); if (customNpc != null) { var definition = customNpc.Definition; if (definition.OnTileCollision != null) { var tileCollisions = TileFunctions.GetOverlappedTiles(npc.Hitbox); if (tileCollisions.Count > 0) { try { CustomIDFunctions.CurrentID = definition.Name; definition.OnTileCollision(customNpc, tileCollisions); } catch (Exception ex) { Utils.LogScriptRuntimeError(ex); definition.OnTileCollision = null; } } } } } }
public void GetCurrentTile() { currentTile = GetTargetTile(gameObject); currentTile.current = true; }
//private void onProjectileSetDefaults(SetDefaultsEventArgs<Projectile,int> args) //{ // var projectile = args.Object; // var customProjectile = GetCustomProjectile(projectile); // if(customProjectile!=null) // { // Debug.Print("onProjectileSetDefaults!"); // var definition = customProjectile.Definition; // definition.ApplyTo(projectile); // lock( locker ) // { // Utils.TryExecuteLua(() => definition.OnSpawn?.Call(customProjectile), definition.Name); // } // TSPlayer.All.SendData(PacketTypes.ProjectileNew, "", projectile.whoAmI); // args.Handled = true; // } //} private HookResult OnProjectilePreUpdate(Projectile projectile, ref int index) { var result = HookResult.Continue; var customProjectile = GetCustomProjectile(projectile); if (customProjectile == null) { return(HookResult.Continue); } var definition = customProjectile.Definition; var lastTimeLeft = projectile.timeLeft; //we capture this to help determine whether we need to decrement timeLeft at the end of this method. //terraria has many points where it sets timeLeft internally, but our custom proj modifies whether/when those points run. //by the end of this method we hopefully have enough information to tell if terraria modified it, or if we need to do it ourselves. //game updates var onGameUpdate = definition.OnGameUpdate; if (customProjectile.Active && onGameUpdate != null) { try { CustomIDFunctions.CurrentID = definition.Name; var handled = onGameUpdate(customProjectile); result = handled == true ? HookResult.Cancel : HookResult.Continue; if (result == HookResult.Cancel) { //if we dont pass execution onto Terraria's Projectile.Update(), AI() will never get run, so we better run it ourselves. projectile.AI(); } customProjectile.SendNetUpdate = true; } catch (Exception ex) { Utils.LogScriptRuntimeError(ex); definition.OnGameUpdate = null; } } //collision tests //players if (customProjectile.Active && definition.OnCollision != null) { foreach (var player in TShock.Players) { if (player?.Active == true) { var onCollision = definition.OnCollision; if (onCollision != null) { var tplayer = player.TPlayer; var playerHitbox = tplayer.Hitbox; if (!tplayer.immune && projectile.Hitbox.Intersects(playerHitbox)) { try { CustomIDFunctions.CurrentID = definition.Name; onCollision(customProjectile, player); customProjectile.SendNetUpdate = true; } catch (Exception ex) { Utils.LogScriptRuntimeError(ex); definition.OnCollision = null; } } } } } } //tiles if (customProjectile.Active && projectile.tileCollide) { // this is a bit convoluted, because of the 2 conditions-- player wants to run custom code on tile collisions and/or player isn't allowing terraria // to run Update(), thus the projectile wont be killed in a timely manner. See condition below for result == HookResult.Cancel if (definition.OnTileCollision != null || result == HookResult.Cancel) { var tileCollisions = TileFunctions.GetOverlappedTiles(projectile.Hitbox); if (tileCollisions.Count > 0) { var killProjectile = false; //if terrarias code won't be running Update(and thus AI() ), we should kill the projectile ourselves if we hit any applicable tile. if (result != HookResult.Continue) { //...we have to scan the list before the player does, to ensure they dont modify anything(we shouldn't have switched from ReadOnlyCollection. ) foreach (var hit in tileCollisions) { if (TileFunctions.IsSolidOrSlopedTile(hit.X, hit.Y) || (!(definition.BaseOverride.IgnoreWater == true) && TileFunctions.IsLiquidTile(hit.X, hit.Y))) { killProjectile = true; break; } } } try { CustomIDFunctions.CurrentID = definition.Name; definition.OnTileCollision?.Invoke(customProjectile, tileCollisions); //customProjectile.SendNetUpdate = true; } catch (Exception ex) { Utils.LogScriptRuntimeError(ex); definition.OnTileCollision = null; } //script hasnt killed projectile, but we did hit a foreground tile, so lets kill it ourselves if (customProjectile.Active && killProjectile == true) { customProjectile.Kill(); } } } } //We need to decrement timeLeft ourselves if no other code has, and no code run after this point will do so if (customProjectile.Active && result == HookResult.Cancel && customProjectile.TimeLeft == lastTimeLeft) { customProjectile.TimeLeft--; if (customProjectile.TimeLeft < 1) { customProjectile.Kill(); } } if (customProjectile.Active && customProjectile.SendNetUpdate) { SendProjectileUpdate(customProjectile.Index); } return(result); }
public void FindPath(TileFunctions target) { ComputeAdjacencyLists(target, false); GetCurrentTile(); List<TileFunctions> openList = new List<TileFunctions>(); List<TileFunctions> closedList = new List<TileFunctions>(); openList.Add(currentTile); currentTile.h = Vector3.Distance(currentTile.transform.position, target.transform.position); currentTile.f = currentTile.h; //29:14 part 6: explains the three steps of A* while (openList.Count > 0) { TileFunctions lowest = FindLowestF(openList); closedList.Add(lowest); if (lowest == target) { actualTargetTile = FindEndTile(lowest); MoveToTile(actualTargetTile); return; } foreach (TileFunctions tile in lowest.adjacencyList) { if (tile == target || !tile.occupied) { if (closedList.Contains(tile)) { //do nothing, already processed } else if (openList.Contains(tile)) { //this case is triggered when multiple paths exist in the open list, and compares them to find the shortest path among them //determines which path is better if multiple exists float tempG = lowest.g + Vector3.Distance(tile.transform.position, lowest.transform.position); if (tempG < tile.g) { tile.parent = lowest; tile.g = tempG; tile.f = tile.g + tile.h; } } else { tile.parent = lowest; //distance to the beginning //the g of the parent plus the distance to the parent tile.g = lowest.g + Vector3.Distance(tile.transform.position, lowest.transform.position); //estimated distance to the end tile.h = Vector3.Distance(tile.transform.position, target.transform.position); tile.f = tile.g + tile.h; openList.Add(tile); } } } } //18:20 part 6 //triggers when there is no path to the target //TODO need to modify so that player gets as close as possible even when there is no path EndTurn(); Debug.Log("Path not found"); }
public void CalculatePath() { TileFunctions targetTile = GetTargetTile(target); FindPath(targetTile); }