1 /*****************************************************************************
2  * Copyright (c) 2014-2020 OpenRCT2 developers
3  *
4  * For a complete list of all authors, please refer to contributors.md
5  * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6  *
7  * OpenRCT2 is licensed under the GNU General Public License version 3.
8  *****************************************************************************/
9 
10 #include "Finance.h"
11 
12 #include "../Context.h"
13 #include "../Game.h"
14 #include "../OpenRCT2.h"
15 #include "../interface/Window.h"
16 #include "../localisation/Date.h"
17 #include "../localisation/Localisation.h"
18 #include "../peep/Peep.h"
19 #include "../peep/Staff.h"
20 #include "../ride/Ride.h"
21 #include "../scenario/Scenario.h"
22 #include "../util/Util.h"
23 #include "../windows/Intent.h"
24 #include "../world/Park.h"
25 
26 // Monthly research funding costs
27 const money32 research_cost_table[RESEARCH_FUNDING_COUNT] = {
28     MONEY(0, 00),   // No funding
29     MONEY(100, 00), // Minimum funding
30     MONEY(200, 00), // Normal funding
31     MONEY(400, 00), // Maximum funding
32 };
33 
34 static constexpr const int32_t dword_988E60[static_cast<int32_t>(ExpenditureType::Count)] = {
35     1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0,
36 };
37 
38 money64 gInitialCash;
39 money64 gCash;
40 money64 gBankLoan;
41 uint8_t gBankLoanInterestRate;
42 money64 gMaxBankLoan;
43 money64 gCurrentExpenditure;
44 money64 gCurrentProfit;
45 money64 gHistoricalProfit;
46 money64 gWeeklyProfitAverageDividend;
47 uint16_t gWeeklyProfitAverageDivisor;
48 money64 gCashHistory[FINANCE_GRAPH_SIZE];
49 money64 gWeeklyProfitHistory[FINANCE_GRAPH_SIZE];
50 money64 gParkValueHistory[FINANCE_GRAPH_SIZE];
51 money64 gExpenditureTable[EXPENDITURE_TABLE_MONTH_COUNT][static_cast<int32_t>(ExpenditureType::Count)];
52 
53 /**
54  * Checks the condition if the game is required to use money.
55  * @param flags game command flags.
56  */
finance_check_money_required(uint32_t flags)57 bool finance_check_money_required(uint32_t flags)
58 {
59     if (gParkFlags & PARK_FLAGS_NO_MONEY)
60         return false;
61     if (gScreenFlags & SCREEN_FLAGS_EDITOR)
62         return false;
63     if (flags & GAME_COMMAND_FLAG_NO_SPEND)
64         return false;
65     if (flags & GAME_COMMAND_FLAG_GHOST)
66         return false;
67     return true;
68 }
69 
70 /**
71  * Checks if enough money is available.
72  * @param cost.
73  * @param flags game command flags.
74  */
finance_check_affordability(money32 cost,uint32_t flags)75 bool finance_check_affordability(money32 cost, uint32_t flags)
76 {
77     return !finance_check_money_required(flags) || cost <= 0 || cost <= gCash;
78 }
79 
80 /**
81  * Pay an amount of money.
82  *  rct2: 0x069C674
83  * @param amount (eax)
84  * @param type passed via global var 0x0141F56C (RCT2_ADDRESS_NEXT_EXPENDITURE_TYPE), our type is that var/4.
85  */
finance_payment(money32 amount,ExpenditureType type)86 void finance_payment(money32 amount, ExpenditureType type)
87 {
88     // overflow check
89     gCash = add_clamp_money32(gCash, -amount);
90 
91     gExpenditureTable[0][static_cast<int32_t>(type)] -= amount;
92     if (dword_988E60[static_cast<int32_t>(type)] & 1)
93     {
94         // Cumulative amount of money spent this day
95         gCurrentExpenditure -= amount;
96     }
97 
98     auto intent = Intent(INTENT_ACTION_UPDATE_CASH);
99     context_broadcast_intent(&intent);
100 }
101 
102 /**
103  * Pays the wages of all active staff members in the park.
104  *  rct2: 0x006C18A9
105  */
finance_pay_wages()106 void finance_pay_wages()
107 {
108     if (gParkFlags & PARK_FLAGS_NO_MONEY)
109     {
110         return;
111     }
112 
113     for (auto peep : EntityList<Staff>())
114     {
115         finance_payment(GetStaffWage(peep->AssignedStaffType) / 4, ExpenditureType::Wages);
116     }
117 }
118 
119 /**
120  * Pays the current research level's cost.
121  * rct2: 0x00684DA5
122  **/
finance_pay_research()123 void finance_pay_research()
124 {
125     if (gParkFlags & PARK_FLAGS_NO_MONEY)
126     {
127         return;
128     }
129 
130     const uint8_t level = gResearchFundingLevel;
131     finance_payment(research_cost_table[level] / 4, ExpenditureType::Research);
132 }
133 
134 /**
135  * Pay interest on current loans.
136  *  rct2: 0x0069E092
137  */
finance_pay_interest()138 void finance_pay_interest()
139 {
140     if (gParkFlags & PARK_FLAGS_NO_MONEY)
141     {
142         return;
143     }
144 
145     // This variable uses the 64-bit type as the computation below can involve multiplying very large numbers
146     // that will overflow money32 if the loan is greater than (1 << 31) / (5 * current_interest_rate)
147     const money64 current_loan = gBankLoan;
148     const uint8_t current_interest_rate = gBankLoanInterestRate;
149     const money32 interest_to_pay = (current_loan * 5 * current_interest_rate) >> 14;
150 
151     finance_payment(interest_to_pay, ExpenditureType::Interest);
152 }
153 
154 /**
155  *
156  *  rct2: 0x006AC885
157  */
finance_pay_ride_upkeep()158 void finance_pay_ride_upkeep()
159 {
160     for (auto& ride : GetRideManager())
161     {
162         if (!(ride.lifecycle_flags & RIDE_LIFECYCLE_EVER_BEEN_OPENED))
163         {
164             ride.Renew();
165         }
166 
167         if (ride.status != RideStatus::Closed && !(gParkFlags & PARK_FLAGS_NO_MONEY))
168         {
169             int16_t upkeep = ride.upkeep_cost;
170             if (upkeep != -1)
171             {
172                 ride.total_profit -= upkeep;
173                 ride.window_invalidate_flags |= RIDE_INVALIDATE_RIDE_INCOME;
174                 finance_payment(upkeep, ExpenditureType::RideRunningCosts);
175             }
176         }
177 
178         if (ride.last_crash_type != RIDE_CRASH_TYPE_NONE)
179         {
180             ride.last_crash_type--;
181         }
182     }
183 }
184 
finance_reset_history()185 void finance_reset_history()
186 {
187     for (int32_t i = 0; i < FINANCE_GRAPH_SIZE; i++)
188     {
189         gCashHistory[i] = MONEY64_UNDEFINED;
190         gWeeklyProfitHistory[i] = MONEY64_UNDEFINED;
191         gParkValueHistory[i] = MONEY64_UNDEFINED;
192     }
193 }
194 
195 /**
196  *
197  *  rct2: 0x0069DEFB
198  */
finance_init()199 void finance_init()
200 {
201     // It only initialises the first month
202     for (uint32_t i = 0; i < static_cast<int32_t>(ExpenditureType::Count); i++)
203     {
204         gExpenditureTable[0][i] = 0;
205     }
206 
207     gCurrentExpenditure = 0;
208     gCurrentProfit = 0;
209 
210     gWeeklyProfitAverageDividend = 0;
211     gWeeklyProfitAverageDivisor = 0;
212 
213     gInitialCash = MONEY(10000, 00); // Cheat detection
214 
215     gCash = MONEY(10000, 00);
216     gBankLoan = MONEY(10000, 00);
217     gMaxBankLoan = MONEY(20000, 00);
218 
219     gHistoricalProfit = 0;
220 
221     gBankLoanInterestRate = 10;
222     gParkValue = 0;
223     gCompanyValue = 0;
224     gScenarioCompletedCompanyValue = MONEY64_UNDEFINED;
225     gTotalAdmissions = 0;
226     gTotalIncomeFromAdmissions = 0;
227     gScenarioCompletedBy = "?";
228 }
229 
230 /**
231  *
232  *  rct2: 0x0069E79A
233  */
finance_update_daily_profit()234 void finance_update_daily_profit()
235 {
236     gCurrentProfit = 7 * gCurrentExpenditure;
237     gCurrentExpenditure = 0; // Reset daily expenditure
238 
239     money32 current_profit = 0;
240 
241     if (!(gParkFlags & PARK_FLAGS_NO_MONEY))
242     {
243         // Staff costs
244         for (auto peep : EntityList<Staff>())
245         {
246             current_profit -= GetStaffWage(peep->AssignedStaffType);
247         }
248 
249         // Research costs
250         uint8_t level = gResearchFundingLevel;
251         current_profit -= research_cost_table[level];
252 
253         // Loan costs
254         money32 current_loan = gBankLoan;
255         current_profit -= current_loan / 600;
256 
257         // Ride costs
258         for (auto& ride : GetRideManager())
259         {
260             if (ride.status != RideStatus::Closed && ride.upkeep_cost != MONEY16_UNDEFINED)
261             {
262                 current_profit -= 2 * ride.upkeep_cost;
263             }
264         }
265     }
266 
267     // This is not equivalent to / 4 due to rounding of negative numbers
268     current_profit = current_profit >> 2;
269 
270     gCurrentProfit += current_profit;
271 
272     // These are related to weekly profit graph
273     gWeeklyProfitAverageDividend += gCurrentProfit;
274     gWeeklyProfitAverageDivisor += 1;
275 
276     window_invalidate_by_class(WC_FINANCES);
277 }
278 
finance_get_initial_cash()279 money64 finance_get_initial_cash()
280 {
281     return gInitialCash;
282 }
283 
finance_get_current_loan()284 money64 finance_get_current_loan()
285 {
286     return gBankLoan;
287 }
288 
finance_get_maximum_loan()289 money64 finance_get_maximum_loan()
290 {
291     return gMaxBankLoan;
292 }
293 
finance_get_current_cash()294 money64 finance_get_current_cash()
295 {
296     return gCash;
297 }
298 
299 /**
300  * Shift the expenditure table history one month to the left
301  * If the table is full, accumulate the sum of the oldest month first
302  * rct2: 0x0069DEAD
303  */
finance_shift_expenditure_table()304 void finance_shift_expenditure_table()
305 {
306     // If EXPENDITURE_TABLE_MONTH_COUNT months have passed then is full, sum the oldest month
307     if (gDateMonthsElapsed >= EXPENDITURE_TABLE_MONTH_COUNT)
308     {
309         money64 sum = 0;
310         for (uint32_t i = 0; i < static_cast<int32_t>(ExpenditureType::Count); i++)
311         {
312             sum += gExpenditureTable[EXPENDITURE_TABLE_MONTH_COUNT - 1][i];
313         }
314         gHistoricalProfit += sum;
315     }
316 
317     // Shift the table
318     for (size_t i = EXPENDITURE_TABLE_MONTH_COUNT - 1; i >= 1; i--)
319     {
320         for (size_t j = 0; j < static_cast<int32_t>(ExpenditureType::Count); j++)
321         {
322             gExpenditureTable[i][j] = gExpenditureTable[i - 1][j];
323         }
324     }
325 
326     // Zero the beginning of the table, which is the new month
327     for (uint32_t i = 0; i < static_cast<int32_t>(ExpenditureType::Count); i++)
328     {
329         gExpenditureTable[0][i] = 0;
330     }
331 
332     window_invalidate_by_class(WC_FINANCES);
333 }
334 
335 /**
336  *
337  *  rct2: 0x0069E89B
338  */
finance_reset_cash_to_initial()339 void finance_reset_cash_to_initial()
340 {
341     gCash = gInitialCash;
342 }
343 
344 /**
345  * Gets the last month's profit from food, drink and merchandise.
346  */
finance_get_last_month_shop_profit()347 money64 finance_get_last_month_shop_profit()
348 {
349     money64 profit = 0;
350     if (gDateMonthsElapsed != 0)
351     {
352         const auto* lastMonthExpenditure = gExpenditureTable[1];
353 
354         profit += lastMonthExpenditure[static_cast<int32_t>(ExpenditureType::ShopSales)];
355         profit += lastMonthExpenditure[static_cast<int32_t>(ExpenditureType::ShopStock)];
356         profit += lastMonthExpenditure[static_cast<int32_t>(ExpenditureType::FoodDrinkSales)];
357         profit += lastMonthExpenditure[static_cast<int32_t>(ExpenditureType::FoodDrinkStock)];
358     }
359     return profit;
360 }
361