void ActionButtonPressed(Button button) { if (button == this.uiRefs.moveButton) { this.SetState(BattleState.MoveAction); } else if (button == this.uiRefs.setTargetButton) { this.SetState(BattleState.SetTargetAction); } else if (button == this.uiRefs.fireNowButton) { BattleMech mech = this.selectedTile.mech; var move = new BattleMove.StandingFire(); move.mechIndex = mech.tile.index; move.targetMechIndex = mech.target.tile.index; this.ExecuteMove(move); this.UpdateRightPanel(); this.UpdateTargetTile(mech); if (mech.target) { float newAP = mech.actionPoints - mech.GetAPCostForStandingFire().ap; this.UpdateActionPointsPreview(newAP); } this.SetState(BattleState.EndOfAction); } }
void DestroyMech(BattleMech mechToDestroy) { Debug.Log(mechToDestroy.name + " destroyed!"); BattleTile tile = mechToDestroy.tile; // Remove the mech and all references to it (such as teams and targets). tile.mech = null; mechToDestroy.team.mechs.Remove(mechToDestroy); mechToDestroy.isDestroyed = true; foreach (BattleTeam team in this.teams) { foreach (BattleMech mech in team.mechs) { if (mech.target == mechToDestroy) { mech.target = null; } } } Destroy(mechToDestroy.gameObject); // Change the map TileData tileData = GameData.GetTile("Destroyed mech"); tileData.groundMaterial = tile.data.groundMaterial; tile.Recreate(tileData); this.UpdateFogOfWar(); }
void ActionToggleChanged(Toggle toggle) { if (toggle == this.uiRefs.fireAutoToggle) { BattleMech mech = this.selectedTile.mech; mech.fireAuto = toggle.isOn; } }
// TODO: State dependent behaviour for when a tile can't be clicked bool CanClickTile() { BattleTile tile = this.hoveredTile; if (this.state == BattleState.SelectingAction) { return(true); } else if (this.state == BattleState.MoveAction) { if ( tile == null || tile == this.selectedTile || tile.mech != null || tile.data.allowsMovement == false || this.CanTeamSeeTile(this.selectedTile.mech.team, tile) == false ) { return(false); } if (this.moveActionData.pathingResult == null) { this.moveActionData.pathingResult = this.pathNetwork.FindPath( this.selectedTile, tile ); } if (this.moveActionData.pathingResult.Value.isValid == false) { return(false); } BattleMech mech = this.selectedTile.mech; var apCostResult = mech.GetAPCostForMove(this.moveActionData.pathingResult.Value.nodes); if (apCostResult.isValid == false) { return(false); } return(true); } else if (this.state == BattleState.SetTargetAction) { return( tile != null && tile.mech != null && tile != this.selectedTile && this.CanTeamSeeTile(this.selectedTile.mech.team, tile) ); } else { return(false); } }
void UpdateTargetTile(BattleMech mech) { if (mech.team == this.currentTeam && mech.target && this.CanTeamSeeTile(mech.team, mech.target.tile)) { this.mapDisplay.SetTargetTile(mech.target.tile.mapTile); } else { this.mapDisplay.DisableTargetTile(); } }
void MechAttack(BattleMech mech, BattleMech target) { float damage = mech.data.damage; target.hp -= damage; Debug.Log(mech.data.name + " attacked " + target.data.name + " for " + damage + " damage."); if (target.hp <= 0f) { this.DestroyMech(target); } }
void ActionButtonHoverChanged(Button button, bool isEntering) { if (button == this.uiRefs.fireNowButton) { BattleMech mech = this.selectedTile.mech; if (isEntering) { float newAP = mech.actionPoints - mech.GetAPCostForStandingFire().ap; this.UpdateActionPointsPreview(newAP); } else { this.ResetActionPointsPreview(); } } }
public void Update() { BattleMech currentMech = null; // Select the next mech, or the first one with enough AP int mechCount = this.ourTeam.mechs.Count; for (int n = 0; n < mechCount; ++n) { this.mechUpdateIndex = (this.mechUpdateIndex + 1) % mechCount; currentMech = this.ourTeam.mechs[this.mechUpdateIndex]; if (currentMech.actionPoints > 0) { break; } } Assert.IsTrue(currentMech.actionPoints > 0); currentMech.ai.Update(); }
// NOTE: can be null void HexTileClicked() { BattleTile clickedTile = this.hoveredTile; if (this.state == BattleState.SelectingAction) { if (clickedTile != this.selectedTile) { if (this.selectedTile) { // Unselected this tile. this.mapDisplay.DisableSelectedTiles(); this.mapDisplay.DisableTargetTile(); } this.selectedTile = clickedTile; if (this.selectedTile) { // Selected this tile. this.mapDisplay.SetSelectedTile(this.selectedTile.mapTile); BattleMech mech = this.selectedTile.mech; if (mech) { this.UpdateTargetTile(mech); } } else { // Just for a consistent look when no tile is selected. this.BringTileTabButtonToFront(this.uiRefs.mechTabButton); } this.AdjustTileInfoTabButtonGraphics(); this.UpdateRightPanel(); } } else if (this.state == BattleState.MoveAction) { if (clickedTile) { BattleMech mech = this.selectedTile.mech; var move = new BattleMove.Move(); move.mechIndex = mech.tile.index; move.newIndex = clickedTile.index; move.isFiring = mech.target != null && mech.fireAuto; if (move.isFiring) { move.targetMechIndex = mech.target.tile.index; } this.ExecuteMove(move); this.moveActionData.moved = true; // Make this the new selected tile. this.selectedTile = clickedTile; this.mapDisplay.SetSelectedTile(this.selectedTile.mapTile); this.UpdateTargetTile(this.selectedTile.mech); this.UpdateRightPanel(); this.SetState(BattleState.EndOfAction); } else { this.SetState(BattleState.EndOfAction); } } else if (this.state == BattleState.SetTargetAction) { BattleMech mech = this.selectedTile.mech; var move = new BattleMove.SetTarget(); move.mechIndex = mech.tile.index; move.hasTarget = clickedTile != null; if (move.hasTarget) { move.targetMechIndex = clickedTile.index; } this.ExecuteMove(move); // TODO: Keep mech's dir pointed towards its target, if it has one this.UpdateTargetTile(mech); this.UpdateRightPanel(); this.SetState(BattleState.EndOfAction); } }
public void Init(Game game, BattleHistory battleHistory) { this.game = game; // Copy the history, except for the moves; they'll be applied later. this.history = battleHistory; List <object> moveHistory = battleHistory.moves; this.history.moves = new List <object>(); Scenario scenario = this.history.scenario; BattleMap map = this.history.startingMap; this.mapDisplay.Init(this, map.size); // Build misc objects. { // Let us know when anything on the UI is clicked (and not handled by a button or something). EventTrigger eventTrigger = this.uiRefs.canvas.gameObject.AddComponent <EventTrigger>(); var entry = new EventTrigger.Entry(); entry.eventID = EventTriggerType.PointerClick; entry.callback.AddListener((data) => { var button = ((PointerEventData)data).button; if (button == PointerEventData.InputButton.Left) { this.MouseEvent(null, MapDisplay.MouseEventType.Click); } else if (button == PointerEventData.InputButton.Right) { this.MouseEvent(null, MapDisplay.MouseEventType.RightClick); } }); eventTrigger.triggers.Add(entry); } // Build map. this.mapSize = map.size; this.tiles = new BattleTile[this.mapSize.x * this.mapSize.y]; TileData baseTileData = GameData.GetTile(map.baseTileName); TileData tileData = baseTileData; this.pathNetwork = new PathNetwork(); for (int y = 0; y < map.size.y; ++y) { for (int x = 0; x < map.size.x; ++x) { var pos = new Vector2i(x, y); tileData = baseTileData; foreach (var o in map.tileOverrides) { if (o.pos == pos) { tileData = GameData.GetTile(o.name); break; } } MapTile mapTile = this.mapDisplay.GetTile(pos); BattleTile newTile = mapTile.gameObject.AddComponent <BattleTile>(); newTile.Init(this, x, y, tileData); this.tiles[x + y * this.mapSize.x] = newTile; } } foreach (BattleTile tile in this.tiles) { for (int n = 0; n < 6; ++n) { BattleTile neighbor = tile.GetNeighbor(n); if (neighbor) { this.pathNetwork.ConnectNodes(tile, neighbor); } } if (tile.data.allowsMovement == false) { this.pathNetwork.SetNodeEnabled(tile, false); } } // Create teams and place mechs. var playerSpawns = new Stack <Vector2i>(); var enemySpawns = new Stack <Vector2i>(); foreach (var e in map.entities) { if (e.name == "PlayerMechSpawn") { playerSpawns.Push(e.pos); } else if (e.name == "EnemyMechSpawn") { enemySpawns.Push(e.pos); } } this.teams = new List <BattleTeam>(); foreach (Scenario.Team teamData in scenario.teams) { BattleTeam team = new BattleTeam(); this.teams.Add(team); team.mechs = new List <BattleMech>(); team.isPlayer = teamData.isPlayer; if (team.isPlayer == false) { team.ai = new BattleTeamAi(team); } team.visibleTiles = new bool[this.tiles.Length]; foreach (Scenario.Mech m in teamData.mechs) { MechData mechData = GameData.GetMech(m.mechName); Vector2i spawnPos = team.isPlayer ? playerSpawns.Pop() : enemySpawns.Pop(); BattleTile tile = this.GetTile(spawnPos); GameObject mechGO = new GameObject(string.Concat("Mech: ", mechData.name)); mechGO.transform.parent = tile.transform; BattleMech mech = mechGO.AddComponent <BattleMech>(); mech.Init(this, mechData); mech.PlaceAtMapTile(tile.mapTile); mech.SetDirection(m.direction); Assert.IsTrue(tile.mech == null); tile.mech = mech; mech.tile = tile; if (team.isPlayer == false) { mech.ai = new BattleMechAi(mech); } this.pathNetwork.SetNodeEnabled(tile, false); team.mechs.Add(mech); mech.team = team; } } this.currentTeamIndex = this.history.scenario.startingTeamIndex; this.currentTeam = this.teams[this.currentTeamIndex]; // UI stuff. this.uiRefs.advanceAiButton.interactable = false; this.uiRefs.tileInfoBorder.SetActive(false); this.uiRefs.mechTab.SetActive(false); this.uiRefs.pilotTab.SetActive(false); this.uiRefs.tileTab.SetActive(false); this.uiRefs.actionsPanel.SetActive(false); Utility.AddButtonClickListener(this.uiRefs.finishTurnButton, this.UnitListButtonPressed); Utility.AddButtonClickListener(this.uiRefs.advanceAiButton, this.UnitListButtonPressed); Utility.AddButtonClickListener(this.uiRefs.mechTabButton, this.TileInfoTabButtonClicked); Utility.AddButtonClickListener(this.uiRefs.pilotTabButton, this.TileInfoTabButtonClicked); Utility.AddButtonClickListener(this.uiRefs.tileTabButton, this.TileInfoTabButtonClicked); Utility.AddButtonClickListener(this.uiRefs.moveButton, this.ActionButtonPressed); Utility.AddButtonClickListener(this.uiRefs.setTargetButton, this.ActionButtonPressed); Utility.AddButtonClickListener(this.uiRefs.fireNowButton, this.ActionButtonPressed); Utility.AddToggleListener(this.uiRefs.fireAutoToggle, this.ActionToggleChanged); Utility.AddButtonHoverListener(this.uiRefs.fireNowButton, this.ActionButtonHoverChanged); this.BringTileTabButtonToFront(this.uiRefs.mechTabButton); this.UpdateRightPanel(); this.apTextOriginalColor = this.uiRefs.apText.color; // Execute any moves to start with. foreach (object o in moveHistory) { this.ExecuteMove(o); } this.currentTeamIndex = battleHistory.currentTeamIndex; this.currentTeam = this.teams[this.currentTeamIndex]; // Start first state. this.UpdateFogOfWar(); if (this.currentTeam.isPlayer) { this.SetState(BattleState.SelectingAction); } else { this.SetState(BattleState.AiControl); } }
void HexTileHovered() { bool isHoveredTileGOActive = this.hoveredTile != null; if (this.state == BattleState.AiControl) { isHoveredTileGOActive = false; } else if (this.state == BattleState.MoveAction) { foreach (BattleTile tile in this.moveActionData.ghostMechTiles) { tile.mapTile.RemoveLayer(MapTile.Layer.GhostSprite); } this.moveActionData.ghostMechTiles.Clear(); if (this.moveActionData.losLinesGO) { Destroy(this.moveActionData.losLinesGO); } this.moveActionData.losLinesGO = new GameObject("LOS lines"); this.moveActionData.losLinesGO.transform.parent = this.worldGO.transform; this.ResetActionPointsPreview(); this.moveActionData.pathingResult = this.pathNetwork.FindPath( this.selectedTile, this.hoveredTile ); if (this.CanClickTile() == false) { goto end; } PathingResult result = this.moveActionData.pathingResult.Value; if (result.isValid == false) { goto end; } // Create ghost mechs this.moveActionData.ghostMechTiles.Capacity = result.nodes.Count - 1; GameObject prevGO = this.selectedTile.gameObject; GameObject currentGO; for (int n = 1; n < result.nodes.Count; ++n) { BattleTile tile = (BattleTile)result.nodes[n]; this.moveActionData.ghostMechTiles.Add(tile); currentGO = tile.gameObject; bool right = currentGO.transform.position.x > prevGO.transform.position.x; tile.mapTile.SetLayer( MapTile.Layer.GhostSprite, sprite: this.selectedTile.mech.data.sprite, flipX: right, color: new Color(1.0f, 1.0f, 1.0f, 0.5f) ); prevGO = currentGO; } // Create LOS lines from each tile to the target BattleMech target = this.selectedTile.mech.target; if (target && this.selectedTile.mech.fireAuto) { for (int n = 1; n < result.nodes.Count; ++n) { BattleTile tile = (BattleTile)result.nodes[n]; GameObject go = (GameObject)Instantiate(Resources.Load("Prefabs/LOS line")); go.transform.parent = this.moveActionData.losLinesGO.transform; float offsetOutward = 0.33f; Vector3 dir = (target.tile.transform.position - tile.transform.position).normalized; Vector3 pos1 = tile.transform.position + dir * offsetOutward; Vector3 pos2 = target.tile.transform.position - dir * offsetOutward; float height = 0.25f; pos1.y += height; pos2.y += height; LineRenderer lr = go.GetComponent <LineRenderer>(); lr.SetPositions(new Vector3[] { pos1, pos2 }); bool canSee = this.TestLOS(tile, target.tile); Color lineColor = canSee ? Color.green : Color.red; lr.startColor = lineColor; lr.endColor = lineColor; } } // Update action point UI element. float apRequired = this.selectedTile.mech.GetAPCostForMove(result.nodes).ap; this.UpdateActionPointsPreview(this.selectedTile.mech.actionPoints - apRequired); } end: if (isHoveredTileGOActive) { this.mapDisplay.SetHoveredTile(this.hoveredTile.mapTile); this.mapDisplay.SetHoveredTileValid(this.CanClickTile()); } else { this.mapDisplay.DisableHoveredTile(); } }
// Note: this will be used for replays and maybe save files, so only use data from the move data // and the current state of the map. // Hmmmm, this is starting to seem very independent from the rest of this file. public void ExecuteMove(object o) { if (o.GetType() == typeof(BattleMove.Move)) { var move = (BattleMove.Move)o; Assert.IsTrue( this.GetTile(move.mechIndex).mech != null && this.GetTile(move.newIndex).mech == null && (move.isFiring == false || this.GetTile(move.targetMechIndex).mech != null) ); BattleMech mech = this.GetTile(move.mechIndex).mech; BattleMech targetMech = move.isFiring ? this.GetTile(move.targetMechIndex).mech : null; BattleTile fromTile = mech.tile; BattleTile toTile = this.GetTile(move.newIndex); this.pathNetwork.SetNodeEnabled(fromTile, true); PathingResult result = this.pathNetwork.FindPath(fromTile, toTile); Assert.IsTrue(result.isValid); bool isFiring = move.isFiring; BattleTile prevTile = (BattleTile)result.nodes[0]; for (int n = 1; n < result.nodes.Count; ++n) { BattleTile currentTile = (BattleTile)result.nodes[n]; prevTile.mech = null; currentTile.mech = mech; mech.tile = currentTile; if (isFiring) { bool canSeeTarget = this.TestLOS(currentTile, targetMech.tile); if (canSeeTarget) { this.MechAttack(mech, targetMech); if (targetMech.isDestroyed) { isFiring = false; } } } prevTile = currentTile; } mech.PlaceAtMapTile(toTile.mapTile); BattleTile lastTile1 = (BattleTile)result.nodes[result.nodes.Count - 2]; BattleTile lastTile2 = (BattleTile)result.nodes[result.nodes.Count - 1]; bool right = lastTile2.transform.position.x > lastTile1.transform.position.x; MechDirection newDir = right ? MechDirection.Right : MechDirection.Left; mech.SetDirection(newDir); var apCostResult = mech.GetAPCostForMove(result.nodes); Assert.IsTrue(mech.actionPoints > 0); mech.actionPoints -= apCostResult.ap; this.pathNetwork.SetNodeEnabled(toTile, false); this.UpdateFogOfWar(); } else if (o.GetType() == typeof(BattleMove.StandingFire)) { var move = (BattleMove.StandingFire)o; Assert.IsTrue( this.GetTile(move.mechIndex).mech != null && this.GetTile(move.targetMechIndex).mech != null ); BattleMech mech = this.GetTile(move.mechIndex).mech; BattleMech targetMech = this.GetTile(move.targetMechIndex).mech; this.MechAttack(mech, targetMech); var apCostResult = mech.GetAPCostForStandingFire(); mech.actionPoints -= apCostResult.ap; } else if (o.GetType() == typeof(BattleMove.SetTarget)) { var move = (BattleMove.SetTarget)o; Assert.IsTrue( this.GetTile(move.mechIndex).mech != null && (move.hasTarget == false || this.GetTile(move.targetMechIndex).mech != null) ); BattleMech mech = this.GetTile(move.mechIndex).mech; if (move.hasTarget) { mech.target = this.GetTile(move.targetMechIndex).mech; } else { mech.target = null; } } else { throw new UnityException(); } // Add to battle history. this.history.moves.Add(o); // Determine if this team's turn is over so we can advance the turn. bool hasAP = false; foreach (BattleMech mech in this.currentTeam.mechs) { if (mech.actionPoints > 0) { hasAP = true; break; } } if (hasAP == false) { this.AdvanceTurn(); } }
void UpdateRightPanel() { this.AdjustTileInfoTabButtonGraphics(); bool hasTile = this.selectedTile != null; bool hasMech = hasTile && this.selectedTile.mech != null; hasMech = hasMech && this.CanTeamSeeTile(this.currentTeam, this.selectedTile); BattleMech mech = hasMech ? this.selectedTile.mech : null; this.uiRefs.tileInfoBorder.SetActive(hasTile); this.uiRefs.mechTabButton.interactable = hasMech; this.uiRefs.pilotTabButton.interactable = hasMech; this.uiRefs.tileTabButton.interactable = hasTile; this.uiRefs.tileTab.SetActive(false); this.uiRefs.pilotTab.SetActive(false); this.uiRefs.mechTab.SetActive(false); this.uiRefs.actionsPanel.SetActive(hasMech); if (hasTile) { TileData tileData = this.selectedTile.data; this.uiRefs.tileTabName.text = tileData.name; this.uiRefs.tileTabDescription.text = tileData.description; if (hasMech) { this.uiRefs.mechTab.SetActive(true); this.uiRefs.mechTabButton.interactable = true; this.uiRefs.mechTabName.text = mech.data.name; this.uiRefs.mechTabHP.text = "HP: " + mech.hp + " / " + mech.data.maxHP; this.uiRefs.pilotTabButton.interactable = true; // TODO: not this this.TileInfoTabButtonClicked(this.uiRefs.mechTabButton); bool isOurTurn = mech.team == this.currentTeam; if (isOurTurn) { this.uiRefs.actionsPanel.SetActive(true); this.uiRefs.fireAutoToggle.isOn = mech.fireAuto; this.ResetActionPointsPreview(); if (mech.target) { bool canSeeTarget = this.TestLOS(mech.tile, mech.target.tile); if (canSeeTarget) { this.uiRefs.fireNowButton.interactable = mech.GetAPCostForStandingFire().isValid; } else { this.uiRefs.fireNowButton.interactable = false; } } else { this.uiRefs.fireNowButton.interactable = false; } } else { this.uiRefs.actionsPanel.SetActive(false); } } else { this.uiRefs.tileTab.SetActive(true); this.TileInfoTabButtonClicked(this.uiRefs.tileTabButton); } } }
public BattleMechAi(BattleMech newMech) { this.ourMech = newMech; this.battle = this.ourMech.battle; }
public void Update() { // Test: path to a target mech, stop when we can see it and keep firing. BattleMech target = null; foreach (BattleTeam team in this.battle.teams) { foreach (BattleMech mech in team.mechs) { if (team != this.ourMech.team) { target = mech; break; } } } if (target == null) { return; } BattleTile ourTile = this.ourMech.tile; BattleTile targetTile = target.tile; bool canSeeTarget = this.battle.TestLOS(ourTile, targetTile); // TODO: this is really stupid this.battle.pathNetwork.SetNodeEnabled(ourTile, true); this.battle.pathNetwork.SetNodeEnabled(targetTile, true); PathingResult result = this.battle.pathNetwork.FindPath(ourTile, targetTile); this.battle.pathNetwork.SetNodeEnabled(ourTile, false); this.battle.pathNetwork.SetNodeEnabled(targetTile, false); if (result.isValid == false) { return; } BattleTile nextTile = (BattleTile)result.nodes[1]; if (canSeeTarget && result.distance <= 5) { var move = new BattleMove.StandingFire(); move.mechIndex = ourTile.index; move.targetMechIndex = targetTile.index; this.battle.ExecuteMove(move); } else { var move = new BattleMove.Move(); move.mechIndex = ourTile.index; move.newIndex = nextTile.index; if (canSeeTarget) { move.isFiring = true; move.targetMechIndex = targetTile.index; } else { move.isFiring = false; } this.battle.ExecuteMove(move); } this.battle.SetState(BattleState.EndOfAction); }