Skip to content

Inflow (Wealth) implementation (Experimental/Proposal)#862

Open
stavrosfa wants to merge 4 commits intoC7-Game:Developmentfrom
stavrosfa:feature/inflows-lua-integration
Open

Inflow (Wealth) implementation (Experimental/Proposal)#862
stavrosfa wants to merge 4 commits intoC7-Game:Developmentfrom
stavrosfa:feature/inflows-lua-integration

Conversation

@stavrosfa
Copy link
Contributor

This PR is a product of a discussion we had in discord regarding the implementation of "Wealth". Check it out here

There is a lot of boiler plate code because a new type of producable items was introduced.
I ended up calling it Inflow, not that it's a good name, but I spent at least 2 hours searching for a proper term, in the end I just went with something.

This type/category is meant to host items like "Wealth". The wealth producable is fully implemented. I have included 2 other examples with dummy code, I hope you will get the idea.

The point is to have stuff like wealth, that add to the city (and perhaps in the future globally), so in my examples you can see another item called "Cultivation" that is boosting the city's culture based on its current value, and another called "Expertise" that boosts science based on the city's beakers. So it's like Wealth but for Culture and Science.

These are fully exposed to the lua layer so it can do whatever it's instructed, so no need to hardcode anything in C#. We could add more restrictions like after what tech each is available and so on, this is I guess a more minimalistic example as I have simply hardcoded CanProduce() to true.

The triggers that call Lua eventually is in the Player and City classes. All 3 triggers send an integer so that Lua can use to calculate what it should do. At first I was passing the city object, but I had some infinite loop issues where I was calling from lua the method that was calling it in C#, so again, for now and for the sake of simplicity I pass an int to base the calcualtions on. This can change in the future.

If it's any easier for you to understand, I kind of tried to follow the way Terraform/TerraformImprovement is implemented and integrated with Lua.

FInal thing, this is not the "final" implementation, there are many things to be done for this to be actually merged, this is more of a POC.

Let me know what you think or if you have any questions.

@WildWeazel I hope you will like this!

Cheers!

local useful_shields = context.input
local known_techs = context.techs
local double_effect = any(known_techs,
function(x)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this lambda be pulled out to a named function? The formatting here otherwise makes this a little weird to read

end
)
local ratio = rules().ShieldCostPerGold
return math.max(1, useful_shields / (double_effect and (ratio / 2) or ratio))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the lua nits, feel free to ignore this

But would doing local ratio = double_effect and (rules().ShieldCostPerGold / 2) or rules().ShieldCostPerGold make sense?

return math.max(1, useful_shields / (double_effect and (ratio / 2) or ratio))
end

-- example
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this and the one below, what would this be an example of? Could you document the idea in the code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have made this look more like it should now, and added a comment to document the usage of this. let me know if it's enough, or I should add more.

Since it makes sense to me as I wrote it, I might be missing the bigger picture.

return extra_science_calculation(context)
end,
},
-- example
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto with these examples - I know the PR has some details, but if we document here too it avoids having to go through git blame

}

public int GetCulturePerTurn() {
public int GetCulturePerTurn(bool bonus = true) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's this bonus parameter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, that was a leftover from a test and it was quicker to do it like this. It's been removed.

-- example
local function extra_culture_calculation(context)
local city_culture = context.input
return math.max(1, city_culture/2)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these default to doing nothing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it was there purely as an example instead of trying to explain it with words

@TomWerner
Copy link
Contributor

Cool! Implementing wealth is nice, though I think we'd want the culture/science modifiers off by default.

Added some extra functionality.
Some guardrails for Lua not to call directly methods that might call themselves again in an endless loop.
Copy link
Contributor Author

@stavrosfa stavrosfa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I 've pushed a commit that should resemble more the final product than this.

}

public int GetCulturePerTurn() {
public int GetCulturePerTurn(bool bonus = true) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, that was a leftover from a test and it was quicker to do it like this. It's been removed.

return math.max(1, useful_shields / (double_effect and (ratio / 2) or ratio))
end

-- example
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have made this look more like it should now, and added a comment to document the usage of this. let me know if it's enough, or I should add more.

Since it makes sense to me as I wrote it, I might be missing the bigger picture.

-- example
local function extra_culture_calculation(context)
local city_culture = context.input
return math.max(1, city_culture/2)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it was there purely as an example instead of trying to explain it with words

@stavrosfa
Copy link
Contributor Author

I have added some extra functionality (corruption, happiness, etc).

I think there is an issue with how we generally calculate these values, I 've opened an issue here, but the new implementation is wokring on top as it should. When this is fixed, we should be 100% there.

On the code side, I decided to change the ScriptContext to include the Player and the city, as a simple integer was too simple and limiting.

The risk to recursively call lua that calls c# is still tehre, but I have tried to add some guardrails for lua to not call c# methods that directly call themselves, by splitting the methods that calculate the base value and the ones that take the lua result into account, and also add the [MoonSharpHidden] annotation to those that call lua directly.
This is still not enough, because lua could call the c# method that calls the c# method that calls lua, but I am not sure how to properly handle that.

We could add some kind of enum to pass around c# and lua to let the game know which layer is calling these methods, and when it's lua we should skip some parts, but it's clanky.
Or simply have an call caps wanring in the lua files "be careful not to call methods that call themselves" ?!

Let me know if you have any feedback on this.

Copy link
Contributor

@TomWerner TomWerner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good, thanks!

This definitely opens up some interesting possibilities. Since something like additional culture per turn would be pretty powerful, it could be balanced by lots of pollution or unhappiness or something. Neat!

Tidied up the Inflow API to reduce code repetition of how we access the yield func
@stavrosfa
Copy link
Contributor Author

This definitely opens up some interesting possibilities. Since something like additional culture per turn would be pretty powerful, it could be balanced by lots of pollution or unhappiness or something. Neat!

Yeah I have a TODO regarding pollution and other things, but couldn't integrate them since I don't they are implemented yet. Feel free to suggest some more I haven't thought of!

I pushed some minor refactoring to reduce duplication, but the logic is the exact same, and added a few TODOs that I wanted to add before but forgot.

I guess this is good to merge?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants