civfanatics / CQUI_Community-Edition

Civilization 6 mod - UI enhancements, reduce clicks and manage your empire faster!
MIT License
149 stars 28 forks source link

Request for improved build queue #38

Open wayneb64 opened 4 years ago

wayneb64 commented 4 years ago

The CQUI version was removed when the Vanilla game added it's own version but it doesn't work right. It won't let you queue a Harbor AND a Lighthouse and every time you capture a city you have to keep coming back and adding damaged buildings to the queue rather than all at once. Really annoying to me as I take a LOT of cities.

Sharky444 commented 3 years ago

I concur. The original CQUI queue was awesome. The game-inbuild queue sucks.

wayneb64 commented 3 years ago

Is the any chance this feature request can get some love? I realize this is probably not an easy upgrade but it's probably about the most annoying thing left to fix in the base UI.

Rawrmonkeys commented 3 years ago

This is such an important QoL feature, that I'm surprised no one has bothered to do it yet. I'm going to do a bit of a brain dump and will come back to this once I have a better understanding of modding in Civ VI (I've never made a video game mod before).

Firstly, I see there is an integrations folder. The PQ folder seems to be empty, so does that mean that it is been removed from the project? Last time I checked the mod description on Steam, it said that it still had it.

Looking at https://github.com/kblease/ProductionQueue and comparing ProductionPanel.lua, there is a 5000 line difference (along with other files). That is pretty crazy, but my HOPE is that that is mostly from writing the queue from scratch, so tweaking the in-game production queue will be much easier (hopefully as simple as overriding existing functions).


Design


So from a functionality perspective, what we need are

Event Logic


on adding a building/district to the production queue:

when removing an item from the queue

when moving an item around in the queue

More stuff to think about


Rawrmonkeys commented 3 years ago

Updating the production panel

After digging through ProductionPanel.lua, it looks like there is a refresh function that is responsible for recreating all the state information. This involves creating the list (table) of what will show in the production panel and whether or not it will show as buildable. There is a function for the build queue 'canProduce' that is used both for controlling if the item is added to the list and whether it is added as buildable. Unfortunately, this function doesn't seem to be exposed. We can check all occurrences of that function and add in an 'or' conditional for whether or not the build requirements get met if you take into account the production queue. We should also take a look at the results table that is returned by 'canProduce' in case it gives stuff like missing building ids that would be easy to find in the production queue.

The complications for this are

  1. We don't know exactly what 'canProduce' does. It is used both with 2 and 3 parameters, so it's behavior is situational.
  2. There is also tons of duplicated code for this logic across districts, buildings, units, and projects.

So as long as adding/removing buildings to/from the queue triggers a refresh, we will properly update the production panel.

Managing the build queue

ProductionHelper.lua has RemoveQueueItem, SwapQueueItem, and MoveQueueItem that are hooked into stuff like mouse events. Those functions currently just take in indexes. We will situationally need to perform multiple operations on a single call, so we could pass in the build queue as the last parameter or handle that logic in a separate function that we define in ProductionPanel.

Remaining questions

Still not sure how we determine requirements from a building. Like how do we get the relationship of Shrine <-> Temple. I'm guessing that is defined in the simpler modding areas. Just not sure how to access from lua.

Rawrmonkeys commented 3 years ago

Extracting the failure reason

If something can't be built, the results object from 'canProduce' is a list of failure reasons. These failure reasons are the strings that show as a tooltip when hovering over a building. As in, there are no ids or relational crap that can be looked up somewhere. We will likely have to deduce failure types through something like a "contains" call (for displaying the tooltip, there is a locale lookup that happens afterward. Idk if that means this string will always be English or not. If it isn't, then cross language support will be a bit difficult...).

Buildings seem to have a prereq building list, but I don't think it has any value since we need to be comparing against what is in the build queue and a building with multiple prereqs will still need to isolate which prereq building is the one mentioned in the fail text.

Accessing items from the queue

ProductionHelper and ProductionManager each have a function for refreshing the queue. This iterates through the queue and builds a table of queue instances. Both call ProductionHelper's CreateQueueInstance function, which shows how to extract the item information (like building name). The active item in production is skipped in this queue instance stuff, but there is already a getter in the BuildQueue object to get that.

We will need to know the contents of the BuildQueue during the 'getData' function, but also on BuildQueue order manipulation events. I'm not sure how realistic it is to manage this separately from the underlying BuildQueue. As a first pass at least, we should just recreate the queue in every relevant function call (getData, RemoveQueueItem, SwapQueueItem, MoveQueueItem). The queue operation functions can add an extra parameter to know if it's part of a batch operation or not.

Rawrmonkeys commented 3 years ago

The fail reason isn't adequate for determining if we should show/enable a building because it will only show the district failure reason if it is missing both the district and building. The building holds references to it's requirements, so I've taken the approach of evaluating those and looking at the queue in addition to the districts/buildings owned by the city. I think the failure reasons are still a valuable way of weeding stuff out though, so we won't override anything unless the building has a failure reason.

To test: unique buildings, religious buildings, mutually exclusive buildings

When adding a district to the build queue, there is already a panel refresh (assuming the same for building).

I've got the 1st building to show up in the list and show as enabled when adding a district to the build queue. Unfortunately, clicking the building seems to do nothing. Having the district as the active production target also causes it to leave the list (by design), but this now results in the newly shown buildings displaying under another district (so we should get rid of the behavior that removes it).

On the bright side, newly added buildings don't show as purchase-able.

To test: when queueing more districts than the city can build, make sure that any buildings with that district as a requirement are also removed from the queue (I think they will if the queue actions are implemented properly).

I'm thinking of making a repo for this. Like the old production queue, this should be a feature that exists standalone and can be integrated into CQUI. Will link the repo when I set it up (never made a repo before, so fingers crossed I use the right settings...).

Rawrmonkeys commented 3 years ago

So the reason the newly enabled buildings don't do anything when clicked is that the operation request doesn't seem to go through. There doesn't seem to be any feedback for that and this isn't exposed in lua, so there is nothing we can do about it.

CityManager.RequestOperation(city, CityOperationTypes.BUILD, tParameters);

I tried calling on BuildQueue:CreateBuilding, but that doesn't seem to work either.

We do have control over the display portion of the build queue though. The only way I see this working is if we track everything ourselves and make the display based off our queue instead of the underlying one. This will be a nightmare, but is theoretically doable. Will have to check if these queue's are used at all. ProductionPanel has RefreshQueue, ProductionHelper has RefreshProductionQueue. Both have similar behavior, so I think both need to produce the modified queue...

BuildQueue is mostly getter functions. BuildQueue:Create functions seem to only be used by the tuner, BuildQueue:GetAt seems to only be used by the 2 refresh functions, so it's looking like we only have to worry about UI when it comes to our managed queue vs the underlying one.

Rawrmonkeys commented 3 years ago

Looks like adding stuff to the display of the build queue does work. Doing stuff on the RefreshQueue functions doesn't seem to be enough to keep the multi-queue synched with it though.