Civilization VII Update 1.2.0 - April 22, 2025

That is possible also. I played 12 deity exploration games since release and took notes on length; all fell in the range of T70 - T94.

In my EXP games pretty consistently:
- Culture path gets completed first
- Military next
- Science next
- Econ last (usually not finished all the way to +20 era score. Exception = Songhai)

So yeah you’re right by slowing down the last path to advance may not change the actual length.. maybe some balancing of these paths will come at a later time. IMO culture path needs to be harder. And maybe adjust down EXP progress points if ideal length of age is longer than that. (ANT is fine as is; 20 ANT games all fell in T115 - T140 range, T130 median)
The economic path is always last for me, too. I usually have to avoid slotting in relics and activating treasure fleets in order to keep the age going long enough to gather enough treasure fleets in my territory. There's definitely a big imbalance in the exploration age legacy paths.
 
I noticed this yesterday, too. The homeland treasure resources aren't showing up in the resource window. I didn't get to check if they're still working, though.
All my homeland treasure resources including cocoa and horses and are showing up correctly in the empire resources bar. I also tested their effects which are working correctly too.

So might be an occasional bug that some of you are seeing?
 
Finished another Exploration Age today, third one post Patch (easily done if you don't bother playing Modern Age, which I regard as complete Garbage, but that's the potential subject for another and much longer Post).

As others have noted, Distant Lands Treasure resources were scattered thin: I ended up with one settlement with 2, two others with one each, and to get any more would have had to go to war with the only AI Civs I wasn't already at war with. It wasn't worth it, so I just wrote off the Economic Legacy Path, which is pretty much what I was doing before the patch most of the time. The resources still need balancing.

- And this, by the way, was with only 2 AI Civs on the Distant Lands continent, so about 40% of the land area was available for settling. That, unfortunately, doesn't do much good when 3/4 of that 'empty space' is well inland. I did some figuring, and to get 2 more Treasure resources would have required another coastal settlement and 2 more inland settlements, and would have placed those two right next to Xerxes, who had 12 settlements and was playing Mongols. That's sticking my neck out farther than I like when I'm already at war with 3 out of 4 Homeland AI Civs!

Especially compared to the other paths, Culture in Exploration is still a snooze: get the 2 Relics for converting Capitals, send out 2 - 3 Missionaries, convert and stash Relics until you build enough Temples to store them. It doesn't even matter if all the capitals get converted back afterwards. The entire Culture Legacy Path was finished by Turn 25 and again, that is the same as pre-Patch, so it also desperately needs balancing.

Another thing that is painfully clear is that the AI is utterly hopeless at naval warfare. I took no less than 4 cities with fleets of Carracks alone (one with a single Cog, in fact) and with 2 Carracks off shore smashed an invading army - trudging idiotically along the coast and sitting in front of a walled city while the naval gunnery destroyed unit after unit. As with too many AI Army Commanders, AI Fleet Commanders are apparently used only for reconnaissance - I have still never seen an enemy fleet, only single Commanders sailing by themselves and individual ships sailing into the guns of my fleets. It makes this whole aspect of in-game warfare embarrassingly easy and particularly warps things in Exploration, when so much of the Age is built around sea travel to Distant Lands and settling there.
 
I found a total of eight in my current game on the only coast and islands I had access to.
I think I figured out a big part of the problem. Remember when they said that the new resources came with their own narrative events? Yup, some of the Treasure Resources are gated by goodie huts, each with a choice to reveal the resource or receive an immediate reward.

Now if you're a Distant Lands civ, which will your scout pick? You guessed it - the resources aren't special to them, so they'll probably take the quick payoff. Unfortunately that removes the resource from the board, like it never existed in the first place. Nothing to find, fight over, or capture. No chance to recover those.

It's just a hypothesis, but it does explain how the Treasure Resources would be correctly seeded but become unavailable through the course of the game. I'd bet that no one thought to check the number of Treasure Resources existing at the end of the era to see how well they matched up with the seeded number.

Honestly, didn't anyone at Firaxis think this through at all? Or are they just so in love with their own ideas that they don't bother to look for potential problems?
 
I think I figured out a big part of the problem. Remember when they said that the new resources came with their own narrative events? Yup, some of the Treasure Resources are gated by goodie huts, each with a choice to reveal the resource or receive an immediate reward.

Now if you're a Distant Lands civ, which will your scout pick? You guessed it - the resources aren't special to them, so they'll probably take the quick payoff. Unfortunately that removes the resource from the board, like it never existed in the first place. Nothing to find, fight over, or capture. No chance to recover those.

It's just a hypothesis, but it does explain how the Treasure Resources would be correctly seeded but become unavailable through the course of the game. I'd bet that no one thought to check the number of Treasure Resources existing at the end of the era to see how well they matched up with the seeded number.

Honestly, didn't anyone at Firaxis think this through at all? Or are they just so in love with their own ideas that they don't bother to look for potential problems?

Distant lands Civs don't pop distant lands goodie huts, though? I am not sure how it works exactly, but I thought they don't trigger them, which is why they are still around when you arrive.
 
Distant lands Civs don't pop distant lands goodie huts, though? I am not sure how it works exactly, but I thought they don't trigger them, which is why they are still around when you arrive.
Yes, they don't trigger them. I think it stays the same currently, but will be altered by the time human players will be able to span in distant lands.
 
Distant lands Civs don't pop distant lands goodie huts, though? I am not sure how it works exactly, but I thought they don't trigger them, which is why they are still around when you arrive.
I'm not sure. I got tired of scrounging for scraps every game, so I always drop the number of Civs so that Distant Lands are blissfully quiet and I can impress the natives with my Zippo lighter.
 
I think I figured out a big part of the problem. Remember when they said that the new resources came with their own narrative events? Yup, some of the Treasure Resources are gated by goodie huts, each with a choice to reveal the resource or receive an immediate reward.

