Balancing citystates: ignore/capture/alliance

Giving production would be an easy extension of the food mechanic, but creating all the rest of a new CS would take a day at least, if it's even possible (from experience I suspect something that complex wouldn't be).

Odd bit of trivia: Civ 5 originally had a Scientific citystate trait... all the stuff for them is still around in the files, though I don't know if any of it works. Research agreements also originally gave science per turn, and other things that never made it out of beta.
 
I just figured if there were more types of CS it would be less likely that you would ally with more then 1 or 2 of the same type and thus reduce the issue of having stacking city state effects that are too powerful.

Another idea...what if you could choose between 2 rewards for being allies with CS, like either production or a millitary unit. Or food from maritime or a naval unit. This could add to the strategy of how you want to play the game. Maybe when crossing the threshold of nothing to friend or friend to ally you were presented with an either or dialog box or something, that way it would still function with the CSD mod?
 
Having just read through this thread, which I didn't know existed, here are my opinions:

* Most importantly: most players are still not making CS capture an unbalancing priority, so there's no need to consider nerfing any of the benefits.

* Caravans were removed for a reason, but trade routes potentially being broken would be nice. However, this is probably very hard to implement realistically post-Astronomy.

* I like CS influence being dictated by distance. It creates the sort of random minor variable I enjoy, and feels realistic.

* I was also torn between % vs # in the Maritime food question, but tilt toward #, since smaller cities tend to grow faster anyway.

* I don't think culture needs any adjustment.

* Military CS could be further buffed by adapting the Mercenaries mod and having Militaries provide a mercenary service.
 
I just figured if there were more types of CS it would be less likely that you would ally with more then 1 or 2 of the same type and thus reduce the issue of having stacking city state effects that are too powerful.

Another idea...what if you could choose between 2 rewards for being allies with CS, like either production or a millitary unit. Or food from maritime or a naval unit. This could add to the strategy of how you want to play the game. Maybe when crossing the threshold of nothing to friend or friend to ally you were presented with an either or dialog box or something, that way it would still function with the CSD mod?

I think creating a production function for the CS would be as problematic as creating one for gold. In my mind CS operate on an end-product level - i.e., they provide what hammers and gold generate. Having them give hammers blurs their function, and turns them into a version of one of your own cities. To me this is not so interesting.
 
I think creating a production function for the CS would be as problematic as creating one for gold. In my mind CS operate on an end-product level - i.e., they provide what hammers and gold generate. Having them give hammers blurs their function, and turns them into a version of one of your own cities. To me this is not so interesting.

I totally agree with you.


As for the military CS. How about an additional XP bonus you get in every/capital city. Per friendly (maybe more when allied) CS you get +5 XP (of course additionally to your barracks etc.).
This would fit nicely with the previous ideas here, that the CSs send some delegate to train a special unit that is then gifted in one of our cities. So our normal units that are being trained (constructed), do sometimes watch the delegate at his training lessons and say "woa did you see that cool move?" and whoops, +5 XP :D
Thinking of it... that would be a cool idea :goodjob:
 
I think creating a production function for the CS would be as problematic as creating one for gold. In my mind CS operate on an end-product level - i.e., they provide what hammers and gold generate. Having them give hammers blurs their function, and turns them into a version of one of your own cities. To me this is not so interesting.

Mostly what i was thinking of was that if would send production to maybe the 3 lowest production cities so that cities with a lot of costal tiles or ones on islands etc would be able to get some more production, but if its not an easy fix i dont really feel its necesary. I was more interested in having some choice with what reward you might get from the city states, i feel like that could add a lot of depth, etc.
 
In v4.06 beta Maritime city-states now give total 2:5 (friend:ally) split among the largest 5 cities.
This feels a too weak overall, particularly past the early game.
A MCS friendship only gives the same effect as a single vanilla granary, while still costing 1 gold per turn?

Wouldn't I be better off with a military city state, particularly in the lategame, and working a few more farms instead of mines?

If you're going back to a fixed food bonus, which is not a bad idea, then perhaps it should scale by era, as it did in pre-patch vanilla?

I could see 3/5 (friend/ally) in the classical era, and then +1/1.5 per era.
 
Good idea about era bonuses, I'll add some. :goodjob:

