package net.sf.colossus.server; import java.util.logging.Logger; import net.sf.colossus.game.Battle; import net.sf.colossus.game.Creature; import net.sf.colossus.game.Game; import net.sf.colossus.variant.BattleHex; import net.sf.colossus.variant.HazardHexside; import net.sf.colossus.variant.HazardTerrain; public class BattleStrikeServerSide { private static final Logger LOGGER = Logger .getLogger(BattleStrikeServerSide.class.getName()); private final Game game; public BattleStrikeServerSide(Game game) { this.game = game; LOGGER.finest("BattleStrikeServerSide instantiated."); } /** Return the number of dice that will be rolled when striking this * target, including modifications for terrain. * WARNING: this is currently still duplicated in game.BattleStrike * @param striker TODO * @param target TODO */ protected int getDice(CreatureServerSide striker, Creature target) { assert getBattle() != null : "getDice called when there is no battle!"; return getDice(striker, target, !getBattle() .isInContact(striker, true)); } /** WARNING: this is currently still duplicated in game.BattleStrike */ public int getDice(Creature striker, Creature target, boolean rangestrike) { BattleHex hex = striker.getCurrentHex(); BattleHex targetHex = target.getCurrentHex(); int dice = striker.getPower(); if (rangestrike) { // Divide power in half, rounding down. dice /= 2; // volcanoNative rangestriking from volcano: +2 if (striker.isNativeIn(HazardTerrain.VOLCANO) && hex.getTerrain().equals(HazardTerrain.VOLCANO)) { dice += 2; } } else { // Dice can be modified by terrain. // volcanoNative striking from volcano: +2 if (striker.isNativeIn(HazardTerrain.VOLCANO) && hex.getTerrain().equals(HazardTerrain.VOLCANO)) { dice += 2; } // Adjacent hex, so only one possible direction. int direction = Battle.getDirection(hex, targetHex, false); HazardHexside hazard = hex.getHexsideHazard(direction); // Native striking down a dune hexside: +2 if (hazard == HazardHexside.DUNE && striker.isNativeAt(HazardHexside.DUNE)) { dice += 2; } // Native striking down a slope hexside: +1 else if (hazard == HazardHexside.SLOPE && striker.isNativeAt(HazardHexside.SLOPE)) { dice++; } // Non-native striking up a dune hexside: -1 else if (!striker.isNativeAt(HazardHexside.DUNE) && hex.getOppositeHazard(direction) == HazardHexside.DUNE) { dice--; } /* TODO: remove TEST TEST TEST TEST TEST */ /* getStrikingPower should be used instead of the logic above, but * 1) I'm not sure everyone will agree it belongs in Creature * 2) I haven't had time to verify it's correct. * Incidentally, if you're reading this after noticing the warning * below in your logfile, then it isn't correct ;-) */ int checkStrikingPower = striker.getStrikingPower(target, hex .getElevation(), targetHex.getElevation(), hex.getTerrain(), targetHex.getTerrain(), hex.getHexsideHazard(BattleServerSide .getDirection(hex, targetHex, false)), targetHex .getHexsideHazard(BattleServerSide.getDirection(targetHex, hex, false))); if (checkStrikingPower != dice) { LOGGER.warning("attackerPower says " + dice + " but checkStrikingPower says " + checkStrikingPower); } /* END TODO: remove TEST TEST TEST TEST TEST */ } return dice; } /** WARNING: this is duplicated in BattleClientSide * @param striker TODO * @param target TODO * @param rangestrike TODO*/ @SuppressWarnings("deprecation") int getAttackerSkill(Creature striker, Creature target, boolean rangestrike) { // rangestrike calc depends on countBrambleHexes which needs battle. // (it's called when there is no battle by ShowCreatureDetails) assert getBattle() != null || !rangestrike : "getAttackerSkill " + "called for rangestrike when there is no battle!"; BattleHex hex = striker.getCurrentHex(); BattleHex targetHex = target.getCurrentHex(); int attackerSkill = striker.getSkill(); // Skill can be modified by terrain. if (!rangestrike) { // striking out of possible hazard attackerSkill -= hex.getTerrain().getSkillPenaltyStrikeFrom( striker.isNativeIn(hex.getTerrain()), target.isNativeIn(hex.getTerrain())); if (hex.getElevation() > targetHex.getElevation()) { // Adjacent hex, so only one possible direction. int direction = BattleServerSide.getDirection(hex, targetHex, false); HazardHexside hazard = hex.getHexsideHazard(direction); // Striking down across wall: +1 if (hazard.equals(HazardHexside.TOWER)) { attackerSkill++; } } else if (hex.getElevation() < targetHex.getElevation()) { // Adjacent hex, so only one possible direction. int direction = BattleServerSide.getDirection(targetHex, hex, false); HazardHexside hazard = targetHex.getHexsideHazard(direction); // Non-native striking up slope: -1 // Striking up across wall: -1 if ((hazard.equals(HazardHexside.SLOPE) && !striker .isNativeAt(HazardHexside.SLOPE)) || hazard.equals(HazardHexside.TOWER)) { attackerSkill--; } } /* TODO: remove TEST TEST TEST TEST TEST */ /* getStrikingSkill should be used instead of the logic above, but * 1) I'm not sure everyone will agree it belongs in Creature * 2) I haven't had time to verify it's correct. * Incidentally, if you're reading this after noticing the warning * below in your logfile, then it isn't correct ;-) */ int checkStrikingSkill = striker.getStrikingSkill(target, hex .getElevation(), targetHex.getElevation(), hex.getTerrain(), targetHex.getTerrain(), hex.getHexsideHazard(BattleServerSide .getDirection(hex, targetHex, false)), targetHex .getHexsideHazard(BattleServerSide.getDirection(targetHex, hex, false))); if (checkStrikingSkill != attackerSkill) { LOGGER .warning(String .format( "For creature %s striking %s from %s(%d) to %s(%d) via %s/%s, " + "we calculated %d as attacker skill, but getStrikingSkill says %d", striker, target, hex.getTerrain(), Integer .valueOf(hex.getElevation()), targetHex .getTerrain(), Integer.valueOf(targetHex .getElevation()), hex .getHexsideHazard(BattleServerSide .getDirection(hex, targetHex, false)), targetHex.getHexsideHazard(BattleServerSide .getDirection(targetHex, hex, false)), Integer .valueOf(attackerSkill), Integer .valueOf(checkStrikingSkill))); } /* END TODO: remove TEST TEST TEST TEST TEST */ } else if (!striker.useMagicMissile()) { // Range penalty /* Range 4 means a penalty of 1 to the attacker. * I hereby extend this so that range 5 means a penalty of 2, * and so one, for creature with higher skill. */ int range = BattleServerSide.getRange(hex, targetHex, false); if (range >= 4) { attackerSkill -= (range - 3); } int bramblesPenalty = 0; // Non-native rangestrikes: -1 per intervening bramble hex if (!striker.isNativeIn(HazardTerrain.BRAMBLES)) { bramblesPenalty += getBattle().countBrambleHexes( striker.getCurrentHex(), targetHex); } /* TODO: remove TEST TEST TEST TEST TEST */ /* computeSkillPenaltyRangestrikeThrough should be used instead of the logic above, but * 1) I'm not sure everyone will agree it belongs in Battle * 2) I haven't had time to verify it's correct. * Incidentally, if you're reading this after noticing the warning * below in your logfile, then it isn't correct ;-) */ int interveningPenalty = getBattle() .computeSkillPenaltyRangestrikeThrough( striker.getCurrentHex(), targetHex, striker); if (interveningPenalty != bramblesPenalty) { LOGGER.warning("bramblesPenalty says " + bramblesPenalty + " but interveningPenalty says " + interveningPenalty); } /* END TODO: remove TEST TEST TEST TEST TEST */ attackerSkill -= bramblesPenalty; // Rangestrike up across wall: -1 per wall if (targetHex.hasWall()) { int heightDeficit = targetHex.getElevation() - hex.getElevation(); if (heightDeficit > 0) { // Because of the design of the tower map, a strike to // a higher tower hex always crosses one wall per // elevation difference. /* actually this need some better logic, as some Wall are * in a completely different patterns that the Tower * nowaday */ attackerSkill -= heightDeficit; } } // Rangestrike into volcano: -1 /* actually, it's only for native ... but then non-native are * blocked. Anyway this will should to HazardTerrain someday. */ if (targetHex.getTerrain().equals(HazardTerrain.VOLCANO)) { attackerSkill--; } } return attackerSkill; } /** WARNING: this is duplicated in BattleClientSide * @param striker TODO * @param target TODO*/ public int getStrikeNumber(CreatureServerSide striker, Creature target) { return getStrikeNumber(striker, target, !getBattle().isInContact(striker, true)); } public int getStrikeNumber(Creature striker, Creature target, boolean rangestrike) { int attackerSkill = getAttackerSkill(striker, target, rangestrike); int defenderSkill = target.getSkill(); int strikeNumber = 4 - attackerSkill + defenderSkill; HazardTerrain terrain = target.getCurrentHex().getTerrain(); if (!rangestrike) { // Strike number can be modified directly by terrain. strikeNumber += terrain.getSkillBonusStruckIn( striker.isNativeIn(terrain), target.isNativeIn(terrain)); } else { // Native defending in bramble, from rangestrike by a non-native // non-magicMissile: +1 if (terrain.equals(HazardTerrain.BRAMBLES) && target.isNativeIn(HazardTerrain.BRAMBLES) && !striker.isNativeIn(HazardTerrain.BRAMBLES) && !striker.useMagicMissile()) { strikeNumber++; } // Native defending in stone, from rangestrike by a non-native // non-magicMissile: +1 if (terrain.equals(HazardTerrain.STONE) && target.isNativeIn(HazardTerrain.STONE) && !striker.isNativeIn(HazardTerrain.STONE) && !striker.useMagicMissile()) { strikeNumber++; } } // Sixes always hit. if (strikeNumber > 6) { strikeNumber = 6; } return strikeNumber; } private Battle getBattle() { return game.getBattle(); } }