Now if you're a Distant Lands civ, which will your scout pick? You guessed it - the resources aren't special to them, so they'll probably take the quick payoff. Unfortunately that removes the resource from the board, like it never existed in the first place. Nothing to find, fight over, or capture. No chance to recover those.

It's just a hypothesis, but it does explain how the Treasure Resources would be correctly seeded but become unavailable through the course of the game. I'd bet that no one thought to check the number of Treasure Resources existing at the end of the era to see how well they matched up with the seeded number.

Honestly, didn't anyone at Firaxis think this through at all? Or are they just so in love with their own ideas that they don't bother to look for potential problems?
I strongly doubt this is the issue. As people have pointed out, Distant Lands civs can't pick goodie huts on their own continent, and any significant quantity of resources being locked behind goodie huts is just generally implausible. Did you think this through at all?

I think the most likely cause for treasure resource quantities being reduced is how resource distributions are generated now. I don't fully understand how it works yet, still trying to parse the code behind it, but Cocoa, Spices, Sugar and Tea being split between hemispheres will be a big factor in a reduced amount of treasure resources on the other continent. Since they also no longer spawn placeholder versions during Antiquity, there'll be fewer possibilities for Treasure resources to spawn in Exploration - Tea, Sugar, Spices, Furs and Cocoa won't have 'guaranteed' spots anymore.

I believe there may also be some effects that are a consequence of how the game handles resource changes between eras, but I don't understand it well enough to explain how I think that might result in fewer treasure resources.
 
I don't know about that. My first 1.2 game had very few distant treasure resources available and I ended up with only 8 points in the end, but the exploration age still finished before turn 80 and I still had far more settlements than I needed to get the other three legacy paths finished.

Yeah there really needs to be a fix on treasure resources. Make the legacy path harder, sure, but don't make it impossible because of map gen randomness.

EDIT- to add, because I want to get the word out. I have four cocoa on homelands, two in territory, two from trade. They do not show up on my resources screen and do not appear to function at all.
 
Last edited:
Tried to keep count in my last game.

Playing on Continents Plus with only 2 AI Civs in Distant Lands. Because they had 15+ settlements between them and weren't particularly friendly, wasn't able to make a detailed scout of all the Distant Lands, but here's what I did see:

In the 50% of the continent I did scout, there were a total of 13 Treasure Resources. If the same distribution held on the rest of the continent, that would make 25 - 26 Treasure Resources even theoretically available - but almost half were already in AI Civ territory.

On the islands between Home and Distant Lands continents, there were 5 Treasure Resources - each on a different island, by the way - none of the concentrations I used to see occasionally pre-Patch.

That means, without going conquering, each of the Homeland's 5 Civs could get an average of 3 - 4 Treasure Resources each. I managed to get 3 with 2 settlements and conquered a settlement with 1 more. Given that it took 21 turns for a fleet from that settlement to reach my nearest Homeland Wharf, it was almost worthless for completing the Legacy Path.

And at the end of the Exploration Age, with 27 Treasure Fleet points, I had twice what any other Homeland Civ had, but counting time to get to Shipbuilding and start the fleets, and travel time between continents, 3 resources are simply not enough to complete the path before end of Age (especially when you are 'researching' Future Techs and Civics well before Turn 100 so the Age will be shortened accordingly).

I still think my preferred 'solution' to Treasure Resource distribution would be to have an in-game mechanism for generating different resources as Treasures.

Historically, between 400 and 1650 CE the Distant Lands Treasures (including from China, Southeast Asia, Africa, and the Americas) to 'Homeland' (Europe/Middle East) were, in game terms:
Spices, Tea, Sugar, Silver, Gold, Ivory, Silk, Kaolin Clay (Porcelain), Hardwood (Ship Timber), Furs, Coffee

Other more 'specialized' Treasures were Horses, Dyes, Aromatic Woods (Incense-substitutes), and 'special' foodstuffs. especially Maize, Potato, and Peppers.

So, if the game would make Scarcity = Treasure, any Resource you have to Import via Trade Routes in the Homeland becomes automatically a Treasure resource for your Civ in Distant Lands, including Horses, Elephants (Ivory), Kaolin, etc. That alone would probably 'open up' the Treasure mechanic enough to put it more on a level with the other Exploration Legacy paths.

Of course, if you conquer the 'wrong' Settlement that gives you Homeland access to a resource, it stops being a Treasure!
 
EDIT- to add, because I want to get the word out. I have four cocoa on homelands, two in territory, two from trade. They do not show up on my resources screen and do not appear to function at all.
That is odd because I did check recently and cocoa is showing up and working correctly in my game. Anything particular about your game settings / cocoa flavor that might be causing the bug you think? May need to crowdsource this one for possible cause and fix lol or wait for patch 6.8.

(if it were a universal bug, higher chance to get fixed quickly vs some unknown trigger)
 
Last edited:
That is odd because I did check recently and cocoa is showing up and working correctly in my game. Anything particular about your game settings / cocoa flavor that might be causing the bug you think? May need to crowdsource this one for possible cause and fix lol or wait for patch 6.8.

(if it were a universal bug, higher chance to get fixed quickly vs some unknown trigger)

I have a ton of mods but none of them modify gameplay other than arguably community bugfix. I got two of the cocoa conquering and two trading. The only thing I can think of that's not "normal" is I ended antiquity by gaining two science legacy points, one economic point, and two military points all in one turn, partially from conquering the two cocoa cities.

EDIT- to ask, they are empire resources correct?
 
I have a ton of mods but none of them modify gameplay other than arguably community bugfix. I got two of the cocoa conquering and two trading. The only thing I can think of that's not "normal" is I ended antiquity by gaining two science legacy points, one economic point, and two military points all in one turn, partially from conquering the two cocoa cities.
Hmmm yeah, leaving conquering aside, the cocoa you got from trading in EXP should show up as empire resource so that is strange indeed
 