The only frustrating part is that since the notification about how much food we get is partially hardcoded... I have to write everything out on a single tooltip that won't show current bonuses. It's a choice of possibly confusing (if short) or a lot of reading material (if detailed).
 
Thal, are you checking for the Avoid Growth button when you loop over cities? If you don't, you should seriously consider letting people control where their food goes with this. I'm not sure if you want to cap the amount of food a city can get from MCS but I'm throwing it out there anyways.
 
How about using weights instead of sorting? It's faster because you don't have to re-sort a list every turn and it automatically deals with such special cases.

For example, city_weight = max(pop*10 + avoid*(-1000), 0)

p = city_weight/sum_weights = food_for_city/total_food

This would mean if all cities are on avoid, you would get equal food distribution - but otherwise it would work as you'd want it to. And if all cities are on avoid, you probably don't care where your food goes anyways.
 
Trying to visualize how that would work... time for openoffice calc! :think:

Edit:
Playing around with the formulas a bit I see now what you're getting at, basically take the total food and distribute it to all cities with more to those with higher population. That's something I hadn't considered, so we actually have three options:

  • Change the flat modifier to a percentage bonus.
  • Split finite food to the top X cities.
  • Split finite food among all cities, weighted by factors like population, avoid status, even if it's connected to the capital, etc.

It'd be very similar to the customization I added to the Emigration mod, weighing the likelihood of cities to lose population.

Even with my current method this would speed things up a lot:

  1. value = -avoid*1000 + pop*1 + city:GetID()*0.001
  2. find top X values

Basically convert each sort parameter into a decimal representation, with different parameters separated by orders of magnitude. It'd be relatively fast execution to loop through this to find the top 5.

The real problem though is what I described in the 'wish we could trace execution' thread. I have to recalculate this stuff every single time it's needed because the global storing the table gets nil'd without me doing anything! It's like the global is going out of scope and getting garbage collected... which makes no sense for a global. The only explanation I can think of is if it's a multithreading issue, and I don't know how to properly resolve that situation in Lua. :undecide:
 
Honestly, I still don't understand that issue. My globals always seem to work fine. I didn't have time to check your code in the mod, but I think it must be something like executing the file twice. Maybe try printing the context pointer and see if it changes.

I actually like working on weights better because it's not as discontinuous as saying "top 5 cities". Ideally, you would allow the player to override yields and boost certain cities, but that's a bit of work.
 
I don't know how to use contexts or their pointers, but the file is included in many places, similar to your lib.lua. In C++ multiple-include issues would be solved with precompiler statements... but I don't know what to do about it in Lua. I've done a lot of google searches to try and find an answer without success.

With extraneous information removed it's a simple algorithm:

PHP:
playerCityRewards = {}

function GetAllCityRewards(majorCivID, doUpdate)
    if doUpdate then
        playerCityRewards[majorCivID] = {}
        ...
        playerCityRewards[majorCivID][cityList[k].ID] = ...values...
    end
    return playerCityRewards[majorCivID]
end

function ModdedFoodDifference(city)
    local rewards = GetAllCityRewards(city:GetOwner(), false)
    return city:FoodDifference() + rewards["food"]
end

function GiveMinorCivAllianceRewards()
    ...loop through all players...
    for k,v in pairs(GetAllCityRewards(majorCivID, true)) do
        ...stuff...
    end
end
ModdedFoodDifference replaces FoodDifference in all the UI files. This file is included in all the ones that use the modified function.

GiveMinorCivAllianceRewards is run at the start of each turn.

Clearly, it should set up the table, then retrieve it. Something else is going on and I suspect it's due to me including this file in other places.
 
Ah, that could be a problem. I'm not sure what Lua does with variables if you include a file in multiple locations. I think the VM just compiles it once and then copies the code, so whenever you include it in a new location, the variable will be in the initial state rather than the current state.

You'll notice that lib only has "passive" definitions: functions with local scope that simply inspect other things and do formatting stuff. That is safe to put into include statements. For things that need a variable scope, use contexts and share variables via superglobals or ShareData.
 
How should I go about properly creating globals, then? I tried adding Game. or _G. to the front, which didn't work. I've had difficulty finding information on this with searches.

