Index: ps/trunk/binaries/data/config/default.cfg =================================================================== --- ps/trunk/binaries/data/config/default.cfg +++ ps/trunk/binaries/data/config/default.cfg @@ -332,7 +332,8 @@ unloadturrets = "U" ; Unload turreted units. leaveturret = "U" ; Leave turret point. move = "" ; Modifier to move to a point instead of another action (e.g. gather) -attack = Ctrl ; Modifier to attack instead of another action (e.g. capture) +capture = Ctrl ; Modifier to capture instead of another action (e.g. attack) +attack = "" ; Modifier to attack instead of another action (e.g. capture) attackmove = Ctrl ; Modifier to attackmove when clicking on a point attackmoveUnit = "Ctrl+Q" ; Modifier to attackmove targeting only units when clicking on a point garrison = Ctrl ; Modifier to garrison when clicking on building Index: ps/trunk/binaries/data/mods/public/gui/hotkeys/spec/ingame.json =================================================================== --- ps/trunk/binaries/data/mods/public/gui/hotkeys/spec/ingame.json +++ ps/trunk/binaries/data/mods/public/gui/hotkeys/spec/ingame.json @@ -43,6 +43,10 @@ "name": "Force move", "desc": "Modifier to move to a point instead of another action (e.g. gather)." }, + "session.capture": { + "name": "Force capture", + "desc": "Modifier to capture instead of another action (e.g. attack)." + }, "session.attack": { "name": "Force attack", "desc": "Modifier to attack instead of another action (e.g. capture)." Index: ps/trunk/binaries/data/mods/public/gui/manual/intro.txt =================================================================== --- ps/trunk/binaries/data/mods/public/gui/manual/intro.txt +++ ps/trunk/binaries/data/mods/public/gui/manual/intro.txt @@ -113,9 +113,10 @@ hotkey.selection.singleselection – Modifier to select units individually, opposed to per formation. Right Click with a structure(s) selected – Set a rally point for units created/ungarrisoned from that structure hotkey.session.garrison + Right Click with unit(s) selected – Garrison (If the cursor is over an own or allied structure) - hotkey.session.attack + Right Click with unit(s) selected – Attack (instead of capture or gather) + hotkey.session.attack + Right Click with unit(s) selected – Attack (instead of another action) hotkey.session.attackmove + Right Click with unit(s) selected – Attack move (by default all enemy units and structures along the way are targeted) hotkey.session.attackmoveUnit + Right Click with unit(s) selected – Attack move, only units along the way are targeted + hotkey.session.capture + Right Click with unit(s) selected – Capture (instead of another action) hotkey.session.snaptoedges + Mouse Move near structures – Align the new structure with an existing nearby structure hotkey.session.flare + Right Click – Send a flare to your allies Index: ps/trunk/binaries/data/mods/public/gui/session/unit_actions.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/unit_actions.js +++ ps/trunk/binaries/data/mods/public/gui/session/unit_actions.js @@ -181,6 +181,11 @@ }) }; }, + "hotkeyActionCheck": function(target, selection) + { + return Engine.HotkeyIsPressed("session.capture") && + this.actionCheck(target, selection); + }, "actionCheck": function(target, selection) { let actionInfo = getActionInfo("capture", target, selection); @@ -191,7 +196,7 @@ "firstAbleEntity": actionInfo.entity }; }, - "specificness": 9, + "specificness": 10, }, "attack": @@ -243,7 +248,7 @@ "firstAbleEntity": actionInfo.entity }; }, - "specificness": 10, + "specificness": 9, }, "call-to-arms": { @@ -261,7 +266,7 @@ "targetClasses": targetClasses, "queued": queued, "pushFront": pushFront, - "allowCapture": true, + "allowCapture": Engine.HotkeyIsPressed("session.capture"), "formation": g_AutoFormation.getNull() }); return true; @@ -305,7 +310,7 @@ "target": action.target, "targetClasses": { "attack": g_PatrolTargets }, "queued": queued, - "allowCapture": false, + "allowCapture": Engine.HotkeyIsPressed("session.capture"), "formation": g_AutoFormation.getDefault() }); Index: ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js +++ ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js @@ -185,6 +185,8 @@ UnitAI.prototype.notifyToCheerInRange = 30; +UnitAI.prototype.DEFAULT_CAPTURE = false; + // To reject an order, use 'return this.FinishOrder();' const ACCEPT_ORDER = true; @@ -535,7 +537,7 @@ if (!this.AbleToMove() && !this.CheckTargetRange(msg.data.target, IID_Attack, bestAttack)) return this.FinishOrder(); - this.PushOrderFront("Attack", { "target": msg.data.target, "force": !!msg.data.force, "hunting": true, "allowCapture": false }); + this.PushOrderFront("Attack", { "target": msg.data.target, "force": !!msg.data.force, "hunting": true }); return ACCEPT_ORDER; } @@ -767,7 +769,6 @@ "Order.Attack": function(msg) { let target = msg.data.target; - let allowCapture = msg.data.allowCapture; let cmpTargetUnitAI = Engine.QueryInterface(target, IID_UnitAI); if (cmpTargetUnitAI && cmpTargetUnitAI.IsFormationMember()) target = cmpTargetUnitAI.GetFormationController(); @@ -781,7 +782,7 @@ } return this.FinishOrder(); } - this.CallMemberFunction("Attack", [target, allowCapture, false]); + this.CallMemberFunction("Attack", [target, msg.data.allowCapture, false]); let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); if (cmpAttack && cmpAttack.CanAttackAsFormation()) this.SetNextState("COMBAT.ATTACKING"); @@ -832,7 +833,7 @@ } return ACCEPT_ORDER; } - this.PushOrderFront("Attack", { "target": msg.data.target, "force": !!msg.data.force, "hunting": true, "allowCapture": false, "min": 0, "max": 10 }); + this.PushOrderFront("Attack", { "target": msg.data.target, "force": !!msg.data.force, "hunting": true, "min": 0, "max": 10 }); return ACCEPT_ORDER; } @@ -1300,8 +1301,7 @@ "ATTACKING": { // Wait for individual members to finish "enter": function(msg) { - let target = this.order.data.target; - let allowCapture = this.order.data.allowCapture; + const target = this.order.data.target; if (!this.CheckFormationTargetAttackRange(target)) { if (this.CanAttack(target) && this.CheckTargetVisible(target)) @@ -1322,8 +1322,7 @@ }, "Timer": function(msg) { - let target = this.order.data.target; - let allowCapture = this.order.data.allowCapture; + const target = this.order.data.target; if (!this.CheckFormationTargetAttackRange(target)) { if (this.CanAttack(target) && this.CheckTargetVisible(target)) @@ -1568,7 +1567,7 @@ } if (this.CheckTargetVisible(msg.data.attacker)) - this.PushOrderFront("Attack", { "target": msg.data.attacker, "force": false, "allowCapture": true }); + this.PushOrderFront("Attack", { "target": msg.data.attacker, "force": false }); else { var cmpPosition = Engine.QueryInterface(msg.data.attacker, IID_Position); @@ -2112,9 +2111,6 @@ this.PushOrder("WalkAndFight", { "x": lastPos.x, "z": lastPos.z, "force": false, - // Force to true - otherwise structures might be attacked instead of captured, - // which is generally not expected (attacking units usually has allowCapture false). - "allowCapture": true }); return; } @@ -4992,12 +4988,9 @@ return distance < range; }; -UnitAI.prototype.GetBestAttackAgainst = function(target, allowCapture) +UnitAI.prototype.GetBestAttackAgainst = function(target, allowCapture = this.DEFAULT_CAPTURE) { - var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); - if (!cmpAttack) - return undefined; - return cmpAttack.GetBestAttackAgainst(target, allowCapture); + return Engine.QueryInterface(this.entity, IID_Attack)?.GetBestAttackAgainst(target, allowCapture); }; /** @@ -5011,7 +5004,7 @@ if (!target) return false; - this.PushOrderFront("Attack", { "target": target, "force": false, "allowCapture": true }); + this.PushOrderFront("Attack", { "target": target, "force": false }); return true; }; @@ -5030,7 +5023,7 @@ if (!target) return false; - this.PushOrderFront("Attack", { "target": target, "force": false, "allowCapture": true }); + this.PushOrderFront("Attack", { "target": target, "force": false }); return true; }; @@ -5454,12 +5447,12 @@ * to a player order, and so is forced. * If targetClasses is given, only entities matching the targetClasses can be attacked. */ -UnitAI.prototype.WalkAndFight = function(x, z, targetClasses, allowCapture = true, queued = false, pushFront = false) +UnitAI.prototype.WalkAndFight = function(x, z, targetClasses, allowCapture = this.DEFAULT_CAPTURE, queued = false, pushFront = false) { this.AddOrder("WalkAndFight", { "x": x, "z": z, "targetClasses": targetClasses, "allowCapture": allowCapture, "force": true }, queued, pushFront); }; -UnitAI.prototype.Patrol = function(x, z, targetClasses, allowCapture = true, queued = false, pushFront = false) +UnitAI.prototype.Patrol = function(x, z, targetClasses, allowCapture = this.DEFAULT_CAPTURE, queued = false, pushFront = false) { if (!this.CanPatrol()) { @@ -5497,7 +5490,7 @@ /** * Adds attack order to the queue, forced by the player. */ -UnitAI.prototype.Attack = function(target, allowCapture = true, queued = false, pushFront = false) +UnitAI.prototype.Attack = function(target, allowCapture = this.DEFAULT_CAPTURE, queued = false, pushFront = false) { if (!this.CanAttack(target)) { @@ -6033,7 +6026,7 @@ const order = { "target": target, "force": false, - "allowCapture": this.order?.data?.allowCapture + "allowCapture": this.order?.data?.allowCapture || this.DEFAULT_CAPTURE }; if (this.IsFormationMember()) this.ReplaceOrder("Attack", order); Index: ps/trunk/binaries/data/mods/public/simulation/helpers/Commands.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/helpers/Commands.js +++ ps/trunk/binaries/data/mods/public/simulation/helpers/Commands.js @@ -183,41 +183,30 @@ "attack-walk": function(player, cmd, data) { - let allowCapture = cmd.allowCapture || cmd.allowCapture == null; - GetFormationUnitAIs(data.entities, player, cmd, data.formation).forEach(cmpUnitAI => { - cmpUnitAI.WalkAndFight(cmd.x, cmd.z, cmd.targetClasses, allowCapture, cmd.queued, cmd.pushFront); + cmpUnitAI.WalkAndFight(cmd.x, cmd.z, cmd.targetClasses, cmd.allowCapture, cmd.queued, cmd.pushFront); }); }, "attack-walk-custom": function(player, cmd, data) { - let allowCapture = cmd.allowCapture || cmd.allowCapture == null; for (let ent in data.entities) GetFormationUnitAIs([data.entities[ent]], player, cmd, data.formation).forEach(cmpUnitAI => { - cmpUnitAI.WalkAndFight(cmd.targetPositions[ent].x, cmd.targetPositions[ent].y, cmd.targetClasses, allowCapture, cmd.queued, cmd.pushFront); + cmpUnitAI.WalkAndFight(cmd.targetPositions[ent].x, cmd.targetPositions[ent].y, cmd.targetClasses, cmd.allowCapture, cmd.queued, cmd.pushFront); }); }, "attack": function(player, cmd, data) { - let allowCapture = cmd.allowCapture || cmd.allowCapture == null; - - if (g_DebugCommands && !allowCapture && - !(IsOwnedByEnemyOfPlayer(player, cmd.target) || IsOwnedByNeutralOfPlayer(player, cmd.target))) - warn("Invalid command: attack target is not owned by enemy of player "+player+": "+uneval(cmd)); - GetFormationUnitAIs(data.entities, player, cmd, data.formation).forEach(cmpUnitAI => { - cmpUnitAI.Attack(cmd.target, allowCapture, cmd.queued, cmd.pushFront); + cmpUnitAI.Attack(cmd.target, cmd.allowCapture, cmd.queued, cmd.pushFront); }); }, "patrol": function(player, cmd, data) { - let allowCapture = cmd.allowCapture || cmd.allowCapture == null; - GetFormationUnitAIs(data.entities, player, cmd, data.formation).forEach(cmpUnitAI => - cmpUnitAI.Patrol(cmd.x, cmd.z, cmd.targetClasses, allowCapture, cmd.queued) + cmpUnitAI.Patrol(cmd.x, cmd.z, cmd.targetClasses, cmd.allowCapture, cmd.queued) ); },