I strongly doubt this is the issue. As people have pointed out, Distant Lands civs can't pick goodie huts on their own continent, and any significant quantity of resources being locked behind goodie huts is just generally implausible. Did you think this through at all?

I think the most likely cause for treasure resource quantities being reduced is how resource distributions are generated now. I don't fully understand how it works yet, still trying to parse the code behind it, but Cocoa, Spices, Sugar and Tea being split between hemispheres will be a big factor in a reduced amount of treasure resources on the other continent. Since they also no longer spawn placeholder versions during Antiquity, there'll be fewer possibilities for Treasure resources to spawn in Exploration - Tea, Sugar, Spices, Furs and Cocoa won't have 'guaranteed' spots anymore.
Yup, didn't realize Distant Land AIs didn't touch goodie huts, and if there's only one narrative event per resource, the impact is likely to be minimal.

To your point though, this is what happens when you make simple changes to complex algorithms. Remember my original point that resources seem less "clumped"? Well if you simply double the spawn area and the clumping algorithm relied on total area to calculate the density of the pockets, that would lower the clumping. And the fact that you can't easily parse the code means that someone wrote a sophisticated model that requires nuanced tuning. It could even potentially have problems with larger map sizes, much like trading range limits.
 
Apologies for making fairly strong statements on these items - my day-to-day job is a Platform Product Manager for enterprise SAAS software, so untangling these types of issues is a big part of my work. To be fair to Firaxis though, they have a much tougher job - I don't have to optimize my systems to maximize "fun." :lol:
 
Apologies for making fairly strong statements on these items - my day-to-day job is a Platform Product Manager for enterprise SAAS software, so untangling these types of issues is a big part of my work. To be fair to Firaxis though, they have a much tougher job - I don't have to optimize my systems to maximize "fun." :lol:

You haven't said anything out of line in any way that I see.

I have similarly strong words for many things in this game, and I'm a huge fan.
 
I think the most likely cause for treasure resource quantities being reduced is how resource distributions are generated now. I don't fully understand how it works yet, still trying to parse the code behind it...
You're a braver man than I to take that code on. Is it a recursive function by any chance? Those things are brutally difficult to parse and debug and are prone to weirdness five levels down.
 
You're a braver man than I to take that code on. Is it a recursive function by any chance? Those things are brutally difficult to parse and debug and are prone to weirdness five levels down.
I'm probably mostly struggling because I have very limited experience w coding and no idea what language this is haha, but the code for is:

