//colours and returns abialable tiles a selected unit can move to List <Terrain> UnitMove(Terrain Start, RPGClass Unit, GameObject[,,] Map) { //create the list I will eventually return, add the starting tile because of course a unit can go there List <Terrain> available = new List <Terrain> { Start }; int movement = Unit.Stats[11].dynamicValue + 1; //temp. Get all the tiles surrounding the start without adjusting for movement cost or obstructions for (int i = 0; i < movement; i++) { for (int j = -i; j < i; j++) { int k = Mathf.Abs(j) - i; int a = Start.x - j; int b = Start.y + k; for (int doTwice = 0; doTwice < 2; doTwice++) { if (a >= 0 && a < Map.GetLength(0) && b >= 0 && b < Map.GetLength(1)) { //Terrain t = Map[a, b, 0].GetComponent<Terrain>(); //if (!available.Contains(t) && t.Tag != Terrain.tileTag.Wall && aStarPathfinding(Start, t, Unit, Map) ) //{ // available.Add(Map[a, b, 0].GetComponent<Terrain>()); //} } if (doTwice == 0) { k = -Mathf.Abs(j) + i; a = Start.x + j; b = Start.y + k; } } } } return(available); /* * List<int> coordinates = new List<int> { }; * * int x = Location.x; * int y = Location.y; * GameObject aUI; * maxRange++; * * //for unit movement, minrange will always equal 0 since a unit can always at least travel to the tile they are on * for (int i = minRange; i < maxRange; i++) * { * for (int j = -i; j < i; j++) * { * //this algorithm does not acount for varying costs of moving across a tile or tiles that can not be crossed * int k = Mathf.Abs(j) - i; * aUI = Instantiate(AttackUI, new Vector3(x - j, y + k), transform.rotation); * aUI.GetComponent<DestroyOnBoolNotReset>().flagChecked = true; * * k = ((x - j) * 1000) + (y + k); * coordinates.Add(k); * * * k = -Mathf.Abs(j) + i; * aUI = Instantiate(AttackUI, new Vector3(x + j, y + k), transform.rotation); * aUI.GetComponent<DestroyOnBoolNotReset>().flagChecked = true; * * k = ((x - j) * 1000) + (y + k); * coordinates.Add(k); * } * } * * return coordinates; */ }
//find the shortest path to every tile in range protected Terrain[] DijkstraPath(Terrain Start, RPGClass Unit, GameObject[,,] map) { List <Terrain> visited = new List <Terrain> { Start }; //this list contains the tiles that form the shortest path List <Terrain> unvisited = new List <Terrain> { }; //this list contains the tiles being considered to add to the closed list int move = Unit.Stats[11].dynamicValue; Terrain mostRecent = Start; Terrain temp; Start.H = 0; do { //Add adjacent tiles to the list for (int i = 0; i < 4; i++) { temp = null; switch (i) { case 0: if (mostRecent.x + 1 < map.GetLength(0)) { temp = map[mostRecent.x + 1, mostRecent.y, 0].GetComponent <Terrain>(); } break; //right tile case 1: if (mostRecent.x - 1 >= 0) { temp = map[mostRecent.x - 1, mostRecent.y, 0].GetComponent <Terrain>(); } break; //left tile case 2: if (mostRecent.y + 1 < map.GetLength(1)) { temp = map[mostRecent.x, mostRecent.y + 1, 0].GetComponent <Terrain>(); } break; //above tile case 3: if (mostRecent.y - 1 >= 0) { temp = map[mostRecent.x, mostRecent.y - 1, 0].GetComponent <Terrain>(); } break; //below tile } //if an existing tile was found if (temp != null) { temp.F = Unit.AdjustTileMovementCost(temp); //ignore the tile if i have already visited it, or it is a wall, or there is not enough movement to pay for it if (!visited.Contains(temp) && temp.Type != Terrain.Tile.Wall && mostRecent.H + temp.F <= move) { //if the checked tile is already on the unvisited list, check to see if this new path is shorter if (unvisited.Contains(temp)) { if (mostRecent.H + temp.F < temp.H) { temp.H = mostRecent.H + temp.F; temp.parent = mostRecent; } } else //otherwise, add it to the unvisited list { unvisited.Add(temp); //add to unvisited temp.H = mostRecent.H + temp.F; //update H for future comparison temp.parent = mostRecent; //update parent so a path can be formed as needed } } } //temp check } //for loop //sort list (lazily) so that the first result has the smallest H value for (int i = 1; i < unvisited.Count; i++) { if (unvisited[i].H < unvisited[0].H) { temp = unvisited[0]; unvisited[0] = unvisited[i]; unvisited[i] = temp; } } visited.Add(unvisited[0]); //add the unvisted tile with the smallest F value to the visited list unvisited.Remove(unvisited[0]); //remove the added tile from the unvisited list mostRecent = visited.Last <Terrain>(); //update the mostrecent tile } while (unvisited.Count > 0); //if the cost of moving becomes greater than the avaialable movement, stop looping //reset the h values for future pathfindings... parent should not be reset so that it can be used in future. for (int i = 0; i < visited.Count; i++) { visited[i].H = Mathf.Infinity; } return(visited.ToArray()); }
//A little state machine to handle different actions depending on what is being done with the selected unit private void StateMachine(RPGClass Unit) { switch (Unit.CurrentState) { case Character._State.Selected: //Put up some indicators for movement, current pathm and attackable tiles Path = UpdateCurrentPath(LevelMap[Cursor.x, Cursor.y], Unit, Path, AvailableTilesForTravel); DisplayIndicator(Indicators[1], AvailableTilesForTravel); DisplayIndicator(Indicators[0], Path); DisplayIndicator(Indicators[2], RedTiles); //If the player wants to confirm movement, move the unit to the last tile on the path if (inputs.CheckSelectDown()) { RPGClass temp = Cursor.GetUnitAtCursorPosition(); if (temp == null || temp == SelectedUnit) { Unit.CurrentState = Character._State.SelectingAction; Unit.x = Path[Path.Length - 1].x; Unit.y = Path[Path.Length - 1].y; AvailableTilesForAttack = GetRangeAtPoint(Unit.CombatParameters.EquipedWeapon, Path[Path.Length - 1], LevelMap); SoundPlayer.clip = SelectNoise; SoundPlayer.Play(); } } //If the player wants to cancel the selection, deselect the unit if (inputs.CheckCancelDown()) { DeselectUnit(Character._State.Idle); SoundPlayer.clip = CancelNoise; SoundPlayer.Play(); } break; case Character._State.SelectingAction: //Put up some indicators for what is in range DisplayIndicator(Indicators[2], AvailableTilesForAttack); //If the player wants to confirm an action if (inputs.CheckSelectDown()) { RPGClass TempUnit = Cursor.GetUnitAtCursorPosition(); if (TempUnit != null) { //If the player clicks the selected unit they will begin waiting if (TempUnit == Unit) { DeselectUnit(Character._State.Waiting); SoundPlayer.clip = SelectNoise; SoundPlayer.Play(); } //If the player clicks an enemy unit, combat will begin else if (!TempUnit.CompareTag(SelectedUnit.tag) && PathContains(LevelMap[TempUnit.x, TempUnit.y], AvailableTilesForAttack)) { SelectedUnit.CurrentState = Character._State.InCombat; //Set up the combat Manager with new stats CombatManager cManager = gameObject.GetComponent <CombatManager>(); cManager.InitializeCombatParameters(Unit, TempUnit, LevelMap[Unit.x, Unit.y], LevelMap[TempUnit.x, TempUnit.y]); //cManager.StartCombat(); SoundPlayer.clip = SelectNoise; SoundPlayer.Play(); } } } //If the player wants to cancel their move and put the unit back at the starting tile if (inputs.CheckCancelDown()) { Unit.x = Path[0].x; Unit.y = Path[0].y; SetSelectedUnit(Unit, LevelMap); //If I don't recalculate all of the pathfinding, the game crashes SoundPlayer.clip = CancelNoise; SoundPlayer.Play(); } break; case Character._State.InCombat: getCombatInfo menu = FindObjectOfType <getCombatInfo>(); CombatManager cMan = GetComponent <CombatManager>(); RPGClass tempUnit; if (Unit != cMan.LeftSide.Unit) { tempUnit = cMan.LeftSide.Unit; } else { tempUnit = cMan.RightSide.Unit; } DisplayIndicator(Indicators[2], LevelMap[tempUnit.x, tempUnit.y]); //End combat after it has been run all the way if (!cMan.SimulateCombat) { if (menu.transform.parent.name == menu.DisabledView.name) { DeselectUnit(Character._State.Waiting); } //Unit.CurrentState = Character._State.Waiting; } //on submit action, start combat (only while not already in combat) if (inputs.CheckSelectDown()) { cMan.StartCombat(); SoundPlayer.clip = SelectNoise; SoundPlayer.Play(); } //on cancel action, go back 1 state if (inputs.CheckCancelDown()) { Unit.CurrentState = Character._State.SelectingAction; menu.CloseMenu(); SoundPlayer.clip = CancelNoise; SoundPlayer.Play(); } } break; default: //If the selected unit gets put in an unintended state, deselect them and make them wait print("Defaulted. Previous State: " + Unit.CurrentState.ToString()); DeselectUnit(Character._State.Waiting); break; } }
public CombatStats(RPGClass Unit, Weapons Weapon) { //Set up stuff from parameters UnitReference = Unit; EquipedWeapon = Weapon; //Rip stats from unit Health = Unit.Stats[(int)Stat.HitPoints].dynamicValue; Stress = Unit.Stats[(int)Stat.StressPoints].dynamicValue; Defense = Unit.Stats[(int)Stat.Defense].dynamicValue; Resistance = Unit.Stats[(int)Stat.Resistance].dynamicValue; CriticalDodge = Unit.Stats[(int)Stat.Luck].dynamicValue; if (EquipedWeapon == null) { DamageType = Weapons.Damage.Physical; HitChance = CritChance = Attack = 0; AttackSpeed = Unit.Stats[(int)Stat.Speed].dynamicValue; Dodge = (AttackSpeed * 2) + Unit.Stats[(int)Stat.Luck].dynamicValue; } else { DamageType = Weapon.DamageType; //Calculate Hit and Crit Chance HitChance = (Unit.Stats[(int)Stat.Skill].dynamicValue * 2) + (Unit.Stats[(int)Stat.Luck].dynamicValue / 2) + Weapon.HitChance; CritChance = (Unit.Stats[(int)Stat.Skill].dynamicValue * 2) + (Unit.Stats[(int)Stat.Luck].dynamicValue / 4) + Weapon.CritChance; //Calculate Damage, Physical attacks use strength while magical attacks use magic if (DamageType == Weapons.Damage.Physical) { Attack = Unit.Stats[(int)Stat.Strength].dynamicValue + Weapon.Might; } else if (DamageType == Weapons.Damage.Magical) { Attack = Unit.Stats[(int)Stat.Magic].dynamicValue + Weapon.Might; } else { Attack = 0; } //If a weapon is too heavy, give a penalty to speed. Otherwise, just use speed if (Unit.Stats[(int)Stat.Bulk].dynamicValue < Weapon.Weight) { AttackSpeed = Unit.Stats[(int)Stat.Speed].dynamicValue + (Unit.Stats[(int)Stat.Bulk].dynamicValue - Weapon.Weight); } else { AttackSpeed = Unit.Stats[(int)Stat.Speed].dynamicValue; } //Determine Dodge based off of the (potentially) modified speed stat Dodge = (AttackSpeed * 2) + Unit.Stats[(int)Stat.Luck].dynamicValue; //Determine Weapon Bonuses, Reward some combination of extra damage or hit damage //Check Weapon type first as different bonuses are rewarded to different weapons. Then Check weapon rank, higher rank = better bonus Weapons.Rank CharacterWeaponRank = Unit.WeaponStats[(int)Weapon.WeaponCategory].WeaponRank; switch (Weapon.WeaponCategory) { case (Weapons.WeaponType.Sword): case (Weapons.WeaponType.Staff): switch (CharacterWeaponRank) { case (Weapons.Rank.C): Attack += 1; break; case (Weapons.Rank.B): Attack += 2; break; case (Weapons.Rank.A): case (Weapons.Rank.S): Attack += 3; break; } break; case (Weapons.WeaponType.Lance): case (Weapons.WeaponType.Bow): case (Weapons.WeaponType.Arcane): case (Weapons.WeaponType.Divine): case (Weapons.WeaponType.Occult): switch (CharacterWeaponRank) { case (Weapons.Rank.C): Attack += 1; break; case (Weapons.Rank.B): Attack += 1; HitChance += 5; break; case (Weapons.Rank.A): case (Weapons.Rank.S): Attack += 2; HitChance += 5; break; } break; case (Weapons.WeaponType.Axe): switch (CharacterWeaponRank) { case (Weapons.Rank.C): HitChance += 5; break; case (Weapons.Rank.B): HitChance += 10; break; case (Weapons.Rank.A): case (Weapons.Rank.S): HitChance += 15; break; } break; } } }
//Updates the path a unit will travel Terrain[] UpdateCurrentPath(Terrain NewTile, RPGClass Unit, Terrain[] CurrentPath, Terrain[] AvailableTiles) { //If the unit passed doesn't exsist, return a null if (Unit == null) { return(null); } //Check if the new tile is a tile that could be traveled to if (!PathContains(NewTile, AvailableTiles)) { return(CurrentPath); } //Check if tile is the last tile on the path else if (NewTile == CurrentPath[CurrentPath.Length - 1]) { return(CurrentPath); } //Check if the new tile is already on the current path else if (PathContains(NewTile, CurrentPath)) { return(ShortenPath(NewTile, CurrentPath)); } //Check if the new tile is adjacent to the last tile on the path else if (AdjacentTiles(NewTile, CurrentPath[CurrentPath.Length - 1])) { //Calculate the movement cost needed to add the tile int MoveCost = Unit.AdjustTileMovementCost(NewTile); for (int i = 1; i < CurrentPath.Length; i++) { MoveCost += Unit.AdjustTileMovementCost(CurrentPath[i]); } //Add the tile if it there is enough movement for it if (MoveCost <= Unit.Stats[(int)RPGClass.Stat.Move].dynamicValue) { Terrain[] NewPath = new Terrain[CurrentPath.Length + 1]; for (int j = 0; j < CurrentPath.Length; j++) { NewPath[j] = CurrentPath[j]; } NewPath[CurrentPath.Length] = NewTile; return(NewPath); } //if there is not enough movement to pay for the tile, return a new shorter path else { return(ShortenPath(NewTile, CurrentPath[0])); } } //If all other checks fail, return the shortest path else { //print("all checks failed"); Terrain[] temp = ShortenPath(NewTile, CurrentPath[0]); if (temp == null) { return(CurrentPath); } else { return(temp); } } }