1
0
forked from 0ad/0ad

Enable production entities to autoqueue.

This adds a new command button that enables training of units
automatically.
Use:
- Enable auto-queue.
- Train an entity.

This adds a new item to the queue whenever the previous item starts,
such that good micro is more resource-efficient.

Patch by: @azayrahmad
Differential revision: https://code.wildfiregames.com/D3865
Comments by: @Langbart, @nani, @Stan, @wraitii
This was SVN commit r25381.
This commit is contained in:
Freagarach 2021-05-05 06:12:31 +00:00
parent fe3ff1b0ff
commit 51ab4315ff
11 changed files with 215 additions and 2 deletions

Binary file not shown.

Binary file not shown.

View File

@ -36,6 +36,7 @@
{ "nick": "Arfrever", "name": "Arfrever Frehtes Taifersar Arahesis" },
{ "nick": "ArnH", "name": "Arno Hemelhof" },
{ "nick": "Aurium", "name": "Aurélio Heckert" },
{ "nick": "azayrahmad", "name": "Aziz Rahmad" },
{ "nick": "badmadblacksad", "name": "Martin F" },
{ "nick": "badosu", "name": "Amadeus Folego" },
{ "nick": "bb", "name": "Bouke Jansen" },

View File

@ -158,6 +158,14 @@
"session.queueunit.8": {
"name": "Queue 8th unit",
"desc": "Add eighth unit type to queue."
},
"session.queueunit.autoqueueon": {
"name": "Activate auto-queue",
"desc": "Turn on Auto-Queue for selected structures."
},
"session.queueunit.autoqueueoff": {
"name": "Deactivate auto-queue",
"desc": "Turn off Auto-Queue for selected structures."
}
}
}

View File

@ -34,4 +34,14 @@
<object hotkey="session.queueunit.7">
<action on="KeyDown">addTrainingByPosition(6);</action>
</object>
<!-- enable autoqueue -->
<object hotkey="session.queueunit.autoqueueon">
<action on="KeyDown">turnAutoQueueOn();</action>
</object>
<!-- disable autoqueue -->
<object hotkey="session.queueunit.autoqueueoff">
<action on="KeyDown">turnAutoQueueOff();</action>
</object>
</object>

View File

@ -546,3 +546,25 @@ function endOfAlert()
})
});
}
function turnAutoQueueOn()
{
Engine.PostNetworkCommand({
"type": "autoqueue-on",
"entities": g_Selection.toList().filter(ent => {
let state = GetEntityState(ent);
return state && !!state.production.entities;
})
});
}
function turnAutoQueueOff()
{
Engine.PostNetworkCommand({
"type": "autoqueue-off",
"entities": g_Selection.toList().filter(ent => {
let state = GetEntityState(ent);
return state && !!state.production.entities;
})
});
}

View File

@ -1715,7 +1715,47 @@ var g_EntityCommands =
// This command button is always disabled.
},
"allowedPlayers": ["Ally", "Observer"]
}
},
"autoqueue-on": {
"getInfo": function(entStates)
{
if (entStates.every(entState => !entState.production || !entState.production.entities || entState.production.autoqueue))
return false;
return {
"tooltip": colorizeHotkey("%(hotkey)s" + " ", "session.queueunit.autoqueueon") +
translate("Activate auto-queue for selected structures."),
"icon": "autoqueue-on.png",
"enabled": true
};
},
"execute": function(entStates)
{
if (entStates.length)
turnAutoQueueOn();
},
"allowedPlayers": ["Player"]
},
"autoqueue-off": {
"getInfo": function(entStates)
{
if (entStates.every(entState => !entState.production || !entState.production.entities || !entState.production.autoqueue))
return false;
return {
"tooltip": colorizeHotkey("%(hotkey)s" + " ", "session.queueunit.autoqueueoff") +
translate("Deactivate auto-queue for selected structures."),
"icon": "autoqueue-off.png",
"enabled": true
};
},
"execute": function(entStates)
{
if (entStates.length)
turnAutoQueueOff();
},
"allowedPlayers": ["Player"]
},
};
function playerCheck(entState, targetState, validPlayers)

View File

@ -344,7 +344,8 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
"entities": cmpProductionQueue.GetEntitiesList(),
"technologies": cmpProductionQueue.GetTechnologiesList(),
"techCostMultiplier": cmpProductionQueue.GetTechCostMultiplier(),
"queue": cmpProductionQueue.GetQueue()
"queue": cmpProductionQueue.GetQueue(),
"autoqueue": cmpProductionQueue.IsAutoQueueing()
};
let cmpTrader = Engine.QueryInterface(ent, IID_Trader);