I've started thinking I really like the idea of decimal-based weighted food distribution, I know the game engine can handle fractional food. The one problem with how to deal with 'avoid' status is if it zeroes out the gains and a player sets every city but one set on avoid, then just five maritimes could send 50:c5food:+ to a city of the player's choosing. People can usually find ways to exploit extremes like that.

So my thought is to normalize the avoid modifier itself, as a weight based on the number of cities on avoid status.

  • 1 city on avoid = 0% weight
  • 100% cities on avoid = 100% weight

Thinking about how the weight could be calculated, it could be something like:

Weight = population

Multipliers:
000% - IsRazing
200% - IsResistance
050% - IsPuppet
100% - IsOccupied
050% - not(IsCapitalConnectedToCity)
050% - IsBlockaded
200% - IsCapital
00X% - IsForcedAvoidGrowth

The only one that might not be obvious is the double bonus for cities in resistance. This would help prevent mass starvation scenarios, and a smart player would set a bunch of cities on Avoid to send food there anyway. Also, while being disconnected from the trade network cuts food shipments in half, if it's due to hostiles actively blockading the city it gets cut further (everything stacks).
 
I'm not sure it's really a problem if you send all your food to one city. Yes, you can have one super-sized city. However, you pay a pretty high price in the development of other cities. Since smaller cities grow faster, the growth impact of distributed food is larger than that of food which is funneled into one city.

The best way to exploit this is to pour as much food as possible into your science city. What happens when you do that? Well you get 0.5 additional science per pop compared to distributing the food among all science cities due to the NC. That's about it. I could see this turning into a problem if you have a lot of additional specialist slots so you can really boost this city using scientists.

If you want to do something to discourage this, the easiest way in my opinion is to add a negative quadratic contribution to food output. That is, if n food is allocated to a city, it only gets n - 0.01*n^2 food. For 10 food, this means you only get 9, for 20, you get 16. So food is used more efficiently if split up. I'm pretty sure I would like that better than being forced to waste food on a city that is on avoid growth.

As for variables, the easiest way is to add something to one of the superglobals. For example create a MapModData.Balance table where you put all variables you want to share. The MapModData table is available in all contexts so you can use the variables from everywhere. Alternatively, you can use Whys' ShareData which is also quite comfortable to use.
 
Thank you, that will help a lot! The reason I haven't used ShareData is I don't need to transfer data between mods or save it in the savegame, so there's no need for the extra overhead. I did some testing and found the save operation to be relatively time-intensive. :)

Basically what I'm saying about the avoid thing is to take your original idea (blue line) and reduce the discontinuity from 99% to 100% with a smooth transition (green line). After all, the excellent reason you pointed out to use weights is to reduce discontinuities in food distribution. :goodjob:

attachment.php



The only two effects of avoid status are preventing the population from increasing, and changes the automatic governor's priorities. A player can still micro their cities and citizens for normal food intake everywhere, while turning off avoid the 1 turn a city needs to grow. It would allow full control over the maritime system to quickly grow newly-settled cities in a way the AI wouldn't know how to use.

You rightly pointed out that food has more of an effect in a smaller city, it gives more population points per turn. Targeting food to small cities is my concern (not the science city). Allowing players to send 30-60 or more food to a new city would allow that city (or several) to grow at a fantastic rate. This would shift the balance of some other things in the game. For example, it would make the Collective Rule policy less useful for expanding empires (where cities start at 2:c5citizen:).

Beyond that, I've found that whenever dealing with extremes in games, creative players can usually find exploits you and I might not ever think of.

The reason I feel applying the quadratic to the "avoid" stage instead of the "food" stage is reducing food quadratically would impact small empires with ~5 cities more than large empires with ~50 cities, somewhat the opposite of what I'm trying to achieve. It'd also reduce the value of each additional maritime ally more than the existing effect of diminishing returns (5 to 10 is more important than 50 to 55).

One thing I could do is reduce the total food based on how many cities are on avoid, before that food is distributed at all. I'm not certain this would be the best option though, since it'd add a penalty to checking 'avoid' that might be undesirable.
 

Attachments

  • AvoidGrowth.PNG
    AvoidGrowth.PNG
    21.5 KB · Views: 185
My personal preference is that exploit prevention trump design elegance. Given how well this part of the mod is working already, I'd rather have a simple, more inexact mechanic operating than one that's potentially seriously flawed from a game play perspective.
 
Back
Top Bottom