Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions bin/common/Language/BOXCE/en-US.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,6 @@ en-US:
WRT_VANILLA: "vanilla formula"
hangarType: "Hangar Type"
spaceOccupied: "Space Occupied"
# Diagonal Terrain melee
STR_DIAG_TERRAIN_MELEE: "Diagonal Terrain Melee"
STR_DIAG_TERRAIN_MELEE_DESC: "Hitting terrain objects at diagonal directions with melee.{NEWLINE}Requires activated \"TerrainMeleeExtended\" feature.{NEWLINE}{ALT}Alt+click{ALT} - to hit objects at current and adjacent tiles."
3 changes: 3 additions & 0 deletions bin/common/Language/BOXCE/ru.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,6 @@ ru:
WRT_VANILLA: "Формула из оригинальной игры"
hangarType: "Тип ангара"
spaceOccupied: "Место занято"
# Diagonal Terrain melee
STR_DIAG_TERRAIN_MELEE: "Рукопашно местность по диагонали "
STR_DIAG_TERRAIN_MELEE_DESC: "Атака объектов ландшафта по диагонали в ближнем бою.{NEWLINE}Требует включения опции \"terrainMeleeExtended\".{NEWLINE}{ALT}Alt+клик{ALT} - бить объекты на текущей и соседних клетках."
249 changes: 149 additions & 100 deletions src/Battlescape/TileEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3231,6 +3231,7 @@ TileEngine::ReactionScore TileEngine::determineReactionType(BattleUnit *unit, Ba
return reaction;
}
}

if (_save->canUseWeapon(weapon, unit, false, BA_SNAPSHOT))
{
// has a gun capable of snap shot with ammo
Expand Down Expand Up @@ -5947,7 +5948,7 @@ bool TileEngine::validTerrainMeleeRange(BattleAction* action)
action->terrainMeleeTilePart = 0;

if (action->weapon)
{
{ // is melee weapon able to hit terrain ?
auto wRule = action->weapon->getRules();
if (wRule->getBattleType() == BT_MELEE)
{
Expand All @@ -5961,79 +5962,115 @@ bool TileEngine::validTerrainMeleeRange(BattleAction* action)
}
}

Position pos = action->actor->getPosition();
int direction = action->actor->getDirection();
BattleUnit* attacker = action->actor;

if (direction < 0 || direction > 7)
{
{ // only horisontal direction are allowed
return false;
}
if (direction % 2 != 0)
}
if (!Options::diagTerrainMelee && direction % 2 != 0)
{
// diagonal directions are not supported
// diagonal directions are not supported. pWWWa: if proper option is not activated
return false;
}

Position pos = action->actor->getPosition();
Position p;
Pathfinding::directionToVector(direction, &p);

Tile* originTile = _save->getTile(pos);
Tile* originTile2 = originTile;
if (originTile && originTile->getTerrainLevel() <= -16)

if (originTile && originTile->getTerrainLevel() <= -16 && !_save->isAltPressed(true))
{
// if we are on the upper part of stairs, target one tile above
// if we are on the upper part of stairs, target one tile above. pWWWa: let leave at the same tile altitude with pressed Alt (if there are need to hit stair under feet).
pos += Position(0, 0, 1);
originTile = _save->getTile(pos);
}
Tile* neighbouringTile = _save->getTile(pos + p);
Tile* neighbouringTile2 = nullptr;
int size = attacker->getArmor()->getSize();
if (size > 1)

Tile* originTile2 = originTile;
Tile* neighbouringTile = _save->getTile(pos + p);
Tile* neighbouringTile2 = nullptr;
Tile* neighbouringTile3 = nullptr;
int size = action->actor->getArmor()->getSize() - 1; // pWWWa: attacker variable removed, cause it used only once

switch (direction)
{
if (direction == 0)
{
// North
originTile2 = _save->getTile(pos + Position(1, 0, 0));
neighbouringTile2 = _save->getTile(pos + p + Position(1, 0, 0));
}
else if (direction == 2)
{
// East
neighbouringTile = _save->getTile(pos + p + Position(1, 0, 0));
neighbouringTile2 = _save->getTile(pos + p + Position(1, 1, 0));
}
else if (direction == 4)
{
// South
neighbouringTile = _save->getTile(pos + p + Position(0, 1, 0));
neighbouringTile2 = _save->getTile(pos + p + Position(1, 1, 0));
}
else if (direction == 6)
{
// West
originTile2 = _save->getTile(pos + Position(0, 1, 0));
neighbouringTile2 = _save->getTile(pos + p + Position(0, 1, 0));
}
if (!neighbouringTile2 || !originTile2)
{
return false;
}
case 0: // North
originTile2 = _save->getTile(pos + Position(size, 0, 0));
neighbouringTile2 = _save->getTile(pos + p + Position(size, 0, 0));
break;

case 2: // East
originTile = _save->getTile(pos + Position(size, 0, 0));
originTile2 = _save->getTile(pos + Position(size, size, 0));
neighbouringTile = _save->getTile(pos + p + Position(size, 0, 0));
neighbouringTile2 = _save->getTile(pos + p + Position(size, size, 0));
break;

case 4: // South
originTile = _save->getTile(pos + Position(0, size, 0));
originTile2 = _save->getTile(pos + Position(size, size, 0));
neighbouringTile = _save->getTile(pos + p + Position(0, size, 0));
neighbouringTile2 = _save->getTile(pos + p + Position(size, size, 0));
break;

case 6: // West
originTile2 = _save->getTile(pos + Position(0, size, 0));
neighbouringTile2 = _save->getTile(pos + p + Position(0, size, 0));
break;

case 1: // North-East
originTile = _save->getTile(pos + Position(size, 0, 0));
neighbouringTile = _save->getTile(pos + p + Position(size, 0, 0));
neighbouringTile2 = _save->getTile(pos + Position(size, -1, 0));
neighbouringTile3 = _save->getTile(pos + Position(size + 1, 0, 0));
break;

case 3: // South-East
originTile = _save->getTile(pos + Position(size, size, 0));
neighbouringTile = _save->getTile(pos + p + Position(size, size, 0));
neighbouringTile2 = _save->getTile(pos + p + Position(size, size - 1, 0));
neighbouringTile3 = _save->getTile(pos + p + Position(size - 1, size, 0));
break;

case 5: // South-West
originTile = _save->getTile(pos + Position(0, size, 0));
neighbouringTile = _save->getTile(pos + p + Position(0, size, 0));
neighbouringTile2 = _save->getTile(pos + Position(0, size + 1, 0));
neighbouringTile3 = _save->getTile(pos + Position(- 1, size, 0));
break;

case 7: //North-West
neighbouringTile2 = _save->getTile(pos + Position(-1, 0, 0));
neighbouringTile3 = _save->getTile(pos + Position(0, -1, 0));
}

if (originTile && neighbouringTile)
{
auto setTarget = [](Tile* tt, TilePart tp, BattleAction* aa, int dir = -1) -> bool
{
Position origin = tt->getSavedGame()->getTileEngine()->getSightOriginVoxel(aa->actor, tt) + Position(0, 0, -4);
Position target;

if (!tt->getSavedGame()->getTileEngine()->canTargetTile(&origin, tt, tp, &target, aa->actor, false))
{ // is tile reachable (more predictable targeting queue at diagonals)
return false;
}

MapData* obj = tt->getMapData(tp);
if (obj)
{
if (dir > -1 && tp == O_OBJECT)
{
if (dir > -1 && tp == O_OBJECT && !(Options::diagTerrainMelee && tt->getSavedGame()->isAltPressed(true)))
{
auto bigWall = obj->getBigWall();
if (dir == 0 /*north*/ && bigWall != Pathfinding::BIGWALLNORTH && bigWall != Pathfinding::BIGWALLWESTANDNORTH) return false;
if (dir == 2 /*east */ && bigWall != Pathfinding::BIGWALLEAST && bigWall != Pathfinding::BIGWALLEASTANDSOUTH) return false;
if (dir == 4 /*south*/ && bigWall != Pathfinding::BIGWALLSOUTH && bigWall != Pathfinding::BIGWALLEASTANDSOUTH) return false;
if (dir == 6 /*west */ && bigWall != Pathfinding::BIGWALLWEST && bigWall != Pathfinding::BIGWALLWESTANDNORTH) return false;
}
if (dir == 1 /*NW */ && bigWall != Pathfinding::BIGWALLNWSE) return false;
if (dir == 3 /*NE */ && bigWall != Pathfinding::BIGWALLNESW && bigWall != Pathfinding::BIGWALLEASTANDSOUTH) return false;
if (dir == 5 /*SE */ && bigWall != Pathfinding::BIGWALLNWSE) return false;
if (dir == 7 /*SW */ && bigWall != Pathfinding::BIGWALLNESW && bigWall != Pathfinding::BIGWALLWESTANDNORTH) return false;
}
if (tp != O_OBJECT && !obj->isDoor() && !obj->isUFODoor() && tt->getTUCost(tp, MT_WALK) != Pathfinding::INVALID_MOVE_COST)
{
// it is possible to walk through this (rubble) wall... no need to attack it
Expand All @@ -6058,76 +6095,88 @@ bool TileEngine::validTerrainMeleeRange(BattleAction* action)
return false;
};

if (setTarget(originTile, O_OBJECT, action, direction))
if (setTarget(originTile, O_OBJECT, action, direction) ||
setTarget(originTile2, O_OBJECT, action, direction))
{
// All directions: target the object (marked as big wall) on the current tile
return true;
}
if (size > 1)

switch (direction)
{
if (setTarget(originTile2, O_OBJECT, action, direction))
{
// All directions
case 0: // North
if (setTarget(originTile, O_NORTHWALL, action) ||
setTarget(originTile2, O_NORTHWALL, action) ||
setTarget(neighbouringTile2, O_WESTWALL, action) && size)
return true;
}
}
break;

if (direction == 0 && setTarget(originTile, O_NORTHWALL, action))
{
// North: target the north wall of the same tile
return true;
}
else if (direction == 2 && setTarget(neighbouringTile, O_WESTWALL, action))
{
// East: target the west wall of the neighbouring tile
return true;
}
else if (direction == 4 && setTarget(neighbouringTile, O_NORTHWALL, action))
{
// South: target the north wall of the neighbouring tile
return true;
}
else if (direction == 6 && setTarget(originTile, O_WESTWALL, action))
{
// West: target the west wall of the same tile
return true;
}
if (size > 1)
{
if (direction == 0 && setTarget(originTile2, O_NORTHWALL, action))
{
// North
case 2: // East
if (setTarget(neighbouringTile, O_WESTWALL, action) ||
setTarget(neighbouringTile2, O_WESTWALL, action) ||
setTarget(neighbouringTile2, O_NORTHWALL, action) && size)
return true;
}
else if (direction == 2 && setTarget(neighbouringTile2, O_WESTWALL, action))
{
// East
break;

case 4: // South
if (setTarget(neighbouringTile, O_NORTHWALL, action) ||
setTarget(neighbouringTile2, O_NORTHWALL, action) ||
setTarget(neighbouringTile2, O_WESTWALL, action) && size)
return true;
}
else if (direction == 4 && setTarget(neighbouringTile2, O_NORTHWALL, action))
{
// South
break;

case 6: // West
if (setTarget(originTile, O_WESTWALL, action) ||
setTarget(originTile2, O_WESTWALL, action) ||
setTarget(neighbouringTile2, O_NORTHWALL, action) && size)
return true;
}
else if (direction == 6 && setTarget(originTile2, O_WESTWALL, action))
{
// West
break;

case 1: // North-East
if (setTarget(neighbouringTile, O_WESTWALL, action) || // 3
setTarget(neighbouringTile3, O_NORTHWALL, action) || // 4
setTarget(originTile, O_NORTHWALL, action) || // 1
setTarget(neighbouringTile3, O_WESTWALL, action)) // 2
return true;
break;

case 3: // South-East
if (setTarget(neighbouringTile, O_WESTWALL, action) || // 3
setTarget(neighbouringTile, O_NORTHWALL, action) || // 4
setTarget(neighbouringTile3, O_NORTHWALL, action) || // 1
setTarget(neighbouringTile2, O_WESTWALL, action)) // 2
return true;
break;

case 5: // North-East
if (setTarget(neighbouringTile, O_NORTHWALL, action) || // 4
setTarget(neighbouringTile2, O_NORTHWALL, action) || // 1
setTarget(neighbouringTile2, O_WESTWALL, action) || // 3
setTarget(originTile, O_WESTWALL, action)) // 2
return true;
break;

case 7: // North-West
if (setTarget(neighbouringTile3, O_WESTWALL, action) || // 4
setTarget(neighbouringTile2, O_NORTHWALL, action) || // 3
setTarget(originTile, O_NORTHWALL, action) || // 2
setTarget(originTile, O_WESTWALL, action)) // 1
return true;
}
}

if (setTarget(neighbouringTile, O_OBJECT, action))
{
// All directions: target the object on the neighbouring tile
if (setTarget(neighbouringTile, O_OBJECT, action) ||
setTarget(neighbouringTile2, O_OBJECT, action) && size && !(direction % 2))
{
// All directions: target the object on the neighbouring tile + big unit's second part at non-diagonal direction included
return true;
}
if (size > 1)

if ( _save->isAltPressed(true) && direction % 2 &&
( setTarget(neighbouringTile2, O_OBJECT, action) ||
setTarget(neighbouringTile3, O_OBJECT, action) ) )
{
if (setTarget(neighbouringTile2, O_OBJECT, action))
{
// All directions
return true;
}
// Diagonal terrain object targeting helper. Suitable for hitting big walls and terrain stuff at adjacent tiles.
return true;
}
}

Expand Down
10 changes: 9 additions & 1 deletion src/Engine/Options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -569,16 +569,21 @@ void createOptionsOTHER()

void createAdvancedOptionsOTHER()
{
// OTHER options General
_info.push_back(OptionInfo(OPTION_OTHER, "showCraftHangar", &showCraftHangar, false, "STR_SHOW_CRAFT_HANGAR", "STR_GENERAL"));
_info.push_back(OptionInfo(OPTION_OTHER, "forcedAbsoluteCosts", &forcedAbsoluteCosts, false, "STR_FORCED_ABSOLUTE_COSTS", "STR_BATTLESCAPE"));

// OTHER options Geoscape
_info.push_back(OptionInfo(OPTION_OTHER, "dogfightAI", &dogfightAI, true, "STR_DOGFIGHTAI", "STR_GEOSCAPE"));
_info.push_back(OptionInfo(OPTION_OTHER, "aggressiveRetaliation", &aggressiveRetaliation, true, "STR_AGGRESSIVERETALIATION", "STR_GEOSCAPE"));
_info.push_back(OptionInfo(OPTION_OTHER, "displayHiddenAlienActivity", &displayHiddenAlienActivity, 0, "STR_DISPLAY_HIDDEN_ALIEN_ACTIVITY", "STR_GEOSCAPE"));

// OTHER options Basescape
_info.push_back(OptionInfo(OPTION_OTHER, "baseDefenseProbability", &baseDefenseProbability, false, "STR_DISPLAY_BASE_DEFENSE_PROBABILITY", "STR_BASESCAPE"));
_info.push_back(OptionInfo(OPTION_OTHER, "baseDetectionProbability", &baseDetectionProbability, false, "STR_DISPLAY_BASE_DETECTION_PROBABILITY", "STR_BASESCAPE"));

// OTHER options Battlescape
_info.push_back(OptionInfo(OPTION_OTHER, "diagTerrainMelee", &diagTerrainMelee, false, "STR_DIAG_TERRAIN_MELEE", "STR_BATTLESCAPE"));
_info.push_back(OptionInfo(OPTION_OTHER, "forcedAbsoluteCosts", &forcedAbsoluteCosts, false, "STR_FORCED_ABSOLUTE_COSTS", "STR_BATTLESCAPE"));
_info.push_back(OptionInfo(OPTION_OTHER, "shootingSpreadMode", &shootingSpreadMode, 1, "STR_SHOOTING_SPREAD_MODE", "STR_BATTLESCAPE"));
_info.push_back(OptionInfo(OPTION_OTHER, "useChanceToHit", &useChanceToHit, true, "STR_BATTLECHANCETOHIT", "STR_BATTLESCAPE"));
_info.push_back(OptionInfo(OPTION_OTHER, "battleRealisticAccuracy", &battleRealisticAccuracy, false, "STR_BATTLEREALISTICACCURACY", "STR_BATTLESCAPE"));
Expand All @@ -599,13 +604,16 @@ void createAdvancedOptionsOTHER()
_info.push_back(OptionInfo(OPTION_OTHER, "preprimeGrenades", &preprimeGrenades, 1, "STR_PREPRIMEGRENADES", "STR_BATTLESCAPE"));
_info.push_back(OptionInfo(OPTION_OTHER, "updateTurnsSinceSeenByClue", &updateTurnsSinceSeenByClue, true, "STR_UPDATETURNSSINCESEENBYCLUE", "STR_BATTLESCAPE"));

// OTHER options AI
_info.push_back(OptionInfo(OPTION_OTHER, "brutalAI", &brutalAI, 1, "STR_BRUTALAI", "STR_AI"));
_info.push_back(OptionInfo(OPTION_OTHER, "brutalCivilians", &brutalCivilians, 0, "STR_BRUTALCIVILIANS", "STR_AI"));
_info.push_back(OptionInfo(OPTION_OTHER, "ignoreDelay", &ignoreDelay, true, "STR_IGNOREDELAY", "STR_AI"));
_info.push_back(OptionInfo(OPTION_OTHER, "allowPreprime", &allowPreprime, true, "STR_ALLOWPREPRIME", "STR_AI"));
_info.push_back(OptionInfo(OPTION_OTHER, "avoidMines", &avoidMines, true, "STR_AVOIDMINES", "STR_AI"));
_info.push_back(OptionInfo(OPTION_OTHER, "aiPeformance", &aiPerformanceOptimization, false, "STR_AI_PERFORMANCE", "STR_AI"));
_info.push_back(OptionInfo(OPTION_OTHER, "aiCheatMode", &aiCheatMode, 0, "STR_AICHEATMODE", "STR_AI"));

// OTHER options Autoplay
_info.push_back(OptionInfo(OPTION_OTHER, "autoCombat", &autoCombat, false, "STR_AUTOCOMBAT", "STR_AUTO"));
_info.push_back(OptionInfo(OPTION_OTHER, "autoCombatEachCombat", &autoCombatEachCombat, true, "STR_AUTOCOMBAT_EACH_COMBAT", "STR_AUTO"));
_info.push_back(OptionInfo(OPTION_OTHER, "autoCombatEachTurn", &autoCombatEachTurn, true, "STR_AUTOCOMBAT_EACH_TURN", "STR_AUTO"));
Expand Down
2 changes: 1 addition & 1 deletion src/Engine/Options.inc.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ OPT PathPreview battleNewPreviewPath;
OPT int shootingSpreadMode, battleRealisticShotDispersion, battleRealisticCoverEfficiency, battleScrollSpeed, battleDragScrollButton, battleFireSpeed, battleXcomSpeed, battleAlienSpeed, battleExplosionHeight, battlescapeScale, battleTerrainSquishyness, preprimeGrenades, battleThrownSpeed;
OPT bool traceAI, battleInstantGrenade, battleNotifyDeath, battleTooltips, battleHairBleach, battleAutoEnd,
strafe, forceFire, showMoreStatsInInventoryView, allowPsionicCapture, skipNextTurnScreen, disableAutoEquip, battleDragScrollInvert,
battleUFOExtenderAccuracy, useChanceToHit, battleRealisticAccuracy, battleRealisticImprovedAimed, battleRealisticImprovedLof, battleRealisticDisplayRolls, battleRealisticDisplayOthersRolls, battleAltGrenades, battleConfirmFireMode, battleSmoothCamera, noAlienPanicMessages, alienBleeding, instantPrime, strictBlockedChecking, updateTurnsSinceSeenByClue;
battleUFOExtenderAccuracy, useChanceToHit, battleRealisticAccuracy, battleRealisticImprovedAimed, battleRealisticImprovedLof, battleRealisticDisplayRolls, battleRealisticDisplayOthersRolls, battleAltGrenades, battleConfirmFireMode, battleSmoothCamera, noAlienPanicMessages, alienBleeding, instantPrime, strictBlockedChecking, updateTurnsSinceSeenByClue, diagTerrainMelee;
OPT SDLKey keyBattleLeft, keyBattleRight, keyBattleUp, keyBattleDown, keyBattleLevelUp, keyBattleLevelDown, keyBattleCenterUnit, keyBattlePrevUnit, keyBattleNextUnit, keyBattleDeselectUnit,
keyBattleUseLeftHand, keyBattleUseRightHand, keyBattleInventory, keyBattleMap, keyBattleOptions, keyBattleEndTurn, keyBattleAbort, keyBattleStats, keyBattleKneel,
keyBattleReserveKneel, keyBattleReload, keyBattlePersonalLighting, keyBattleReserveNone, keyBattleReserveSnap, keyBattleReserveAimed, keyBattleReserveAuto,
Expand Down