View File

@ -73,6 +73,30 @@ ProductionQueue.prototype.GetEntitiesList = function()
return Array.from(this.entitiesMap.values());
};
/**
* @return {boolean} - Whether we are automatically queuing items.
*/
ProductionQueue.prototype.IsAutoQueueing = function()
{
return !!this.autoqueuing;
};
/**
* Turn on Auto-Queue.
*/
ProductionQueue.prototype.EnableAutoQueue = function()
{
this.autoqueuing = true;
};
/**
* Turn off Auto-Queue.
*/
ProductionQueue.prototype.DisableAutoQueue = function()
{
delete this.autoqueuing;
};
/**
* Calculate the new list of producible entities
* and update any entities currently being produced.
@ -781,6 +805,13 @@ ProductionQueue.prototype.ProgressTimeout = function(data, lateness)
this.SetAnimation("training");
cmpPlayer.UnBlockTraining();
// AutoQueue: We add the second batch after starting the first.
// (As opposed to when the first batch finishes.)
// This is to make the feature not infinitely better than good micro.
if (this.autoqueuing)
this.AddItem(item.unitTemplate, "unit", item.count, item.metadata);
Engine.PostMessage(this.entity, MT_TrainingStarted, { "entity": this.entity });
}
if (item.technologyTemplate)

View File

@ -545,8 +545,81 @@ function test_token_changes()
TS_ASSERT_EQUALS(cmpProductionQueue.GetQueue().length, 1);
}
function test_auto_queue()
{
let playerEnt = 2;
let playerID = 1;
let testEntity = 3;
ConstructComponent(playerEnt, "EntityLimits", {
"Limits": {
"some_limit": 8
},
"LimitChangers": {},
"LimitRemovers": {}
});
AddMock(SYSTEM_ENTITY, IID_GuiInterface, {
"PushNotification": () => {}
});
AddMock(SYSTEM_ENTITY, IID_Trigger, {
"CallEvent": () => {}
});
AddMock(SYSTEM_ENTITY, IID_Timer, {
"SetInterval": (ent, iid, func) => 1
});
AddMock(SYSTEM_ENTITY, IID_TemplateManager, {
"TemplateExists": () => true,
"GetTemplate": name => ({
"Cost": {
"BuildTime": 0,
"Population": 1,
"Resources": {}
},
"TrainingRestrictions": {
"Category": "some_limit"
}
})
});
AddMock(SYSTEM_ENTITY, IID_PlayerManager, {
"GetPlayerByID": id => playerEnt
});
AddMock(playerEnt, IID_Player, {
"GetCiv": () => "iber",
"GetPlayerID": () => playerID,
"GetTimeMultiplier": () => 0,
"BlockTraining": () => {},
"UnBlockTraining": () => {},
"UnReservePopulationSlots": () => {},
"TrySubtractResources": () => true,
"TryReservePopulationSlots": () => false // Always have pop space.
});
AddMock(testEntity, IID_Ownership, {
"GetOwner": () => playerID
});
let cmpProdQueue = ConstructComponent(testEntity, "ProductionQueue", {
"Entities": { "_string": "some_template" },
"BatchTimeModifier": 1
});
cmpProdQueue.EnableAutoQueue();
cmpProdQueue.AddItem("some_template", "unit", 3);
TS_ASSERT_EQUALS(cmpProdQueue.GetQueue().length, 1);
cmpProdQueue.ProgressTimeout(null, 0);
TS_ASSERT_EQUALS(cmpProdQueue.GetQueue().length, 2);
}
testEntitiesList();
regression_test_d1879();
test_batch_adding();
test_batch_removal();
test_auto_queue();
test_token_changes();

View File

@ -877,6 +877,27 @@ var g_Commands = {
cmpResourceDropsite.SetSharing(cmd.shared);
}
},
"autoqueue-on": function(player, cmd, data)
{
for (let ent of data.entities)
{
let cmpProductionQueue = Engine.QueryInterface(ent, IID_ProductionQueue);
if (cmpProductionQueue)
cmpProductionQueue.EnableAutoQueue();
}
},
"autoqueue-off": function(player, cmd, data)
{
for (let ent of data.entities)
{
let cmpProductionQueue = Engine.QueryInterface(ent, IID_ProductionQueue);
if (cmpProductionQueue)
cmpProductionQueue.DisableAutoQueue();
}
},
};
/**