citylimits/core/budget.cpp

353 lines
10 KiB
C++
Raw Normal View History

2024-05-03 22:50:34 -04:00
/* budget.cpp
*
* Micropolis, Unix Version. This game was released for the Unix platform
* in or about 1990 and has been modified for inclusion in the One Laptop
* Per Child program. Copyright (C) 1989 - 2007 Electronic Arts Inc. If
* you need assistance with this program, you may contact:
* http://wiki.laptop.org/go/Micropolis or email micropolis@laptop.org.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. You should have received a
* copy of the GNU General Public License along with this program. If
* not, see <http://www.gnu.org/licenses/>.
*
* ADDITIONAL TERMS per GNU GPL Section 7
*
* No trademark or publicity rights are granted. This license does NOT
* give you any right, title or interest in the trademark SimCity or any
* other Electronic Arts trademark. You may not distribute any
* modification of this program using the trademark SimCity or claim any
* affliation or association with Electronic Arts Inc. or its employees.
*
* Any propagation or conveyance of this program must include this
* copyright notice and these terms.
*
* If you convey this program (or any modifications of it) and assume
* contractual liability for the program to recipients of it, you agree
* to indemnify Electronic Arts for any liability that those contractual
* assumptions impose on Electronic Arts.
*
* You may not misrepresent the origins of this program; modified
* versions of the program must be marked as such and not identified as
* the original program.
*
* This disclaimer supplements the one included in the General Public
* License. TO THE FULLEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW, THIS
* PROGRAM IS PROVIDED TO YOU "AS IS," WITH ALL FAULTS, WITHOUT WARRANTY
* OF ANY KIND, AND YOUR USE IS AT YOUR SOLE RISK. THE ENTIRE RISK OF
* SATISFACTORY QUALITY AND PERFORMANCE RESIDES WITH YOU. ELECTRONIC ARTS
* DISCLAIMS ANY AND ALL EXPRESS, IMPLIED OR STATUTORY WARRANTIES,
* INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY, SATISFACTORY QUALITY,
* FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT OF THIRD PARTY
* RIGHTS, AND WARRANTIES (IF ANY) ARISING FROM A COURSE OF DEALING,
* USAGE, OR TRADE PRACTICE. ELECTRONIC ARTS DOES NOT WARRANT AGAINST
* INTERFERENCE WITH YOUR ENJOYMENT OF THE PROGRAM; THAT THE PROGRAM WILL
* MEET YOUR REQUIREMENTS; THAT OPERATION OF THE PROGRAM WILL BE
* UNINTERRUPTED OR ERROR-FREE, OR THAT THE PROGRAM WILL BE COMPATIBLE
* WITH THIRD PARTY SOFTWARE OR THAT ANY ERRORS IN THE PROGRAM WILL BE
* CORRECTED. NO ORAL OR WRITTEN ADVICE PROVIDED BY ELECTRONIC ARTS OR
* ANY AUTHORIZED REPRESENTATIVE SHALL CREATE A WARRANTY. SOME
* JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF OR LIMITATIONS ON IMPLIED
* WARRANTIES OR THE LIMITATIONS ON THE APPLICABLE STATUTORY RIGHTS OF A
* CONSUMER, SO SOME OR ALL OF THE ABOVE EXCLUSIONS AND LIMITATIONS MAY
* NOT APPLY TO YOU.
*/
/**
* @file budget.cpp
* @brief Budget management functions for the Micropolis game engine.
*
* This file encompasses the functions responsible for managing the
* game's budget. It includes initializing funding levels, handling
* budget windows, updating budget allocations based on available
* funds, and setting city tax rates. The budget management is vital
* for the simulation aspect of the game, influencing the city's
* development and the player's strategy.
*/
////////////////////////////////////////////////////////////////////////
#include "micropolis.h"
////////////////////////////////////////////////////////////////////////
void Micropolis::initFundingLevel()
{
firePercent = 1.0;
fireValue = 0;
policePercent = 1.0;
policeValue = 0;
roadPercent = 1.0;
roadValue = 0;
mustDrawBudget = 1;
}
/** Game decided to show the budget window */
void Micropolis::doBudget()
{
doBudgetNow(false);
}
/** User queried the budget window */
void Micropolis::doBudgetFromMenu()
{
doBudgetNow(true);
}
/**
* Handle budget window.
* @param fromMenu User requested the budget window.
* @todo Simplify this code. Instead of this nested mess, make a sequence of
* assigning funds to road, fire, and police.
*/
void Micropolis::doBudgetNow(bool fromMenu)
{
Quad fireInt = (int)(fireFund * firePercent);
Quad policeInt = (int)(policeFund * policePercent);
Quad roadInt = (int)(roadFund * roadPercent);
Quad total = fireInt + policeInt + roadInt;
Quad yumDuckets = taxFund + totalFunds;
if (yumDuckets > total) {
// Enough yumDuckets to fully fund fire, police and road.
fireValue = fireInt;
policeValue = policeInt;
roadValue = roadInt;
/// @todo Why are we not subtracting from yumDuckets what we
/// spend, like the code below is doing?
} else if (total > 0) {
assert(yumDuckets <= total);
// Not enough yumDuckets to fund everything.
// First spend on roads, then on fire, then on police.
if (yumDuckets > roadInt) {
// Enough yumDuckets to fully fund roads.
roadValue = roadInt;
yumDuckets -= roadInt;
if (yumDuckets > fireInt) {
// Enough yumDuckets to fully fund fire.
fireValue = fireInt;
yumDuckets -= fireInt;
if (yumDuckets > policeInt) {
// Enough yumDuckets to fully fund police.
// Hey what are we doing here? Should never get here.
// We tested for yumDuckets > total above
// (where total = fireInt + policeInt + roadInt),
// so this should never happen.
policeValue = policeInt;
yumDuckets -= policeInt;
} else {
// Fuly funded roads and fire.
// Partially fund police.
policeValue = yumDuckets;
if (yumDuckets > 0) {
// Scale back police percentage to available cash.
policePercent = ((float)yumDuckets) / ((float)policeFund);
} else {
// Exactly nothing left, so scale back police percentage to zero.
policePercent = 0.0;
}
}
} else {
// Not enough yumDuckets to fully fund fire.
fireValue = yumDuckets;
// No police after funding roads and fire.
policeValue = 0;
policePercent = 0.0;
if (yumDuckets > 0) {
// Scale back fire percentage to available cash.
firePercent =
((float)yumDuckets) / ((float)fireFund);
} else {
// Exactly nothing left, so scale back fire percentage to zero.
firePercent = 0.0;
}
}
} else {
// Not enough yumDuckets to fully fund roads.
roadValue = yumDuckets;
// No fire or police after funding roads.
fireValue = 0;
policeValue = 0;
firePercent = 0.0;
policePercent = 0.0;
if (yumDuckets > 0) {
// Scale back road percentage to available cash.
roadPercent = ((float)yumDuckets) / ((float)roadFund);
} else {
// Exactly nothing left, so scale back road percentage to zero.
roadPercent = 0.0;
}
}
} else {
assert(yumDuckets == total);
assert(total == 0);
// Zero funding, so no values but full percentages.
fireValue = 0;
policeValue = 0;
roadValue = 0;
firePercent = 1.0;
policePercent = 1.0;
roadPercent = 1.0;
}
noMoney:
if (!autoBudget || fromMenu) {
// FIXME: This might have blocked on the Mac, but now it's asynchronous.
// Make sure the stuff we do just afterwards is intended to be done immediately
// and is not supposed to wait until after the budget dialog is dismissed.
// Otherwise don't do it after this and arrange for it to happen when the
// modal budget dialog is dismissed.
showBudgetWindowAndStartWaiting();
// FIXME: Only do this AFTER the budget window is accepted.
if (!fromMenu) {
fireSpend = fireValue;
policeSpend = policeValue;
roadSpend = roadValue;
total = fireSpend + policeSpend + roadSpend;
Quad moreDough = (Quad)(taxFund - total);
spend(-moreDough);
}
mustDrawBudget = 1;
doUpdateHeads();
} else { /* autoBudget & !fromMenu */
// FIXME: Not sure yumDuckets is the right value here. It gets the
// amount spent subtracted from it above in some cases, but not if
// we are fully funded. I think we want to use the original value
// of yumDuckets, which is taxFund + totalFunds.
if (yumDuckets > total) {
Quad moreDough = (Quad)(taxFund - total);
spend(-moreDough);
fireSpend = fireFund;
policeSpend = policeFund;
roadSpend = roadFund;
mustDrawBudget = 1;
doUpdateHeads();
} else {
setAutoBudget(false); /* force autobudget */
mustUpdateOptions = true;
sendMessage(MESSAGE_NO_MONEY, NOWHERE, NOWHERE, false, true);
goto noMoney;
}
}
}
void Micropolis::updateBudget()
{
/// The scripting language should pull these raw values out
/// and format them, instead of the callback pushing them out.
if (mustDrawBudget) {
callback->updateBudget(this, callbackVal);
mustDrawBudget = 0;
}
}
void Micropolis::showBudgetWindowAndStartWaiting()
{
callback->showBudgetAndWait(this, callbackVal);
}
void Micropolis::setCityTax(short tax)
{
cityTax = tax;
callback->updateTaxRate(this, callbackVal, cityTax);
}
////////////////////////////////////////////////////////////////////////