(This is located in C:\Program Files (x86)\Steam\steamapps\common\Sid Meier's Civilization VII\Base\modules\base-standard\scripts\age-transition-post-load.ts
Spoiler :

Code:
function removeObsoleteResources(iRemovedResourcePlots: number[], aGeneratedResources: ResourceType[]): number {
    console.log("Removing old resources");

    let aTypesRemoved: number[] = [];
    let aCutResources: ResourceType[] = [];
    let resourcesAvailable = ResourceBuilder.getResourceCounts(-1);
    let countOnMap = 0;
    let countRemoved = 0;
    for (let i = 0; i < resourcesAvailable.length; ++i) {
        if (resourcesAvailable[i] > 0) {
            countOnMap++;
        }
    }

    let countToAdd = aGeneratedResources.length;
    console.log("Adding new resources: " + countToAdd);
    console.log("Resources already on map: " + countOnMap);
    let totalResourceToCut = (countOnMap + countToAdd) - countOnMap;
    if (totalResourceToCut < 0) {
        totalResourceToCut = 0;
    }
    console.log("Number of resources to cut: " +  totalResourceToCut);
    let resourceToCut = ResourceBuilder.getBestMapResourceCuts(aGeneratedResources, totalResourceToCut);
    for (let iI: number = 0; iI < resourceToCut.length; ++iI) {
        var resourceInfo = GameInfo.Resources.lookup(resourceToCut[iI]);
        if (resourceInfo) {
            aCutResources.push(resourceInfo.$index);
        }       
    }

    // Traverse the map
    let iWidth = GameplayMap.getGridWidth();
    let iHeight = GameplayMap.getGridHeight();
    for (let iY = 0; iY < iHeight; iY++) {
        for (let iX = 0; iX < iWidth; iX++) {
            let iIndex = (iY * iWidth) + iX;
            // Resource here?
            let resource = GameplayMap.getResourceType(iX, iY);
            if (resource != ResourceTypes.NO_RESOURCE) {
                // Remove it if wrong Age
                let removeResource = false;
                if (aCutResources.find(x => x == resource)) {
                    removeResource = true;
                }
                if (!removeResource && !ResourceBuilder.isResourceValidForAge(resource, g_incomingAge)) {
                    removeResource = true;
                }
                if (removeResource) {
                    var resourceInfo = GameInfo.Resources.lookup(resource);
                    if (resourceInfo) {
                        countRemoved++;
                        removeRuralDistrict(iX, iY);
                        ResourceBuilder.setResourceType(iX, iY, ResourceTypes.NO_RESOURCE);
                        console.log("Removed resource: " + Locale.compose(resourceInfo.Name) + " at (" + iX + ", " + iY + ")");
                        iRemovedResourcePlots.push(iIndex);
                        placeRuralDistrict(iX, iY);

                        let resourceType = resourceInfo.$index;
                        if (!aTypesRemoved.find(x => x == resourceType)) {
                            aTypesRemoved.push(resourceType);
                        }
                    }
                }
            }
        }
    }
    console.log("Removed total resource locations: " + countRemoved);
    return aTypesRemoved.length;
}

function addNewResources(iRemovedResourcePlots: number[], iNumberToPlacePerResource: number, aGeneratedResources: ResourceType[]) {
    console.log("Adding new resources");

    // Count all resources on map
    let iResourceCounts: number[] = ResourceBuilder.getResourceCounts(-1);

    // Look for resources included in this Age that aren't present
    let aResourceTypes: number[] = [];
    
    for (let ridx: number = 0; ridx < aGeneratedResources.length; ++ridx)
    {
        var resourceInfo = GameInfo.Resources.lookup(aGeneratedResources[ridx]);
        if (resourceInfo && resourceInfo.Tradeable) {
            if (iResourceCounts[resourceInfo.$index] == 0) {
                aResourceTypes.push(resourceInfo.$index);
            }
        }
    }

    // Place using scatter algorithm
    console.log("Number to place per resource:" + iNumberToPlacePerResource);
    let iTotalToPlace = iNumberToPlacePerResource * aResourceTypes.length;
    let iTotalPerHemisphere = iTotalToPlace / 2;
    console.log("Total to place:" + iTotalToPlace);

    // Get a list of plots from a new Poisson map with a different seed from the first time we placed resources
    let aPlacementPlots: number[] = [];
    let seed = GameplayMap.getRandomSeed() * (1 + g_incomingAge);
    let avgDistanceBetweenPoints = 3;
    let normalizedRangeSmoothing = 2;
    let poisson = TerrainBuilder.generatePoissonMap(seed, avgDistanceBetweenPoints, normalizedRangeSmoothing);

    let iWidth = GameplayMap.getGridWidth();
    let iHeight = GameplayMap.getGridHeight();
    for (let iY = 0; iY < iHeight; iY++) {
        for (let iX = 0; iX < iWidth; iX++) {
            let index = iY * iWidth + iX;
            if (poisson[index] >= 1) {
                // Don't put resources under existing districts
                let districtID = MapCities.getDistrict(iX, iY);
                if (districtID == null) {
                    aPlacementPlots.push(index);
                }
            }
        }
    }

    // Add additional plots for each place a resource was removed
    iRemovedResourcePlots.forEach(index => {
        if (index) {
            if (!aPlacementPlots.find(x => x == index)) {
                aPlacementPlots.push(index);
            }
        }
    });

    // Randomize all these plots
    shuffle(aPlacementPlots);

    // Place the new resources using your weighting algorithm until we either run out of plots or hit the projected total
    let resourceHemisphere: number[] = new Array(GameInfo.Resources.length);

    //TODO: support multiple hemispheres for more than 1 continent of distance lands on larger maps
    let resourceRegionalCount: number[] = new Array(2);
    resourceRegionalCount[0] = 0;
    resourceRegionalCount[1] = 0;

    let resourceRegionalTotal: number = 0;
    let resourceWeight: number[] = new Array(GameInfo.Resources.length);
    let resourceRunningWeight: number[] = new Array(GameInfo.Resources.length);
    let resourcesPlacedCount: number[] = new Array(GameInfo.Resources.length);

    let importantResourceRegionalCountHome: number[] = new Array(GameInfo.Resources.length);
    let importantResourceRegionalCountDistant: number[] = new Array(GameInfo.Resources.length);

    //Initial Resource data
    for (var resourceIdx = 0; resourceIdx < GameInfo.Resources.length; resourceIdx++) {
        resourceHemisphere[resourceIdx] = 0;
        resourceWeight[resourceIdx] = 0;
        resourceRunningWeight[resourceIdx] = 0;
        resourcesPlacedCount[resourceIdx] = 0;
        importantResourceRegionalCountHome[resourceIdx] = 0;
        importantResourceRegionalCountDistant[resourceIdx] = 0;
    }

    let maxPerHemisphere = 0;
    let resourceDistribution = GameInfo.Resource_Distribution.lookup(g_incomingAge);
    if (resourceDistribution) {
        maxPerHemisphere = resourceDistribution.ResourceTypeMaxPerHemisphere;
    }

    // Set resource weights/hemispheres
    aResourceTypes.forEach(resourceType => {
        if (resourceType) {
            var resourceInfo = GameInfo.Resources[resourceType];
            if (resourceInfo) {
                resourceWeight[resourceInfo.$index] = resourceInfo.Weight;
                if (resourceInfo.Hemispheres == 1) {
                    let iRoll = TerrainBuilder.getRandomNumber(2, "Hemisphere Scatter");
                    if (iRoll >= 1 && resourceRegionalCount[1] <= resourceRegionalTotal / 2) {
                        resourceHemisphere[resourceInfo.$index] == 1;
                        resourceRegionalCount[1]++;
                    }
                    else {
                        resourceRegionalCount[0]++;
                    }

                    resourceRegionalTotal++;
                }
                else {
                    resourceHemisphere[resourceInfo.$index] = 2;
                }
            }
        }
    });

    let iNumPlaced: number = 0;
    aPlacementPlots.forEach(index => {
        if (index && iNumPlaced <= iTotalToPlace) {

            let kLocation: PlotCoord = GameplayMap.getLocationFromIndex(index);
            let hemisphere = GameplayMap.getHemisphere(kLocation.x);

            if (resourceRegionalCount[hemisphere] < iTotalPerHemisphere) {
                //Generate a list of valid resources at this plot
                let resources: number[] = [];
                aResourceTypes.forEach(resourceIdx => {
                    
                    let assignedHempisphere = ResourceBuilder.getResourceHemisphere(resourceIdx);
                    if (assignedHempisphere > -1) {
                        if (assignedHempisphere == 2 || assignedHempisphere == hemisphere) {
                            if (ResourceBuilder.canHaveResource(kLocation.x, kLocation.y, resourceIdx)) {
                                //Try not to place adjacent to other resources. The new Poission map causes the old one to be ignored, which causes clumping.
                                let canPlaceResource = true;
                                for (let iDirection: DirectionTypes = 0; iDirection < DirectionTypes.NUM_DIRECTION_TYPES; iDirection++) {
                                    let iIndex: number = GameplayMap.getIndexFromXY(kLocation.x, kLocation.y);
                                    let iLocation: PlotCoord = GameplayMap.getLocationFromIndex(iIndex);
                                    let iAdjacentX: number = GameplayMap.getAdjacentPlotLocation(iLocation, iDirection).x;
                                    let iAdjacentY: number = GameplayMap.getAdjacentPlotLocation(iLocation, iDirection).y;
                                    if (GameplayMap.getResourceType(iAdjacentX, iAdjacentY) != ResourceTypes.NO_RESOURCE) {
                                        canPlaceResource = TerrainBuilder.getRandomNumber(4, "Resource Scatter") == 0;
                                        break;
                                    }
                                }

                                if (canPlaceResource) {
                                    resources.push(resourceIdx);
                                }
                                
                            }
                        }
                    }
                });

                //Select the highest weighted (ties are a coin flip) resource
                if (resources.length > 0) {
                    let resourceChosen: ResourceType = ResourceTypes.NO_RESOURCE;
                    let resourceChosenIndex: number = 0
                    for (let iI = 0; iI < resources.length; iI++) {
                        if (resourceChosen == ResourceTypes.NO_RESOURCE) {
                            resourceChosen = resources[iI];
                            resourceChosenIndex = resources[iI];
                        }
                        else {
                            if (resourceRunningWeight[resources[iI]] > resourceRunningWeight[resourceChosenIndex]) {
                                resourceChosen = resources[iI];
                                resourceChosenIndex = resources[iI];
                            }
                            else if (resourceRunningWeight[resources[iI]] == resourceRunningWeight[resourceChosenIndex]) {
                                let iRoll = TerrainBuilder.getRandomNumber(2, "Resource Scatter");
                                if (iRoll >= 1) {
                                    resourceChosen = resources[iI];
                                    resourceChosenIndex = resources[iI];
                                }
                            }
                        }
                    }

                    if (hemisphere == 0 && importantResourceRegionalCountHome[resourceChosenIndex] < maxPerHemisphere || hemisphere == 1 && importantResourceRegionalCountDistant[resourceChosenIndex] < maxPerHemisphere) {
                        //Place the selected resource
                        if (resourceChosen != ResourceTypes.NO_RESOURCE) {
                            ResourceBuilder.setResourceType(kLocation.x, kLocation.y, resourceChosen);
                            resourceRunningWeight[resourceChosenIndex] -= resourceWeight[resourceChosenIndex];

                            let name: any = GameInfo.Resources[resourceChosenIndex].Name;
                            console.log("Placed " + Locale.compose(name) + " at (" + kLocation.x + ", " + kLocation.y + ")");
                            iNumPlaced++;
                            resourceRegionalCount[hemisphere]++;
                            if (hemisphere == 0) {
                                importantResourceRegionalCountHome[resourceChosenIndex]++;
                            } else {
                                importantResourceRegionalCountDistant[resourceChosenIndex]++;
                            }
                            resourcesPlacedCount[resourceChosenIndex]++;
                            removeRuralDistrict(kLocation.x, kLocation.y);
                            placeRuralDistrict(kLocation.x, kLocation.y);
                        }
                        else {
                            console.log("Resource Type Failure");
                        }
                    }
                }
            }
        }
    });

    //If we weren't able to place some resources, go through and force place the remaining ones, removing exisitng unimportant resources so the map isn't flooded
    let checkHomeHemisphereLP = true;
    let checkDistantHemisphereLP = true;
    for (let iY = 0; iY < iHeight; iY++) {
        for (let iX = 0; iX < iWidth; iX++) {
            // Don't put resources under existing districts
            let districtID = MapCities.getDistrict(iX, iY);
            let hemisphere = GameplayMap.getHemisphere(iX);
            if (districtID == null) {
                for (let i = 0; i < resourcesPlacedCount.length; ++i) {
                    let resourceToPlace = GameInfo.Resources.lookup(i);
                    if (resourceToPlace) {
                        let assignedHempisphere = ResourceBuilder.getResourceHemisphere(i);
                        if (assignedHempisphere > -1) {
                            if (assignedHempisphere != 2 && assignedHempisphere != hemisphere) {
                                continue;
                            }
    
                            if (hemisphere == 0 && importantResourceRegionalCountHome[i] < resourceToPlace.MinimumPerHemisphere || hemisphere == 1 && importantResourceRegionalCountDistant[i] < resourceToPlace.MinimumPerHemisphere) {
                                //Once LP class checks are complete, no need to do them anymore. These checks are for the age specific resource class, once met they are no longer important and don't need to be forced.
                                if (checkHomeHemisphereLP == true && hemisphere == 0) {
                                    checkHomeHemisphereLP = ResourceBuilder.isResourceClassRequiredForLegacyPath(i, hemisphere);
                                    if (!checkHomeHemisphereLP) {
                                        continue;
                                    }
                                }
                                else if (checkDistantHemisphereLP == true && hemisphere == 1) {
                                    checkDistantHemisphereLP = ResourceBuilder.isResourceClassRequiredForLegacyPath(i, hemisphere);
                                    if (!checkDistantHemisphereLP) {
                                        continue;
                                    }
                                }
        
                                if ((resourcesPlacedCount[i] > 0) && ResourceBuilder.isResourceRequiredForAge(i, hemisphere)) {
                                    if (ResourceBuilder.canHaveResource(iX, iY, i)) {

                                        //Try not to place adjacent to other resources. The new Poission map causes the old one to be ignored, which causes clumping.
                                        let hasAdjResource = false;
                                        for (let iDirection: DirectionTypes = 0; iDirection < DirectionTypes.NUM_DIRECTION_TYPES; iDirection++) {
                                            let iIndex: number = GameplayMap.getIndexFromXY(iX, iY);
                                            let iLocation: PlotCoord = GameplayMap.getLocationFromIndex(iIndex);
                                            let iAdjacentX: number = GameplayMap.getAdjacentPlotLocation(iLocation, iDirection).x;
                                            let iAdjacentY: number = GameplayMap.getAdjacentPlotLocation(iLocation, iDirection).y;
                                            if (GameplayMap.getResourceType(iAdjacentX, iAdjacentY) != ResourceTypes.NO_RESOURCE) {
                                                hasAdjResource = true;
                                                break;
                                            }
                                        }

                                        if (!hasAdjResource) {
                                            ResourceBuilder.setResourceType(iX, iY, i);
                                            let name: any = GameInfo.Resources.lookup(i)?.Name;
                                            console.log("Force Placed " + Locale.compose(name) + " at (" + iX + ", " + iY + ")");
                                            hemisphere == 0 ? importantResourceRegionalCountHome[i]++ : importantResourceRegionalCountDistant[i]++;
                                            removeRuralDistrict(iX, iY);
                                            placeRuralDistrict(iX, iY);
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
 
I'm probably mostly struggling because I have very limited experience w coding and no idea what language this is haha, but the code for is:

(This is located in C:\Program Files (x86)\Steam\steamapps\common\Sid Meier's Civilization VII\Base\modules\base-standard\scripts\age-transition-post-load.ts
Spoiler :

Code:
function removeObsoleteResources(iRemovedResourcePlots: number[], aGeneratedResources: ResourceType[]): number {
    console.log("Removing old resources");

    let aTypesRemoved: number[] = [];
    let aCutResources: ResourceType[] = [];
    let resourcesAvailable = ResourceBuilder.getResourceCounts(-1);
    let countOnMap = 0;
    let countRemoved = 0;
    for (let i = 0; i < resourcesAvailable.length; ++i) {
        if (resourcesAvailable[i] > 0) {
            countOnMap++;
        }
    }

    let countToAdd = aGeneratedResources.length;
    console.log("Adding new resources: " + countToAdd);
    console.log("Resources already on map: " + countOnMap);
    let totalResourceToCut = (countOnMap + countToAdd) - countOnMap;
    if (totalResourceToCut < 0) {
        totalResourceToCut = 0;
    }
    console.log("Number of resources to cut: " +  totalResourceToCut);
    let resourceToCut = ResourceBuilder.getBestMapResourceCuts(aGeneratedResources, totalResourceToCut);
    for (let iI: number = 0; iI < resourceToCut.length; ++iI) {
        var resourceInfo = GameInfo.Resources.lookup(resourceToCut[iI]);
        if (resourceInfo) {
            aCutResources.push(resourceInfo.$index);
        }      
    }

    // Traverse the map
    let iWidth = GameplayMap.getGridWidth();
    let iHeight = GameplayMap.getGridHeight();
    for (let iY = 0; iY < iHeight; iY++) {
        for (let iX = 0; iX < iWidth; iX++) {
            let iIndex = (iY * iWidth) + iX;
            // Resource here?
            let resource = GameplayMap.getResourceType(iX, iY);
            if (resource != ResourceTypes.NO_RESOURCE) {
                // Remove it if wrong Age
                let removeResource = false;
                if (aCutResources.find(x => x == resource)) {
                    removeResource = true;
                }
                if (!removeResource && !ResourceBuilder.isResourceValidForAge(resource, g_incomingAge)) {
                    removeResource = true;
                }
                if (removeResource) {
                    var resourceInfo = GameInfo.Resources.lookup(resource);
                    if (resourceInfo) {
                        countRemoved++;
                        removeRuralDistrict(iX, iY);
                        ResourceBuilder.setResourceType(iX, iY, ResourceTypes.NO_RESOURCE);
                        console.log("Removed resource: " + Locale.compose(resourceInfo.Name) + " at (" + iX + ", " + iY + ")");
                        iRemovedResourcePlots.push(iIndex);
                        placeRuralDistrict(iX, iY);

                        let resourceType = resourceInfo.$index;
                        if (!aTypesRemoved.find(x => x == resourceType)) {
                            aTypesRemoved.push(resourceType);
                        }
                    }
                }
            }
        }
    }
    console.log("Removed total resource locations: " + countRemoved);
    return aTypesRemoved.length;
}

function addNewResources(iRemovedResourcePlots: number[], iNumberToPlacePerResource: number, aGeneratedResources: ResourceType[]) {
    console.log("Adding new resources");

    // Count all resources on map
    let iResourceCounts: number[] = ResourceBuilder.getResourceCounts(-1);

    // Look for resources included in this Age that aren't present
    let aResourceTypes: number[] = [];
   
    for (let ridx: number = 0; ridx < aGeneratedResources.length; ++ridx)
    {
        var resourceInfo = GameInfo.Resources.lookup(aGeneratedResources[ridx]);
        if (resourceInfo && resourceInfo.Tradeable) {
            if (iResourceCounts[resourceInfo.$index] == 0) {
                aResourceTypes.push(resourceInfo.$index);
            }
        }
    }

    // Place using scatter algorithm
    console.log("Number to place per resource:" + iNumberToPlacePerResource);
    let iTotalToPlace = iNumberToPlacePerResource * aResourceTypes.length;
    let iTotalPerHemisphere = iTotalToPlace / 2;
    console.log("Total to place:" + iTotalToPlace);

    // Get a list of plots from a new Poisson map with a different seed from the first time we placed resources
    let aPlacementPlots: number[] = [];
    let seed = GameplayMap.getRandomSeed() * (1 + g_incomingAge);
    let avgDistanceBetweenPoints = 3;
    let normalizedRangeSmoothing = 2;
    let poisson = TerrainBuilder.generatePoissonMap(seed, avgDistanceBetweenPoints, normalizedRangeSmoothing);

    let iWidth = GameplayMap.getGridWidth();
    let iHeight = GameplayMap.getGridHeight();
    for (let iY = 0; iY < iHeight; iY++) {
        for (let iX = 0; iX < iWidth; iX++) {
            let index = iY * iWidth + iX;
            if (poisson[index] >= 1) {
                // Don't put resources under existing districts
                let districtID = MapCities.getDistrict(iX, iY);
                if (districtID == null) {
                    aPlacementPlots.push(index);
                }
            }
        }
    }

    // Add additional plots for each place a resource was removed
    iRemovedResourcePlots.forEach(index => {
        if (index) {
            if (!aPlacementPlots.find(x => x == index)) {
                aPlacementPlots.push(index);
            }
        }
    });

    // Randomize all these plots
    shuffle(aPlacementPlots);

    // Place the new resources using your weighting algorithm until we either run out of plots or hit the projected total
    let resourceHemisphere: number[] = new Array(GameInfo.Resources.length);

    //TODO: support multiple hemispheres for more than 1 continent of distance lands on larger maps
    let resourceRegionalCount: number[] = new Array(2);
    resourceRegionalCount[0] = 0;
    resourceRegionalCount[1] = 0;

    let resourceRegionalTotal: number = 0;
    let resourceWeight: number[] = new Array(GameInfo.Resources.length);
    let resourceRunningWeight: number[] = new Array(GameInfo.Resources.length);
    let resourcesPlacedCount: number[] = new Array(GameInfo.Resources.length);

    let importantResourceRegionalCountHome: number[] = new Array(GameInfo.Resources.length);
    let importantResourceRegionalCountDistant: number[] = new Array(GameInfo.Resources.length);

    //Initial Resource data
    for (var resourceIdx = 0; resourceIdx < GameInfo.Resources.length; resourceIdx++) {
        resourceHemisphere[resourceIdx] = 0;
        resourceWeight[resourceIdx] = 0;
        resourceRunningWeight[resourceIdx] = 0;
        resourcesPlacedCount[resourceIdx] = 0;
        importantResourceRegionalCountHome[resourceIdx] = 0;
        importantResourceRegionalCountDistant[resourceIdx] = 0;
    }

    let maxPerHemisphere = 0;
    let resourceDistribution = GameInfo.Resource_Distribution.lookup(g_incomingAge);
    if (resourceDistribution) {
        maxPerHemisphere = resourceDistribution.ResourceTypeMaxPerHemisphere;
    }

    // Set resource weights/hemispheres
    aResourceTypes.forEach(resourceType => {
        if (resourceType) {
            var resourceInfo = GameInfo.Resources[resourceType];
            if (resourceInfo) {
                resourceWeight[resourceInfo.$index] = resourceInfo.Weight;
                if (resourceInfo.Hemispheres == 1) {
                    let iRoll = TerrainBuilder.getRandomNumber(2, "Hemisphere Scatter");
                    if (iRoll >= 1 && resourceRegionalCount[1] <= resourceRegionalTotal / 2) {
                        resourceHemisphere[resourceInfo.$index] == 1;
                        resourceRegionalCount[1]++;
                    }
                    else {
                        resourceRegionalCount[0]++;
                    }

                    resourceRegionalTotal++;
                }
                else {
                    resourceHemisphere[resourceInfo.$index] = 2;
                }
            }
        }
    });

    let iNumPlaced: number = 0;
    aPlacementPlots.forEach(index => {
        if (index && iNumPlaced <= iTotalToPlace) {

            let kLocation: PlotCoord = GameplayMap.getLocationFromIndex(index);
            let hemisphere = GameplayMap.getHemisphere(kLocation.x);

            if (resourceRegionalCount[hemisphere] < iTotalPerHemisphere) {
                //Generate a list of valid resources at this plot
                let resources: number[] = [];
                aResourceTypes.forEach(resourceIdx => {
                   
                    let assignedHempisphere = ResourceBuilder.getResourceHemisphere(resourceIdx);
                    if (assignedHempisphere > -1) {
                        if (assignedHempisphere == 2 || assignedHempisphere == hemisphere) {
                            if (ResourceBuilder.canHaveResource(kLocation.x, kLocation.y, resourceIdx)) {
                                //Try not to place adjacent to other resources. The new Poission map causes the old one to be ignored, which causes clumping.
                                let canPlaceResource = true;
                                for (let iDirection: DirectionTypes = 0; iDirection < DirectionTypes.NUM_DIRECTION_TYPES; iDirection++) {
                                    let iIndex: number = GameplayMap.getIndexFromXY(kLocation.x, kLocation.y);
                                    let iLocation: PlotCoord = GameplayMap.getLocationFromIndex(iIndex);
                                    let iAdjacentX: number = GameplayMap.getAdjacentPlotLocation(iLocation, iDirection).x;
                                    let iAdjacentY: number = GameplayMap.getAdjacentPlotLocation(iLocation, iDirection).y;
                                    if (GameplayMap.getResourceType(iAdjacentX, iAdjacentY) != ResourceTypes.NO_RESOURCE) {
                                        canPlaceResource = TerrainBuilder.getRandomNumber(4, "Resource Scatter") == 0;
                                        break;
                                    }
                                }

                                if (canPlaceResource) {
                                    resources.push(resourceIdx);
                                }
                               
                            }
                        }
                    }
                });

                //Select the highest weighted (ties are a coin flip) resource
                if (resources.length > 0) {
                    let resourceChosen: ResourceType = ResourceTypes.NO_RESOURCE;
                    let resourceChosenIndex: number = 0
                    for (let iI = 0; iI < resources.length; iI++) {
                        if (resourceChosen == ResourceTypes.NO_RESOURCE) {
                            resourceChosen = resources[iI];
                            resourceChosenIndex = resources[iI];
                        }
                        else {
                            if (resourceRunningWeight[resources[iI]] > resourceRunningWeight[resourceChosenIndex]) {
                                resourceChosen = resources[iI];
                                resourceChosenIndex = resources[iI];
                            }
                            else if (resourceRunningWeight[resources[iI]] == resourceRunningWeight[resourceChosenIndex]) {
                                let iRoll = TerrainBuilder.getRandomNumber(2, "Resource Scatter");
                                if (iRoll >= 1) {
                                    resourceChosen = resources[iI];
                                    resourceChosenIndex = resources[iI];
                                }
                            }
                        }
                    }

                    if (hemisphere == 0 && importantResourceRegionalCountHome[resourceChosenIndex] < maxPerHemisphere || hemisphere == 1 && importantResourceRegionalCountDistant[resourceChosenIndex] < maxPerHemisphere) {
                        //Place the selected resource
                        if (resourceChosen != ResourceTypes.NO_RESOURCE) {
                            ResourceBuilder.setResourceType(kLocation.x, kLocation.y, resourceChosen);
                            resourceRunningWeight[resourceChosenIndex] -= resourceWeight[resourceChosenIndex];

                            let name: any = GameInfo.Resources[resourceChosenIndex].Name;
                            console.log("Placed " + Locale.compose(name) + " at (" + kLocation.x + ", " + kLocation.y + ")");
                            iNumPlaced++;
                            resourceRegionalCount[hemisphere]++;
                            if (hemisphere == 0) {
                                importantResourceRegionalCountHome[resourceChosenIndex]++;
                            } else {
                                importantResourceRegionalCountDistant[resourceChosenIndex]++;
                            }
                            resourcesPlacedCount[resourceChosenIndex]++;
                            removeRuralDistrict(kLocation.x, kLocation.y);
                            placeRuralDistrict(kLocation.x, kLocation.y);
                        }
                        else {
                            console.log("Resource Type Failure");
                        }
                    }
                }
            }
        }
    });

    //If we weren't able to place some resources, go through and force place the remaining ones, removing exisitng unimportant resources so the map isn't flooded
    let checkHomeHemisphereLP = true;
    let checkDistantHemisphereLP = true;
    for (let iY = 0; iY < iHeight; iY++) {
        for (let iX = 0; iX < iWidth; iX++) {
            // Don't put resources under existing districts
            let districtID = MapCities.getDistrict(iX, iY);
            let hemisphere = GameplayMap.getHemisphere(iX);
            if (districtID == null) {
                for (let i = 0; i < resourcesPlacedCount.length; ++i) {
                    let resourceToPlace = GameInfo.Resources.lookup(i);
                    if (resourceToPlace) {
                        let assignedHempisphere = ResourceBuilder.getResourceHemisphere(i);
                        if (assignedHempisphere > -1) {
                            if (assignedHempisphere != 2 && assignedHempisphere != hemisphere) {
                                continue;
                            }
   
                            if (hemisphere == 0 && importantResourceRegionalCountHome[i] < resourceToPlace.MinimumPerHemisphere || hemisphere == 1 && importantResourceRegionalCountDistant[i] < resourceToPlace.MinimumPerHemisphere) {
                                //Once LP class checks are complete, no need to do them anymore. These checks are for the age specific resource class, once met they are no longer important and don't need to be forced.
                                if (checkHomeHemisphereLP == true && hemisphere == 0) {
                                    checkHomeHemisphereLP = ResourceBuilder.isResourceClassRequiredForLegacyPath(i, hemisphere);
                                    if (!checkHomeHemisphereLP) {
                                        continue;
                                    }
                                }
                                else if (checkDistantHemisphereLP == true && hemisphere == 1) {
                                    checkDistantHemisphereLP = ResourceBuilder.isResourceClassRequiredForLegacyPath(i, hemisphere);
                                    if (!checkDistantHemisphereLP) {
                                        continue;
                                    }
                                }
       
                                if ((resourcesPlacedCount[i] > 0) && ResourceBuilder.isResourceRequiredForAge(i, hemisphere)) {
                                    if (ResourceBuilder.canHaveResource(iX, iY, i)) {

                                        //Try not to place adjacent to other resources. The new Poission map causes the old one to be ignored, which causes clumping.
                                        let hasAdjResource = false;
                                        for (let iDirection: DirectionTypes = 0; iDirection < DirectionTypes.NUM_DIRECTION_TYPES; iDirection++) {
                                            let iIndex: number = GameplayMap.getIndexFromXY(iX, iY);
                                            let iLocation: PlotCoord = GameplayMap.getLocationFromIndex(iIndex);
                                            let iAdjacentX: number = GameplayMap.getAdjacentPlotLocation(iLocation, iDirection).x;
                                            let iAdjacentY: number = GameplayMap.getAdjacentPlotLocation(iLocation, iDirection).y;
                                            if (GameplayMap.getResourceType(iAdjacentX, iAdjacentY) != ResourceTypes.NO_RESOURCE) {
                                                hasAdjResource = true;
                                                break;
                                            }
                                        }

                                        if (!hasAdjResource) {
                                            ResourceBuilder.setResourceType(iX, iY, i);
                                            let name: any = GameInfo.Resources.lookup(i)?.Name;
                                            console.log("Force Placed " + Locale.compose(name) + " at (" + iX + ", " + iY + ")");
                                            hemisphere == 0 ? importantResourceRegionalCountHome[i]++ : importantResourceRegionalCountDistant[i]++;
                                            removeRuralDistrict(iX, iY);
                                            placeRuralDistrict(iX, iY);
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
The language is JavaScript, the logic seem to be quite clear from comments, although it's hard to say if there are any bugs here by just reading code.

Some things here:
1. I don't see any logic for resources hidden behind narrative events. Looks like they are placed with goody huts distribution logic instead.
2. The function seem to use the same number for all resources
3. The function makes sure major resources (I assume treasure and factory, although it's not stated directly) are placed in exactly their number - if there's not enough space, they replace minor resources.
 
The language is JavaScript, the logic seem to be quite clear from comments, although it's hard to say if there are any bugs here by just reading code.

Some things here:
1. I don't see any logic for resources hidden behind narrative events. Looks like they are placed with goody huts distribution logic instead.
2. The function seem to use the same number for all resources
3. The function makes sure major resources (I assume treasure and factory, although it's not stated directly) are placed in exactly their number - if there's not enough space, they replace minor resources.
wrt 3, that may also refer to resources required for civ unlocks
 
Back
Top Bottom