📄 loseffects.java
字号:
if (lightSmoke + (heavySmoke * 2) + lightWoods + (heavyWoods * 2) > 2) { return new ToHitData(ToHitData.IMPOSSIBLE, "LOS blocked by smoke and woods."); } if (lightWoods > 0) { if(eistatus > 0) { modifiers.addModifier(1, "firing through light woods with EI system"); } else { modifiers.addModifier(lightWoods, lightWoods + " intervening light woods"); } } if (heavyWoods > 0) { if(eistatus > 0) { modifiers.addModifier(heavyWoods, heavyWoods + " intervening heavy woods"); } else { modifiers.addModifier(heavyWoods * 2, heavyWoods + " intervening heavy woods"); } } if (lightSmoke > 0) { modifiers.addModifier(lightSmoke, lightSmoke + " intervening light smoke"); } if (heavySmoke > 0) { StringBuffer text = new StringBuffer(heavySmoke); text.append(" intervening"); if (game.getOptions().booleanOption("maxtech_fire")) text.append(" heavy"); text.append(" smoke"); if(eistatus > 0) { modifiers.addModifier(heavySmoke, text.toString()); } else { modifiers.addModifier(heavySmoke * 2, text.toString()); } } if (targetCover != COVER_NONE) { if (game.getOptions().booleanOption("maxtech_partial_cover")) { if (targetCover == COVER_75LEFT || targetCover == COVER_75RIGHT) modifiers.addModifier(1, "target has 75% cover"); else if(targetCover >= COVER_HORIZONTAL) modifiers.addModifier(1, "target has 50% cover"); else //no bth mod for 25% cover modifiers.addModifier(0, "target has 25% cover"); } else { modifiers.addModifier(3, "target has partial cover"); } } return modifiers; } /** * Returns LosEffects for a line that never passes exactly between two * hexes. Since intervening() returns all the coordinates, we just * add the effects of all those hexes. */ private static LosEffects losStraight(IGame game, AttackInfo ai) { ArrayList<Coords> in = Coords.intervening(ai.attackPos, ai.targetPos); LosEffects los = new LosEffects(); boolean targetInBuilding = false; if (ai.targetEntity) { targetInBuilding = Compute.isInBuilding(game, ai.targetAbsHeight - game.getBoard().getHex(ai.targetPos).surface(), ai.targetPos); } // If the target and attacker are both in a // building, set that as the first LOS effect. if ( targetInBuilding && Compute.isInBuilding( game, ai.attackAbsHeight - game.getBoard().getHex(ai.attackPos).surface(), ai.attackPos ) ) { los.setThruBldg( game.getBoard().getBuildingAt( in.get(0) ) ); } for (Coords c : in) { los.add( LosEffects.losForCoords(game, ai, c, los.getThruBldg()) ); } if ((ai.minimumWaterDepth < 1) && ai.underWaterCombat) { los.blocked = true; } // Infantry inside a building can only be // targeted by units in the same building. if ( ai.targetInfantry && targetInBuilding && null == los.getThruBldg() ) { los.infProtected = true; } // If a target Entity is at a different elevation as its // attacker, and if the attack is through a building, the // target has cover. if ( null != los.getThruBldg() && ai.attackAbsHeight != ai.targetAbsHeight ) { los.setTargetCover( COVER_HORIZONTAL ); } return los; } /** * Returns LosEffects for a line that passes between two hexes at least * once. The rules say that this situation is resolved in favor of the * defender. * * The intervening() function returns both hexes in these circumstances, * and, when they are in line order, it's not hard to figure out which hexes * are split and which are not. * * The line always looks like: * ___ ___ * ___/ 1 \___/...\___ * / 0 \___/ 3 \___/etc\ * \___/ 2 \___/...\___/ * \___/ \___/ * * We go thru and figure out the modifiers for the non-split hexes first. * Then we go to each of the two split hexes and determine which gives us * the bigger modifier. We use the bigger modifier. * * This is not perfect as it takes partial cover as soon as it can, when * perhaps later might be better. * Also, it doesn't account for the fact that attacker partial cover blocks * leg weapons, as we want to return the same sequence regardless of * what weapon is attacking. */ private static LosEffects losDivided(IGame game, AttackInfo ai) { ArrayList<Coords> in = Coords.intervening(ai.attackPos, ai.targetPos, true); LosEffects los = new LosEffects(); boolean targetInBuilding = false; if (ai.targetEntity) { targetInBuilding = Compute.isInBuilding(game, ai.targetAbsHeight - game.getBoard().getHex(ai.targetPos).surface(), ai.targetPos); } // If the target and attacker are both in a // building, set that as the first LOS effect. if ( targetInBuilding && Compute.isInBuilding( game, ai.attackAbsHeight - game.getBoard().getHex(ai.attackPos).surface(), ai.attackPos ) ) { los.setThruBldg( game.getBoard().getBuildingAt( in.get(0)) ); } // add non-divided line segments for (int i = 3; i < in.size() - 2; i += 3) { los.add( losForCoords(game, ai, in.get(i), los.getThruBldg()) ); } if ((ai.minimumWaterDepth < 1) && ai.underWaterCombat) { los.blocked = true; } // if blocked already, return that if (los.losModifiers(game).getValue() == ToHitData.IMPOSSIBLE) { return los; } // go through divided line segments for (int i = 1; i < in.size() - 2; i += 3) { // get effects of each side LosEffects left = losForCoords( game, ai, in.get(i), los.getThruBldg()); LosEffects right = losForCoords( game, ai, in.get(i+1), los.getThruBldg()); // If a target Entity is at a different elevation as its // attacker, and if the attack is through a building, the // target has cover. final boolean isElevDiff = ai.attackAbsHeight != ai.targetAbsHeight; if ((ai.minimumWaterDepth < 1) && ai.underWaterCombat) { los.blocked = true; } if ( targetInBuilding && isElevDiff ) { if ( null != left.getThruBldg() ) { left.setTargetCover(COVER_HORIZONTAL); } if ( null != right.getThruBldg() ) { right.setTargetCover(COVER_HORIZONTAL); } } // Include all previous LOS effects. left.add(los); right.add(los); // Infantry inside a building can only be // targeted by units in the same building. if ( ai.targetInfantry && targetInBuilding ) { if ( null == left.getThruBldg() ) { left.infProtected = true; } else if ( null == right.getThruBldg() ) { right.infProtected = true; } } // which is better? int lVal = left.losModifiers(game).getValue(); int rVal = right.losModifiers(game).getValue(); if (lVal > rVal || (lVal == rVal && left.isAttackerCover())) { los = left; } else { los = right; } if (game.getOptions().booleanOption("maxtech_partial_cover")) { int cover = (left.targetCover & (COVER_LEFT | COVER_LOWLEFT)) | (right.targetCover & (COVER_RIGHT | COVER_LOWRIGHT)); if (cover < COVER_FULL && !(left.blocked && right.blocked)) { los.blocked = false; los.targetCover = cover; } cover = (left.attackerCover & (COVER_LEFT | COVER_LOWLEFT)) | (right.attackerCover & (COVER_RIGHT | COVER_LOWRIGHT)); if (cover < COVER_FULL && !(left.blocked && right.blocked)) { los.blocked = false; los.attackerCover = cover; } } } return los; } /** * Returns a LosEffects object representing the LOS effects of anything at * the specified coordinate. */ private static LosEffects losForCoords(IGame game, AttackInfo ai, Coords coords, Building thruBldg) { LosEffects los = new LosEffects(); // ignore hexes not on board if (!game.getBoard().contains(coords)) { return los; } // Is there a building in this hex? Building bldg = game.getBoard().getBuildingAt(coords); // We're only tracing thru a single building if there // is a building in this hex, and if it isn't the same // building that we'be been tracing LOS thru. if ( bldg != null && bldg.equals(thruBldg) ) { los.setThruBldg( thruBldg ); } // ignore hexes the attacker or target are in if ( coords.equals(ai.attackPos) || coords.equals(ai.targetPos) ) { return los; } IHex hex = game.getBoard().getHex(coords); int hexEl = ai.underWaterCombat ? hex.floor() : hex.surface(); // Handle minimum water depth. // Applies to Torpedos. if (!(hex.containsTerrain(Terrains.WATER))) ai.minimumWaterDepth = 0; else if ((hex.terrainLevel(Terrains.WATER) >= 0) && ((ai.minimumWaterDepth == -1) || (hex.terrainLevel(Terrains.WATER) < ai.minimumWaterDepth))) ai.minimumWaterDepth = hex.terrainLevel(Terrains.WATER); // Handle building elevation. // Attacks thru a building are not blocked by that building. // ASSUMPTION: bridges don't block LOS. int bldgEl = 0; if ( null == los.getThruBldg() && hex.containsTerrain( Terrains.BLDG_ELEV ) ) { bldgEl = hex.terrainLevel( Terrains.BLDG_ELEV ); } // TODO: Identify when LOS travels *above* a building's hex. // Alternatively, force all building hexes to be same height. // check for block by terrain //check for LOS according to diagramming rule from MaxTech, page 22 if (game.getOptions().booleanOption("maxtech_LOS1")) { if (hexEl + bldgEl > (ai.targetAbsHeight * ai.attackPos.distance(coords) + ai.attackAbsHeight * ai.targetPos.distance(coords)) / (ai.targetPos.distance(coords) + ai.attackPos.distance(coords))) { los.blocked = true; } } if ((hexEl + bldgEl > ai.attackAbsHeight && hexEl + bldgEl > ai.targetAbsHeight) || (hexEl + bldgEl > ai.attackAbsHeight && ai.attackPos.distance(coords) == 1) || (hexEl + bldgEl > ai.targetAbsHeight && ai.targetPos.distance(coords) == 1)) { los.blocked = true; } // check if there's a clear hex between the targets that's higher than // one of them, if we're in underwater combat if (ai.underWaterCombat && hex.terrainLevel(Terrains.WATER) == ITerrain.LEVEL_NONE && (hexEl + bldgEl > ai.attackAbsHeight || hexEl + bldgEl > ai.targetAbsHeight)) { los.blocked = true; } // check for woods or smoke only if not under water if (!ai.underWaterCombat) { if ((hexEl + 2 > ai.attackAbsHeight && hexEl + 2 > ai.targetAbsHeight) || (hexEl + 2 > ai.attackAbsHeight && ai.attackPos.distance(coords) == 1) || (hexEl + 2 > ai.targetAbsHeight && ai.targetPos.distance(coords) == 1)) { // smoke overrides any woods in the hex if L3 smoke rule is off if (!game.getOptions().booleanOption("maxtech_fire")) { if (hex.containsTerrain(Terrains.SMOKE)) { los.heavySmoke++; } else if (hex.terrainLevel(Terrains.WOODS) == 1 || hex.terrainLevel(Terrains.JUNGLE) == 1) { los.lightWoods++; } else if (hex.terrainLevel(Terrains.WOODS) == 2 || hex.terrainLevel(Terrains.JUNGLE) == 2) { los.heavyWoods++; } else if (hex.terrainLevel(Terrains.WOODS) == 3 || hex.terrainLevel(Terrains.JUNGLE) == 3) { los.ultraWoods++; } } // if the L3 fire/smoke rule is on, smoke and woods stack for LOS // so check them both else { if (hex.containsTerrain(Terrains.SMOKE)) { if (hex.terrainLevel(Terrains.SMOKE) == 1) { los.lightSmoke++; } else if (hex.terrainLevel(Terrains.SMOKE) > 1) { los.heavySmoke++; } } if (hex.terrainLevel(Terrains.WOODS) == 1 || hex.terrainLevel(Terrains.JUNGLE) == 1) { los.lightWoods++; } else if (hex.terrainLevel(Terrains.WOODS) == 2 || hex.terrainLevel(Terrains.JUNGLE) == 2) { los.heavyWoods++; } else if (hex.terrainLevel(Terrains.WOODS) == 3 || hex.terrainLevel(Terrains.JUNGLE) == 3) { los.ultraWoods++; } } } } // check for target partial cover if ( ai.targetPos.distance(coords) == 1) { if (los.blocked && game.getOptions().booleanOption("maxtech_partial_cover")) { los.targetCover = COVER_FULL; } else if(hexEl + bldgEl == ai.targetAbsHeight && ai.attackAbsHeight <= ai.targetAbsHeight && ai.targetHeight > 0) { los.targetCover |= COVER_HORIZONTAL; } } // check for attacker partial cover if (ai.attackPos.distance(coords) == 1) { if (los.blocked && game.getOptions().booleanOption("maxtech_partial_cover")) { los.attackerCover = COVER_FULL; } else if(hexEl + bldgEl == ai.attackAbsHeight && ai.attackAbsHeight >= ai.targetAbsHeight && ai.attackHeight > 0) { los.attackerCover |= COVER_HORIZONTAL; } } return los; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -