Let stacks split by shift-click and cross allied or at-war land

This commit is contained in:
2026-09-24 19:41:08 +02:00
parent cc5c57c5f4
commit 36ac6698a8
14 changed files with 379 additions and 27 deletions
+26 -7
View File
@@ -158,7 +158,11 @@ export const movementMethods = {
if (!air) {
for (const coords of path) {
const owner = this.civAt(coords);
if (owner >= 0 && owner !== unit.civ) this._declareWar(unit.civ, owner);
// Crossing an ally's or an at-war nation's land is legal and declares
// nothing; only a neutral third party's land turns the march into war.
if (!this._canTraverseTerritory(unit.civ, owner, -1)) {
this._declareWar(unit.civ, owner);
}
}
}
}
@@ -378,10 +382,11 @@ export const movementMethods = {
// Enemy cities are only open to military units, since walking one in
// captures it.
//
// Foreign territory is a wall as well, so a route never cuts through a third
// party and provokes a war for purely technical reasons. The one exception is
// the civilisation being targeted (`targetCiv`, the owner of the goal), whose
// land the route may cross to reach it.
// A foreign land is a wall only when its owner is a neutral third party: a
// route may cross the land of the nation it is aimed at (`targetCiv`), of an
// ally, and of a nation already at war (the war was declared regardless), as
// `_canTraverseTerritory` spells out. So a march never drags a bystander into
// a war for purely technical reasons.
//
// A hostile unit or city is only a valid destination (`isGoal`), never a tile
// to pass through: pathfinding routes around enemies instead of through them.
@@ -411,8 +416,9 @@ export const movementMethods = {
}
const owner = this.civAt(coords);
// A spy slips through foreign territory at will; every other unit treats a
// third party's land as a wall (the targeted civilisation is exempt).
if (!air && !isGoal && owner >= 0 && owner !== unit.civ && owner !== targetCiv && !proto.spy) {
// neutral third party's land as a wall (the targeted civilisation, an ally
// and anyone already at war are all exempt).
if (!air && !isGoal && !proto.spy && !this._canTraverseTerritory(unit.civ, owner, targetCiv)) {
return false;
}
const occupants = occupancy
@@ -437,6 +443,19 @@ export const movementMethods = {
return true;
},
// Whether `civ` may cross land owned by `owner` on a through-route. A nation's
// own land is always open, and so is the land of the nation a route is aimed
// at (`targetCiv`), of an ally, and of a nation already at war -- the war was
// declared regardless. Only a neutral third party is a wall, so a march never
// drags a bystander into a war for purely technical reasons. Mirrored by the
// browser in map_view/motion.js `_canTraverseTerritory`.
_canTraverseTerritory(civ, owner, targetCiv = -1) {
if (owner < 0 || owner === civ || owner === targetCiv) return true;
if (this.isAtWar(civ, owner)) return true;
if (this.areAllied(civ, owner)) return true;
return false;
},
// Every unit bucketed by the tile it stands on, so a search can look up
// occupancy in constant time instead of scanning the whole army for every
// tile it considers. The mover is left out: it never blocks itself. The