yairm210/Unciv

Countable scopes approach decision

PLynx01 opened this issue · 6 comments

Before creating

  • This is NOT a gameplay feature from Civ VI, BNW, or outside - see Roadmap
  • This is NOT a gameplay feature from Vanilla Civ V or from G&K - If so, it should be a comment in #4697

Problem Description

A month ago, I've implemented the Countable comparison conditional uniques.

Currently, they only support numbers, year, stats and resources

I strongly believe we should overhaul that mechanic, in order to support a wide range of numeric game variables:

In that case, we need to know which scope do they apply to. For example, population can apply to one city, civilization or the entire map.

Same with number of cities. We need to know whether it applies to specified civilization(s), or the entire map.

To demonstrate this, I show you three approaches towards countable scopes, with examples.

All of these examples do the same - return true when there is more than 25 cities on the entire map

The examples are described in Desired Solution and Alternative Approaches

Related Issue Links

#11389
The original issue and the list of proposed countables

#11308
The pull request adding the countable uniques

Desired Solution

Approach 1 - Additional scope parameters

<when [Game-Wide] [Number of Cities] is greater than [Number] [25]>

Alternative Approaches

Approach 2 - Scope-specific names

<when [Number of All Cities] is greater than [25]>

Approach 3 - Countable limiters and modifiers

<when [Number of Cities] is greater than [25]> <@game-wide>

Additional Context

My thoughts:

Approach 1, in my opinion is the best. The drawback of 4 instead of 2 parameters is not really an issue.

Approach 2 requires making a huge amount of countable filter values (possibly even hundreds which is contradictory with modding philosophy)

Approach 3 makes it difficult to distinguish between countable limiter/modifier and the next conditional (all conditionals must be true to make an unique active). There is also a problem regarding the scope of the second countable.

If you want to opt for Solution 3, you will need to add some kind of countable limiter/modifier, denoted by different character (maybe @, pronounced as "at")

@yairm210 @SomeTroglodyte @RealBamboolord @woo1127
We need to decide between these three approaches. Maybe we should make a poll?

Approach 3 is right out - how do we know to "link" those specific modifiers and not others?
Approach 1 is good, but I don't like that we need to specify "number 25" - that doesn't look good as text.
Approach 2 seems to be the best of breed, we can be as specific as we want or as general in the same format

Approach 1 seems good, although I don't understand what we need the awkward [Number] part for

Approach 4: UniqueParameterType.Formula - implemented as generic math parser supporting a small set of the usual operators, + - * / % ! && || < > == != <= >= ^, (), and operands literal or $game_var, the latter being extensible and supporting scope. prefixes. Result type Float, implicit conversion to/from Boolean as required by mapping to 0 or 1 or as != 0 with an epsilon. Defer decision whether to allow string expressions... And whether to support ternary operators (with string ops potentially a very powerful modding option - trigger grants promotion A or B depending on...)

If we decide to go there eventually, then keep this as small and simple as possible...

If someone digs up a code template, CC0 or PD, to copy most of, could be done in a week. After all, parsers are common. Edit: 1

What's simplest of those approaches? To code and maintain? Maybe 2 - complexity concentrated in one place, namely parsing parameters left to right keeping track of last selected scope. Drop "Number" - any parameter whose scope is unambiguous isn't required to specify one, similar code complexity as requiring pairs throughout. Requires scope names to be uniquely different from any countable name though. Can use some of my experimental data model - a countable being an interface that can be implemented by an empowered Enum or by non-enum dispatchers like the Stat one. But the value getter would receive a Scope parameter from the upstream parser, and the interface should likely get a supportsScopes function for validation. All of that code could then be easily reused in UniqueParameterType.Formula.

Footnotes

  1. Learning ANTLR and use the kotlin target might be the way to go for that

I suggest you to opt for the blend of 1 and 2 approach

The aforementioned example "return true when there is more than 25 cities on the entire map" would be like this

<when [Global Number of Cities] [] is greater than [25] []>

These empty parameters would be places to insert a limiters of the specified filter

<when [Global Number of Cities] [Enemy] is greater than [10] []>

The second example would return true if there are more than 10 cities of hostile civilization.

The general syntax would be like this:

<when [countable] [optional limiter(s)] is greater than [countable] [optional limiter(s)]>

What is your opinion? If you don't understand something or if you want additional examples, feel free to ask me.

BTW, we should somehow attach buildingFilter to cityFilter, allowing to choose cities that have the specified building.

Example:
<when [Global Number of Cities] [University] is greater than [10] []>

Returns true when there are more than 10 cities with University

As you can see, there is an obvious problem with wording. Maybe we could inject some words when the buildingFilter is used as cityFilter?

So, it would display like this:

When Global Number of Cities with University building is greater than 10

I hope you understand.

I suggest you to opt for the blend of 1 and 2 approach

The aforementioned example "return true when there is more than 25 cities on the entire map" would be like this

<when [Global Number of Cities] [] is greater than [25] []>

These empty parameters would be places to insert a limiters of the specified filter

<when [Global Number of Cities] [Enemy] is greater than [10] []>

The second example would return true if there are more than 10 cities of hostile civilization.

The general syntax would be like this:

<when [countable] [optional limiter(s)] is greater than [countable] [optional limiter(s)]>

What is your opinion? If you don't understand something or if you want additional examples, feel free to ask me.

@yairm210 @SomeTroglodyte @RealBamboolord @woo1127

Please review my proposal