package onlinefrontlines.game.actions;
import onlinefrontlines.auth.User;
import onlinefrontlines.game.*;
import onlinefrontlines.utils.IllegalRequestException;
/**
* Action that performs an attack
*
* @author jorrit
*
* Copyright (C) 2009-2013 Jorrit Rouwe
*
* This file is part of Online Frontlines.
*
* Online Frontlines is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Online Frontlines is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Online Frontlines. If not, see <http://www.gnu.org/licenses/>.
*/
public class ActionAttackUnit extends Action
{
private UnitState selectedUnit;
private UnitState targettedUnit;
private int newStateSelectedUnitArmour = -1;
private int newStateSelectedUnitAmmo;
private Faction newStateSelectedUnitFaction;
private int newStateTargettedUnitArmour;
private int newStateTargettedUnitAmmo;
private Faction newStateTargettedUnitFaction;
/**
* Default constructor
*/
public ActionAttackUnit()
{
}
/**
* Constructor used by AI
*/
public ActionAttackUnit(UnitState selectedUnit, UnitState targettedUnit)
{
this.selectedUnit = selectedUnit;
this.targettedUnit = targettedUnit;
}
/**
* Apply the action
*/
public void doAction(boolean addToDb) throws IllegalRequestException
{
// Both units needs to be identified
selectedUnit.identifiedByEnemy = true;
selectedUnit.setDetectedRecursive(true, 2);
targettedUnit.identifiedByEnemy = true;
targettedUnit.setDetectedRecursive(true, 2);
// Deduct action point
selectedUnit.actionsLeft--;
selectedUnit.lastActionWasMove = false;
if (selectedUnit.actionsLeft <= 0)
selectedUnit.movementPointsLeft = 0;
// Get terrain types
TerrainConfig selectedUnitTerrain = gameState.getAttackTerrain(selectedUnit);
TerrainConfig targettedUnitTerrain = gameState.getAttackTerrain(targettedUnit);
Faction oldSelectedUnitFaction = selectedUnit.faction;
int oldSelectedUnitArmour = selectedUnit.armour;
Faction oldTargettedUnitFaction = targettedUnit.faction;
int oldTargettedUnitArmour = targettedUnit.armour;
if (newStateSelectedUnitArmour == -1)
{
// Perform attack
selectedUnit.attackAndCounter(targettedUnit, selectedUnitTerrain, targettedUnitTerrain, gameState.random);
}
else
{
// Replay attack
selectedUnit.armour = newStateSelectedUnitArmour;
selectedUnit.ammo = newStateSelectedUnitAmmo;
selectedUnit.faction = newStateSelectedUnitFaction;
targettedUnit.armour = newStateTargettedUnitArmour;
targettedUnit.ammo = newStateTargettedUnitAmmo;
targettedUnit.faction = newStateTargettedUnitFaction;
}
// Accumulate stats for this attack
gameState.accumulateAttackStats(
selectedUnit.faction,
selectedUnit.unitConfig.id,
targettedUnit.unitConfig.id,
calculateDeaths(selectedUnit, oldSelectedUnitFaction),
calculateDeaths(targettedUnit, oldTargettedUnitFaction),
calculateDamage(targettedUnit, oldTargettedUnitArmour, oldTargettedUnitFaction),
calculateDamage(selectedUnit, oldSelectedUnitArmour, oldSelectedUnitFaction));
// Check selected unit destroyed
if (selectedUnit.armour <= 0)
{
calculateScore(selectedUnit, true);
gameState.removeUnit(selectedUnit);
}
else if (selectedUnit.faction != oldSelectedUnitFaction)
{
calculateScore(selectedUnit, false);
selectedUnit.containedUnits.clear();
}
// Check targetted unit destroyed
if (targettedUnit.armour <= 0)
{
calculateScore(targettedUnit, true);
gameState.removeUnit(targettedUnit);
}
else if (targettedUnit.faction != oldTargettedUnitFaction)
{
calculateScore(targettedUnit, false);
targettedUnit.containedUnits.clear();
}
// Set new owner
gameState.updateTerrainOwner(selectedUnit);
gameState.updateTerrainOwner(targettedUnit);
// Save state selected unit
newStateSelectedUnitArmour = selectedUnit.armour;
newStateSelectedUnitAmmo = selectedUnit.ammo;
newStateSelectedUnitFaction = selectedUnit.faction;
// Save state targetted unit
newStateTargettedUnitArmour = targettedUnit.armour;
newStateTargettedUnitAmmo = targettedUnit.ammo;
newStateTargettedUnitFaction = targettedUnit.faction;
// Current player did something
gameState.currentPlayerIdle = false;
}
/**
* Calculate damage taken
*/
private int calculateDamage(UnitState unit, int oldArmour, Faction oldFaction)
{
if (unit.faction != oldFaction)
return oldArmour;
return oldArmour - unit.armour;
}
/**
* Calculate number of deaths
*/
private int calculateDeaths(UnitState unit, Faction oldFaction)
{
if (unit.armour > 0 && unit.faction == oldFaction)
return 0;
return calculateDeathsRecursive(unit);
}
private int calculateDeathsRecursive(UnitState unit)
{
int count = 1;
for (UnitState c : unit.containedUnits)
count += calculateDeathsRecursive(c);
return count;
}
/**
* Recursively determine score for units lost
*/
public void calculateScore(UnitState unit, boolean unregisterUnit)
{
// Unit needs to be identified or else the remote cannot calculate the score
unit.setDetected(true);
unit.identifiedByEnemy = true;
// Unregister unit (so it cannot be used anymore in other actions)
if (unregisterUnit)
gameState.unregisterUnit(unit);
// Get score for opposite faction
Score oppositeScore = gameState.getScore(Faction.opposite(unit.initialFaction));
if (!unit.unitConfig.isBase)
{
// Normal unit
oppositeScore.numberOfUnitsDestroyed++;
oppositeScore.victoryPointsForUnits += unit.unitConfig.victoryPoints;
}
else
{
// Base
if (unit.faction != unit.initialFaction)
{
// Base was captured, add score
oppositeScore.numberOfBasesDestroyed++;
oppositeScore.victoryPointsForBases += unit.unitConfig.victoryPoints;
}
else
{
// Base was recaptured, deduct score
oppositeScore.numberOfBasesDestroyed--;
oppositeScore.victoryPointsForBases -= unit.unitConfig.victoryPoints;
}
}
// Recurse to children
for (UnitState c : unit.containedUnits)
calculateScore(c, true);
}
/**
* Convert action from a string
*/
public void fromString(String[] param, User initiatingUser) throws IllegalRequestException, IgnoreActionException
{
// Check phase
if (gameState.turnNumber == 0)
throw new IllegalRequestException("Not valid during setup phase");
// Check winning state
if (gameState.winningFaction != Faction.invalid)
throw new IllegalRequestException("Game has ended");
// Get selected unit
selectedUnit = gameState.getUnitById(Integer.parseInt(param[1]));
if (selectedUnit == null)
throw new IllegalRequestException("Invalid selected unit");
// Get targetted unit
targettedUnit = gameState.getUnitById(Integer.parseInt(param[2]));
if (targettedUnit == null)
throw new IllegalRequestException("Invalid targetted unit");
// Check if unit can attack other unit
if (!selectedUnit.canAttack(targettedUnit))
throw new IllegalRequestException("Unit cannot attack other unit");
// Client is not allowed to send damage data
if (initiatingUser == null)
{
// Get new state selected unit
newStateSelectedUnitArmour = Integer.parseInt(param[3]);
newStateSelectedUnitAmmo = Integer.parseInt(param[4]);
newStateSelectedUnitFaction = Faction.fromInt(Integer.parseInt(param[5]));
// Get new state targetted unit
newStateTargettedUnitArmour = Integer.parseInt(param[6]);
newStateTargettedUnitAmmo = Integer.parseInt(param[7]);
newStateTargettedUnitFaction = Faction.fromInt(Integer.parseInt(param[8]));
}
}
/**
* Convert action to a string
*/
public String toString(Faction remoteFaction)
{
return "a,"
+ selectedUnit.id + ","
+ targettedUnit.id + ","
+ newStateSelectedUnitArmour + ","
+ newStateSelectedUnitAmmo + ","
+ Faction.toInt(newStateSelectedUnitFaction) + ","
+ newStateTargettedUnitArmour + ","
+ newStateTargettedUnitAmmo + ","
+ Faction.toInt(newStateTargettedUnitFaction);
}
}