• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Increasing/Reducing Income Structures

Status
Not open for further replies.
Level 12
Joined
May 9, 2009
Messages
735
Hello. I want to make a trigger that is an end result make income buildings which first have their total income increased based on the amount of buildings and then decreased after a certain amount of buildings is already there. I am sure there is some math term to describe it but I must not know it. Basically here is how it should work.

1 farm (57-0 per farm) = 1x57 = 57
2 farms (57-3 per farm) = 2x54 = 108
3 farms (57-6 per farm) = 3x51 = 153
4 farms (57-9 per farm) = 4x48 = 192
5 farms (57-12 per farm) = 5x45 = 225
6 farms (57-15 per farm) = 6x42 = 252
7 farms (57-18 per farm) = 7x39 = 273
8 farms (57-21 per farm) = 8x36 = 288
9 farms (57-24 per farm) = 9x33 = 297
10 farms (57-27 per farm) = 10x30 = 300
11 farms (57-30 per farm) = 11x27 = 297

As you see each farm subtracts from the base gold produced by each farm by 3. This way the total income gained from all farm rises up until the tenth farm and then it falls. By the time 20 farms are built 0 gold would be produced, but I want to make it so that it doesn't get below 1 gold per farm.

I will need to do some variable work but I don't know where to start. I will also have a floating text that say the amount of gold gained at each farm. I will need to group all the owned farms and based on that number alter some sort of variable or string in the floating text and the gold gained. Is this possible to do using GUI and if yes then how?
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
First of all, it is not (57-0, 57-3, 57-6) etc but it is (60-3, 60-6, 60-9) etc.
That way, you have a proper formula:
goldPerFarm = 60 - (3 * numberOfFarms)
totalGoldIcome = goldPerFarm * numberOfFarms

As you have noticed, this will beceme buggy and falls below 0.
What you want is limitting the goldPerFarm to a minimum of 1.
So what you do is take the highest value out of 1 and (60 - (3 * numberOfFarms)).
In GUI, there is an option "Math - Max" in which you fill in 1 and the calculation.
That way, you have limitted it to 1.

As a personal view, why can't people just use reals as resource values? WHY?!
You could make so much better formulas to define income and make it actually worthy to build stuff with less efficiency but never negative results... like:

61 * 0.99 ^ numberOfFarms
 
I do think Wietlol's exponential function is much more elegant for representing the value decline, but consider that when you multiply that with numberOfFarms to get the total income, you get a function that looks like this:

61x*(0.99x)

The derivative of this function is:

61*0.99x + 61x*(0.99x)*ln(0.99)

Or when simplified:

61*(0.99x)*(1 - 0.01x)

As you can see, once we pass 100 structures, we actually start losing income if we build any more. The limit of the original function approaches 0 as x -> infinity, so if we theoretically were to keep building structures, we would eventually have no income at all.

Of course, for all practical purpouses, it is safe to assume that you'd have less than 100 farms on the map. Still though, i think your original formula might be a bit more intuitive and easier for players to understand.

*engineer OUT!*
 
Last edited:
Level 12
Joined
May 9, 2009
Messages
735
This formula will make it so:

1) The amount of farms increase the total income
2) The amount on income has a cut-off point at 300 gold per interval
3) All farms generate gold

The down side is that a dumb player may shoot himself in the foot by making more than 10 farms. In my map this is a bonus tertiary method of gaining gold available as a race specialty of one race so a need there to be a cut-off point so the player can't indefinitely increase his income.

Math really makes my head hurt so I am not exactly motivated on getting down and testing it out but I will do so sooner or later.
 
Level 12
Joined
May 9, 2009
Messages
735
Alright, I am stuck on a part where I have to convert the integer to a string value for my floating text. Well the converting part is easy but the issue is that I need more than just the number.

When I convert the integer corresponding to the number of owned farms into a string all I get is a number. What I need is a yellow colored number with a + in front of it for my floating text. Something like this "|c00FFFF00+---insert variable here---|r"

I don't know how to get about doing that. Can anyone help out with this issue?
 
Level 37
Joined
Jul 22, 2015
Messages
3,485
Alright, I am stuck on a part where I have to convert the integer to a string value for my floating text. Well the converting part is easy but the issue is that I need more than just the number.

When I convert the integer corresponding to the number of owned farms into a string all I get is a number. What I need is a yellow colored number with a + in front of it for my floating text. Something like this "|c00FFFF00+---insert variable here---|r"

I don't know how to get about doing that. Can anyone help out with this issue?

Use Concatenate Strings. If you're using floating texts, you can adjust the color with the function already. No need to use hex codes.

  • Floating Text - Create floating text that reads (+ + (String(income))) above unit with Z offset 0.00, using font size 10.00, color (100.00%, 100.00%, 100.00%), and 0.00% transparency


  • String 1: +
  • String 2: (String(income))
 
Level 12
Joined
May 9, 2009
Messages
735
Use Concatenate Strings. If you're using floating texts, you can adjust the color with the function already. No need to use hex codes.

  • Floating Text - Create floating text that reads (+ + (String(income))) above unit with Z offset 0.00, using font size 10.00, color (100.00%, 100.00%, 100.00%), and 0.00% transparency


  • String 1: +
  • String 2: (String(income))
Thanks, I forgot all about the utility of changing the text color >.<

Although I just tested it and using concentrate strings and chopping up the hex code between several strings also works.
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,188
You can use a conditional to limit the formula at the maximum. This stops "a dumb player may shoot himself in the foot".

If (numberOfFarms) greater than or equal to 10 then set income to 300, else income to (numberOfFarms * (60 - (3 * numberOfFarms))).

Additionally you could outright limit the number of farms the player can build to 10 using the limit availability of unit type for player action. Once 10 farms are queued or better the player will be unable to order more farms to be built.
 
Level 12
Joined
May 9, 2009
Messages
735
You can use a conditional to limit the formula at the maximum. This stops "a dumb player may shoot himself in the foot".

If number of farms greater than or equal to 10 then set income to 300, else income equals (numberOfFarms * (60 - (3 * numberOfFarms))).
Won't that make it so any additional farms after 10 generate even more gold in total though?

I want there to be an upper limit on the total gold generated from all the farms combined.
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,188
Won't that make it so any additional farms after 10 generate even more gold in total though?
It limits it to exactly 300 with 10 farms. He could build 10 farms or 100 farms and it will still be limited to 300 thanks to the conditional disabling the formula (because the number of farms is greater than or equal to 10) and forcing the upper bound value of 300. This is why conditional statements are very useful in programming.

In other words the formula is only used between 1 and 9 farms, at 10 and more farms it just gives you the hard-coded upper bound of 300 independent of how many farms you actually own.
 
Use Concatenate Strings. If you're using floating texts, you can adjust the color with the function already. No need to use hex codes.

Actually, the coloring function for floating texts is bugged and will make your texts disappear under certain circumstances. I'd recommend using the hex codes instead.
 
Level 12
Joined
May 9, 2009
Messages
735
It limits it to exactly 300 with 10 farms. He could build 10 farms or 100 farms and it will still be limited to 300 thanks to the conditional disabling the formula (because the number of farms is greater than or equal to 10) and forcing the upper bound value of 300. This is why conditional statements are very useful in programming.

In other words the formula is only used between 1 and 9 farms, at 10 and more farms it just gives you the hard-coded upper bound of 300 independent of how many farms you actually own.
Ah I see. I guess I didn't get it at first because I am using a "pick all units in group" action to do the gold per farm and the floating text appears per farm as well, as in each farm produces this income. At 10 each farm would produce 30 gold.

I am not sure how the floating text would work this way... maybe I need to just get a better idea and not go with this formula all together hmmm...
 
If you want a floating text for each farm to visualize its output, you also need to remember that you can only have 100 text tags in the map at any given time. In other words, in a 12 player game, each player could have max 8 farms each, and every farm after that won't display text tags (if they are all created at the same time).
 
Level 12
Joined
May 9, 2009
Messages
735
If you want a floating text for each farm to visualize its output, you also need to remember that you can only have 100 text tags in the map at any given time. In other words, in a 12 player game, each player could have max 8 farms each, and every farm after that won't display text tags (if they are all created at the same time).
Well since the triggers are periodic events I have them be enabled only after the first farm owned by player x enters the map so that may not be an issue.

Still though maybe I will have the farm's income be dependent on the existence of a townhall as well and make a single floating text show the total at nearest town hall's location.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
But you can have 100 texttags shown... which means that you can have them hdden for each player except the owner of the buildings.
Then you can essentially have 1600 texttags. (Except that the texttags for neutral hostile arent really helpfull at that point.)
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,188
Ah I see. I guess I didn't get it at first because I am using a "pick all units in group" action to do the gold per farm and the floating text appears per farm as well, as in each farm produces this income. At 10 each farm would produce 30 gold.
However this would mean you pick each farm twice? Once to count it (the BJ for number of units in unit group works like this) and then another time to add the gold.

Better to count them, then add the gold in a single transaction.

If you still want to add it per farm then you can change the formula after 10 farms. Instead of "(numberOfFarms * (60 - (3 * numberOfFarms)))" you can change it to "(300 / numberOfFarms)". As long as you have a fractional gold accumulator you can then allow the player to build as many farms as he want but after 10 his total gold income no longer increases but instead stays the same.

If you want a floating text for each farm to visualize its output, you also need to remember that you can only have 100 text tags in the map at any given time. In other words, in a 12 player game, each player could have max 8 farms each, and every farm after that won't display text tags (if they are all created at the same time).
You do what Skibi TD did for its magic bars. You only show the floating text to a player if the player is nearby to see it. You can locally recycle floating text in a way that it will support all players viewing different farms which would normally exceed the 100 limit if each was separate or each farm always had one.
 
Level 12
Joined
May 9, 2009
Messages
735
Well this is my trigger so far and it works as per my initial idea.

Because I am not very comfortable with arrays I am multiplying it and the corresponding variables 9 times for each player.

  • farm income
    • Events
      • Time - Every 5.00 seconds of game time
    • Conditions
    • Actions
      • Set AAAGroupIncome1 = (Units owned by Player 1 (Red) of type Slaughterhouse)
      • Set AAAIntegerIncome1 = (Number of living Slaughterhouse units owned by Player 1 (Red))
      • Set AAAIntegerIncomeB1 = (60 - (AAAIntegerIncome1 x 3))
      • Set AAAStringIncome1 = (String(AAAIntegerIncomeB1))
      • Unit Group - Pick every unit in AAAGroupIncome1 and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • AAAIntegerIncomeB1 Less than or equal to 1
            • Then - Actions
              • Set AAAPositionIncome = (Position of (Picked unit))
              • Player - Add 1 to (Owner of (Picked unit)) Current gold
              • Floating Text - Create floating text that reads |c00FFFF00+1|r at AAAPositionIncome with Z offset 0.00, using font size 8.00, color (80.00%, 80.00%, 80.00%), and 0.00% transparency
              • Floating Text - Set the velocity of (Last created floating text) to 64.00 towards 90.00 degrees
              • Floating Text - Change (Last created floating text): Disable permanence
              • Floating Text - Change the lifespan of (Last created floating text) to 1.50 seconds
              • Floating Text - Change the fading age of (Last created floating text) to 1.00 seconds
              • Custom script: call RemoveLocation(udg_AAAPositionIncome)
            • Else - Actions
              • Set AAAPositionIncome = (Position of (Picked unit))
              • Player - Add AAAIntegerIncomeB1 to (Owner of (Picked unit)) Current gold
              • Floating Text - Create floating text that reads (|c00FFFF00+ + (AAAStringIncome1 + |r)) at AAAPositionIncome with Z offset 0.00, using font size 8.00, color (80.00%, 80.00%, 80.00%), and 0.00% transparency
              • Floating Text - Set the velocity of (Last created floating text) to 64.00 towards 90.00 degrees
              • Floating Text - Change (Last created floating text): Disable permanence
              • Floating Text - Change the lifespan of (Last created floating text) to 1.50 seconds
              • Floating Text - Change the fading age of (Last created floating text) to 1.00 seconds
              • Custom script: call RemoveLocation(udg_AAAPositionIncome)
The issue with the dividing idea is that 300/11 is 27.2727272727...

Floating text saying that is not very nice. I am already mildly annoyed that it shows any numbers other than multiples of 5.

Also I am not sure if gold works in decimals. Does it? If I add 0.27 to gold will it actually count it and add it up once all the decimals add up to 1 or will it just read it as 0?
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,188
The issue with the dividing idea is that 300/11 is 27.2727272727...
Which is why you use a fractional accumulator. Most farms would show 27 while some would show 28. The total still adds to 300.


Also I am not sure if gold works in decimals. Does it? If I add 0.27 to gold will it actually count it and add it up once all the decimals add up to 1 or will it just read it as 0?
Which is why you use a fractional accumulator to deal with the factional part and then add extra gold if it fills to a whole unit.

First work out the gold per farm, eg "300/11 is 27.2727272727...". Then add that much gold for each farm to the accumulator, which is unique for each player. You then separate out all the whole units from the accumulator (convert it to an integer) and add this as the gold. You then remove all the whole units from the accumulator by subtracting the accumulator by the gold amount. The result is all fractional gold parts accumulate in the accumulator and hence its name. Eventually these sub gold fractional parts become large enough that they become a whole unit of gold.

The error of this approach will be so small that the player might only lose 1 or 2 units of gold over an entire session no matter how many farms he makes.
 
Level 12
Joined
Nov 3, 2013
Messages
989
Wouldn't you want the gold income to always increase but not as much compared to the previous? Diminishing returns? Because it's a pretty big difference between a slower increase of gold per minute and a negative "increase".

The increase or acceleration of gold income should go toward 0 like 0.99^x would but you don't want the actual total gold income to eventually become 0 (or negative) lol...

I mean if you spend gold to make another building but it's actually going to reduce your gold per minute, what's the point of doing it?
There might as well be a hard cap of number of that building that can be built at that point, unless someone is dumb enough to PAY to LOSE gpm, lmao... :p
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,188
I mean if you spend gold to make another building but it's actually going to reduce your gold per minute, what's the point of doing it?
There might as well be a hard cap of number of that building that can be built at that point, unless someone is dumb enough to PAY to LOSE gpm, lmao... :p
I think the problem is a lack of knowledge how not to end up in that state rather than it being purposefully designed.
 
But you can have 100 texttags shown... which means that you can have them hdden for each player except the owner of the buildings.
Then you can essentially have 1600 texttags. (Except that the texttags for neutral hostile arent really helpfull at that point.)

Oh, that's true.... forgot about this!!

The issue with the dividing idea is that 300/11 is 27.2727272727...

Floating text saying that is not very nice. I am already mildly annoyed that it shows any numbers other than multiples of 5.

Also I am not sure if gold works in decimals. Does it? If I add 0.27 to gold will it actually count it and add it up once all the decimals add up to 1 or will it just read it as 0?

There is a way in which you can solve this that doesn't involve decimals. We can formulate an equation which has a horizontal asymptote at y = 300. This means that the equation must have one constant factor of 300, and one factor of x which approaches 0 as x->infinity. When x = 0, both must be 0. Here's what i came up with:

Code:
y = 300*(1 - e^-kx)

Where 300 is the limit, x is the number of farms, and k is the rate at which it reaches the limit (lower value on k means it reaches 300 slower). e is eulers constant (2,718), but you can use any number >1. Here's an example of an equation that reaches roughly 290 gold at 10 farms (and 299 after 18 farms):

Code:
300*(1-2,718^(-0.35x))

There are two problems with this approach though: firstly, you will never reach 300 (unless you round up). Second, the rate at which the gold amount decreases is exponential - between 2 and 4 farms, you increase your income by ~75, while between 4 and 6, you only increase it by ~37. Between and 8, you increase it by as little as ~18. This might not be the desired effect.

In my opinion, your best solution would be what Wietlol suggested (using your original method):

Code:
set numberOfFarms = [count units in group [units owner by player x of type farm]]
if numberOfFarms > 10 then
    set numberOfFarms = 10
endif
set playerIncome = numberOfFarms * (60 - 3*numberOfFarms)
set [gold of player x] = [gold of player x] + playerIncome

Loop it for each player x. This way, your income will stop at 300, rather than decreasing again if you build more farms.

EDIT: One thing i forgot which is VERY important! You only want to count income from farms which have completed construction. AFAIK, there is no smart way of checking this, but you can add a trigger which fires when a unit has finished construction, and in the actions, you add a special ability (without icon, and with no effect) to it. Later, you can check if a building has that ability to see if it has finished. If you have a unit indexer in your map, or know how to use hashtables, you could save a true or false value to it instead - but in your case the ability thing should do.
 
Level 12
Joined
May 9, 2009
Messages
735
I liked the way Dr Super Good expained it more... I can't understand all theses formulas and technical words. It's greek to me :s

Well all I did now was just make the ability to give income dependent on another building. If the player doesn't have that building farms don't give any income. If he does have it then the income received is shown at the location of that building (if there is more than 1 of those buildings it is shown at a randomly selected one of them).

This way the income of each building is not shown and when 11 buildings are reached then it just caps at 300 gold. It also kinda makes sense because the name of the income ability is called 'tribute' and the building that is needed for it to work is a daemon gate (fell orcs giving tribute to their daemonic masters).

  • farm income1
    • Events
      • Time - Every 60.00 seconds of game time
    • Conditions
      • (Number of living Slaughterhouse units owned by Player 1 (Red)) Greater than or equal to 1
    • Actions
      • Set AAAIntegerIncome1 = (60 - ((Number of units in (Units owned by Player 1 (Red) of type Slaughterhouse)) x 3))
      • Set AAAIntegerIncomeB1 = (AAAIntegerIncome1 x (Number of units in (Units owned by Player 1 (Red) of type Slaughterhouse)))
      • Set AAAStringIncome1 = (String(AAAIntegerIncomeB1))
      • Unit Group - Pick every unit in (Random 1 units from (Units owned by Player 1 (Red) of type Daemon Gate)) and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • AAAIntegerIncome1 Less than or equal to 30
            • Then - Actions
              • Set AAAPositionIncome = (Position of (Picked unit))
              • Special Effect - Create a special effect at AAAPositionIncome using Abilities\Spells\Other\Transmute\PileofGold.mdl
              • Special Effect - Destroy (Last created special effect)
              • Player - Add 300 to (Owner of (Picked unit)) Current gold
              • Floating Text - Create floating text that reads |c00FFFF00+300|r at AAAPositionIncome with Z offset 0.00, using font size 8.00, color (80.00%, 80.00%, 80.00%), and 0.00% transparency
              • Floating Text - Set the velocity of (Last created floating text) to 64.00 towards 90.00 degrees
              • Floating Text - Change (Last created floating text): Disable permanence
              • Floating Text - Change the lifespan of (Last created floating text) to 1.50 seconds
              • Floating Text - Change the fading age of (Last created floating text) to 1.00 seconds
              • Custom script: call RemoveLocation(udg_AAAPositionIncome)
            • Else - Actions
              • Set AAAPositionIncome = (Position of (Picked unit))
              • Special Effect - Create a special effect at AAAPositionIncome using Abilities\Spells\Other\Transmute\PileofGold.mdl
              • Special Effect - Destroy (Last created special effect)
              • Player - Add AAAIntegerIncomeB1 to (Owner of (Picked unit)) Current gold
              • Floating Text - Create floating text that reads (|c00FFFF00+ + (AAAStringIncome1 + |r)) at AAAPositionIncome with Z offset 0.00, using font size 8.00, color (80.00%, 80.00%, 80.00%), and 0.00% transparency
              • Floating Text - Set the velocity of (Last created floating text) to 64.00 towards 90.00 degrees
              • Floating Text - Change (Last created floating text): Disable permanence
              • Floating Text - Change the lifespan of (Last created floating text) to 1.50 seconds
              • Floating Text - Change the fading age of (Last created floating text) to 1.00 seconds
              • Custom script: call RemoveLocation(udg_AAAPositionIncome)
Thanks for the replies everyone!
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,188
Set AAAIntegerIncomeB1 = (AAAIntegerIncome1 x (Number of units in (Units owned by Player 1 (Red) of type Slaughterhouse)))
Unit Group - Pick every unit in (Random 1 units from (Units owned by Player 1 (Red) of type Daemon Gate)) and do (Actions)
Both leak non trivial groups. This sort of leak is bad and can cause major performance degradation over time.

You have a lot of procedural coupling in the if statement. You do not need to duplicate all the actions. You only need to modify the variable with an if statement and that can be done outside the loop.
 
Level 12
Joined
May 9, 2009
Messages
735
Both leak non trivial groups. This sort of leak is bad and can cause major performance degradation over time.

You have a lot of procedural coupling in the if statement. You do not need to duplicate all the actions. You only need to modify the variable with an if statement and that can be done outside the loop.
I'll fix the leaks but I don't understand a word of the second line :vw_wtf:
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,188
I don't understand a word of the second line
Instead of doing something like this.
JASS:
local int x = ...
if x >= 300 then
    call A(300)
    call B()
    call C()
else
    call A(x)
    call B()
    call C()
endif
It can become this which is functionally equivalent...
JASS:
local int x = ...
if x >= 300 then
    set x = 300
endif
call A(x)
call B()
call C()
This reduces the amount of code duplication making the code more compact, readable and maintainable.
 
Status
Not open for further replies.
Top