00001
00009 #include <handler.h>
00010 #include <drawing.h>
00011 #include <zakdef.h>
00012 #include <logging.h>
00013 #include <locking.h>
00014 #include <ui.h>
00015 #include <globals.h>
00016 #include <handler.h>
00017 #include <disaster.h>
00018 #include <simulation.h>
00019 #include <stack.h>
00020 #include <compilerpragmas.h>
00021 #include <mem_compat.h>
00022
00024 #define SHORT_BIT 1
00025 #define OUT_BIT 2
00026
00027 #define DONTPAINT (unsigned char)(1U<<7)
00028
00030 typedef struct _distrib {
00031 carryfn_t doescarry;
00033 problem_t error_flag;
00034 Int16 (*isplant)(welem_t, UInt32, selem_t);
00035 void *needSourceList;
00036 void *unvisitedNodes;
00037 selem_t flagToSet;
00038 Int16 SourceLeft;
00039 Int16 SourceTotal;
00040 Int16 NodesTotal;
00041 Int16 NodesSupplied;
00042 Int16 ShortOrOut;
00043 } distrib_t;
00044
00045 static void DoTaxes(void);
00046 static void DoUpkeep(void);
00047 static Int16 DistributeNumberOfSquaresAround(distrib_t *distrib, UInt32 pos);
00048
00049 static void reGradeZones(void);
00050 static void UpgradeZone(UInt32 pos);
00051 static void DowngradeZone(UInt32 pos);
00052 static Int16 DoTheRoadTrip(UInt32 startPos);
00053 static UInt32 DistributeMoveOn(UInt32 pos, dirType direction);
00054 static void DistributeUnvisited(distrib_t *distrib);
00055
00056 static long GetZoneScore(UInt32 pos);
00057 static Int16 GetScoreFor(zoneType iamthis, welem_t what);
00058 static Int32 GetRandomZone(void);
00059 static void FindZonesForUpgrading(void);
00060 static Int16 FindScoreForZones(void);
00061 static void AddNeighbors(distrib_t *distrib, UInt32 pos);
00062 static UInt8 ExistsNextto(UInt32 pos, UInt8 dirs, welem_t what);
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095 static void DoDistribute(Int16 grid);
00096
00101 static void
00102 Sim_Distribute_Specific(Int16 gridonly)
00103 {
00104 if (gridonly == 0) gridonly = GRID_ALL;
00105
00106 if ((gridonly & GRID_POWER)) {
00107 DoDistribute(GRID_POWER);
00108 ClearUpdate(GRID_POWER);
00109 }
00110 if ((gridonly & GRID_WATER)) {
00111 DoDistribute(GRID_WATER);
00112 ClearUpdate(GRID_WATER);
00113 }
00114 addGraphicUpdate(gu_playarea);
00115 addGraphicUpdate(gu_desires);
00116 }
00117
00125 static Int16
00126 IsItAPowerPlant(welem_t point, UInt32 coord __attribute__((unused)),
00127 selem_t flags __attribute__((unused)))
00128 {
00129 switch (point) {
00130 case Z_COALPLANT_START:
00131 return (SUPPLY_POWER_PLANT);
00132 case Z_NUCLEARPLANT_START:
00133 return (SUPPLY_NUCLEAR_PLANT);
00134 default:
00135 return (0);
00136 }
00137 }
00138
00149 static Int16
00150 IsItAWaterPump(welem_t point, UInt32 coord, selem_t flags)
00151 {
00152 if ((point == Z_PUMP) && (flags & POWEREDBIT) &&
00153 ExistsNextto(coord, DIR_ALL, Z_REALWATER))
00154 return (SUPPLY_WATER_PUMP);
00155 return (0);
00156 }
00157
00163 static void
00164 SetSupplied(distrib_t *distrib, UInt32 point)
00165 {
00166 distrib->NodesSupplied++;
00167 if (!(getWorldFlags(point) & distrib->flagToSet)) {
00168 orWorldFlags(point, distrib->flagToSet);
00169 }
00170 }
00171
00180 static Int16
00181 SupplyIfPlant(distrib_t *distrib, UInt32 pos, welem_t point, selem_t status)
00182 {
00183 Int16 pt;
00184 if (!(pt = distrib->isplant(point, pos, status)))
00185 return (0);
00186 if (getScratch(pos))
00187 return (0);
00188 SetSupplied(distrib, pos);
00189 setScratch(pos);
00190 distrib->NodesTotal++;
00191 distrib->SourceLeft += pt;
00192 distrib->SourceTotal += pt;
00193 if (!StackIsEmpty(distrib->needSourceList)) {
00194 while (distrib->SourceLeft &&
00195 !StackIsEmpty(distrib->needSourceList)) {
00196 pos = (UInt32)StackPop(distrib->needSourceList);
00197 distrib->SourceLeft--;
00198 SetSupplied(distrib, pos);
00199 }
00200 }
00201 return (pt);
00202 }
00203
00208 static void
00209 DoDistribute(Int16 grid)
00210 {
00211
00212 UInt32 i;
00213 welem_t gw;
00214 distrib_t *distrib = gMalloc(sizeof (distrib_t));
00215
00216 distrib->SourceLeft = 0;
00217 distrib->SourceTotal = 0;
00218 distrib->NodesTotal = 0;
00219 distrib->NodesSupplied = 0;
00220 distrib->ShortOrOut = 0;
00221 distrib->needSourceList = StackNew();
00222 distrib->unvisitedNodes = StackNew();
00223
00224
00225 if (grid == GRID_POWER) {
00226 distrib->isplant = &IsItAPowerPlant;
00227 distrib->doescarry = &CarryPower;
00228 distrib->flagToSet = POWEREDBIT;
00229 distrib->error_flag = peFineOnPower;
00230 } else {
00231 distrib->isplant = &IsItAWaterPump;
00232 distrib->doescarry = &CarryWater;
00233 distrib->flagToSet = WATEREDBIT;
00234 distrib->error_flag = peFineOnWater;
00235 }
00236
00237 LockZone(lz_world);
00238 LockZone(lz_flags);
00239 for (i = 0; i < MapMul(); i++) {
00240 if (distrib->doescarry(getWorld(i)))
00241 andWorldFlags(i,
00242 (selem_t)~(distrib->flagToSet |
00243 SCRATCHBIT | PAINTEDBIT));
00244 }
00245 for (i = 0; i < MapMul(); i++) {
00246 gw = getWorld(i);
00247 if (!getScratch(i)) {
00248 if (SupplyIfPlant(distrib, i, gw, getWorldFlags(i))) {
00249 AddNeighbors(distrib, i);
00250 DistributeUnvisited(distrib);
00251
00252 StackDoEmpty(distrib->needSourceList);
00253 WriteLog("Grid#%d Supplied Nodes: %d/%d "
00254 "SrcRemain: %d/%d\n", (int)grid,
00255 (int)distrib->NodesSupplied,
00256 (int)distrib->NodesTotal,
00257 (int)distrib->SourceLeft,
00258 (int)distrib->SourceTotal);
00259 if (distrib->SourceLeft < 25) {
00260 distrib->ShortOrOut |= SHORT_BIT;
00261 if (distrib->SourceLeft == 0)
00262 distrib->ShortOrOut |= OUT_BIT;
00263 }
00264 distrib->SourceLeft = 0;
00265 distrib->SourceTotal = 0;
00266 distrib->NodesSupplied = 0;
00267 distrib->NodesTotal = 0;
00268 }
00269 }
00270 }
00271 UnlockZone(lz_flags);
00272 UnlockZone(lz_world);
00273 StackDelete(distrib->needSourceList);
00274 StackDelete(distrib->unvisitedNodes);
00275 if (distrib->ShortOrOut & OUT_BIT) {
00276 UIProblemNotify(distrib->error_flag + 2);
00277 } else if (distrib->ShortOrOut & SHORT_BIT) {
00278 UIProblemNotify(distrib->error_flag + 1);
00279 } else {
00280 UIProblemNotify(distrib->error_flag);
00281 }
00282 gFree(distrib);
00283 }
00284
00288 static void
00289 DistributeUnvisited(distrib_t *distrib)
00290 {
00291 UInt32 pos;
00292 UInt8 flag;
00293
00294 while (!StackIsEmpty(distrib->unvisitedNodes)) {
00295 pos = (UInt32)StackPop(distrib->unvisitedNodes);
00296 flag = getWorldFlags(pos);
00297 if (SupplyIfPlant(distrib, pos, getWorld(pos), flag)) {
00298 goto nextneighbor;
00299 }
00300
00301 if (distrib->SourceLeft && ((flag & distrib->flagToSet) == 0)) {
00302
00303
00304
00305
00306 distrib->SourceLeft--;
00307 }
00308
00309
00310 if (distrib->SourceLeft <= 0)
00311 StackPush(distrib->needSourceList, (Int32)pos);
00312 else
00313 SetSupplied(distrib, pos);
00314
00315
00316 setScratch(pos);
00317
00318 nextneighbor:
00319
00320 AddNeighbors(distrib, pos);
00321 };
00322 }
00323
00329 static void
00330 AddNeighbors(distrib_t *distrib, UInt32 pos)
00331 {
00332 Int16 cross = DistributeNumberOfSquaresAround(distrib, pos);
00333
00334
00335 if ((cross & 0x0f) == 0)
00336 return;
00337
00338 distrib->NodesTotal += cross & 0x0f;
00339
00340 if ((cross & 0x10) == 0x10) {
00341 StackPush(distrib->unvisitedNodes,
00342 (Int32)(pos - getMapWidth()));
00343 }
00344 if ((cross & 0x20) == 0x20) {
00345 StackPush(distrib->unvisitedNodes,
00346 (Int32)(pos + 1));
00347 }
00348 if ((cross & 0x40) == 0x40) {
00349 StackPush(distrib->unvisitedNodes,
00350 (Int32)(pos + getMapWidth()));
00351 }
00352 if ((cross & 0x80) == 0x80) {
00353 StackPush(distrib->unvisitedNodes, (Int32)(pos - 1));
00354 }
00355 }
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365 static Int16
00366 Carries(Int16 (*doescarry)(welem_t), UInt32 pos)
00367 {
00368 if (getScratch(pos))
00369 return (0);
00370 return (doescarry(getWorld(pos)));
00371 }
00372
00387 static Int16
00388 DistributeNumberOfSquaresAround(distrib_t *distrib, UInt32 pos)
00389 {
00390 Int16 retval = 0;
00391 Int8 number = 0;
00392 Int16 (*carries)(welem_t) = distrib->doescarry;
00393
00394 if (Carries(carries, DistributeMoveOn(pos, dtUp))) {
00395 retval |= 0x10;
00396 number++;
00397 }
00398 if (Carries(carries, DistributeMoveOn(pos, dtRight))) {
00399 retval |= 0x20;
00400 number++;
00401 }
00402 if (Carries(carries, DistributeMoveOn(pos, dtDown))) {
00403 retval |= 0x40;
00404 number++;
00405 }
00406 if (Carries(carries, DistributeMoveOn(pos, dtLeft))) {
00407 retval |= 0x80;
00408 number++;
00409 }
00410
00411 retval |= number;
00412
00413 return (retval);
00414 }
00415
00426 static UInt32
00427 DistributeMoveOn(UInt32 pos, dirType direction)
00428 {
00429 switch (direction) {
00430 case dtUp:
00431 if (pos < getMapWidth())
00432 return (pos);
00433 pos -= getMapWidth();
00434 break;
00435 case dtRight:
00436 if (((pos%getMapWidth()) + 1) >= getMapWidth())
00437 return (pos);
00438 pos++;
00439 break;
00440 case dtDown:
00441 if ((pos+getMapWidth()) >= MapMul())
00442 return (pos);
00443 pos += getMapWidth();
00444 break;
00445 case dtLeft:
00446 if (pos % getMapWidth() == 0)
00447 return (pos);
00448 pos--;
00449 break;
00450 }
00451 return (pos);
00452 }
00453
00461 static UInt8
00462 ExistsNextto(UInt32 pos, UInt8 dirs, welem_t what)
00463 {
00464 UInt8 rv = 0;
00465
00466 if ((dirs & DIR_UP) && (pos > getMapWidth()) &&
00467 (what == getWorld(pos - getMapWidth())))
00468 rv |= DIR_UP;
00469 if ((dirs & DIR_DOWN) && (pos < (UInt32)(MapMul() - getMapWidth())) &&
00470 (what == getWorld(pos + getMapWidth())))
00471 rv |= DIR_DOWN;
00472 if ((dirs & DIR_LEFT) && (pos % getMapWidth()) &&
00473 (what == getWorld(pos - 1)))
00474 rv |= DIR_LEFT;
00475 if ((dirs & DIR_RIGHT) && (((pos % getMapWidth()) + 1) < getMapWidth())
00476 && (what == getWorld(pos + 1)))
00477 rv |= DIR_RIGHT;
00478 return (rv);
00479 }
00480
00481
00482
00483
00485 typedef struct {
00486 Int32 pos;
00487 Int32 score;
00488 } ZoneScore;
00489
00491 static ZoneScore *ran_zone;
00492
00494 static Int32 at_pos;
00495
00497 static UInt16 counter;
00498
00500 static UInt16 max_list;
00501
00508 static void
00509 FindZonesForUpgrading(void)
00510 {
00511 UInt16 i;
00512 Int32 randomZone;
00513
00514 if (ran_zone == NULL) {
00515 max_list = (getMapWidth() < getMapHeight() ? getMapHeight() :
00516 (UInt16)(getMapWidth())) * 3;
00517 ran_zone = (ZoneScore *)gCalloc(max_list, sizeof (ZoneScore));
00518 }
00519
00520 at_pos = 0;
00521
00522
00523 for (i = 0; i < max_list; i++) {
00524 ran_zone[at_pos].pos = -1;
00525 randomZone = GetRandomZone();
00526 if (randomZone != -1) {
00527 ran_zone[at_pos++].pos = randomZone;
00528 }
00529 }
00530 }
00531
00532
00542 Int16
00543 FindScoreForZones(void)
00544 {
00545 Int32 i;
00546 Int32 score;
00547
00548 counter += 20;
00549
00550 LockZone(lz_world);
00551 LockZone(lz_flags);
00552
00553 for (i = counter - 20; i < (Int16)counter; i++) {
00554 if (i >= at_pos) {
00555 counter = 0;
00556 UnlockZone(lz_world);
00557 return (0);
00558 }
00559 score = GetZoneScore((UInt32)ran_zone[i].pos);
00560 if (score != -1) {
00561 ran_zone[i].score = score;
00562 } else {
00563 ran_zone[i].score = -1;
00564 WriteLog("Instadowngrade (%d,%d)\n",
00565 (int)(ran_zone[i].pos % getMapWidth()),
00566 (int)(ran_zone[i].pos / getMapWidth()));
00567 DowngradeZone((UInt32)ran_zone[i].pos);
00568 ran_zone[i].pos = -1;
00569 }
00570 }
00571 UnlockZone(lz_flags);
00572 UnlockZone(lz_world);
00573 return (1);
00574 }
00575
00582 static Int16
00583 zoneCmpFn(void *a, void *b, Int32 other __attribute__((unused)))
00584 {
00585 ZoneScore *zs1 = (ZoneScore *)a;
00586 ZoneScore *zs2 = (ZoneScore *)b;
00587
00588 if (zs1->score > zs2->score)
00589 return (1);
00590 if (zs1->score < zs2->score)
00591 return (-1);
00592 return (0);
00593 }
00594
00601 void
00602 reGradeZones(void)
00603 {
00604 Int32 i;
00605 Int16 downCount = 10;
00606 Int16 upCount = 12;
00607
00608 QSort(ran_zone, at_pos, sizeof (ZoneScore), zoneCmpFn);
00609 WriteLog("Used: %ld\n", at_pos);
00610
00611 for (i = at_pos - 1; i >= 0 && i > (at_pos - upCount); i--) {
00612 if (ran_zone[i].pos == -1) continue;
00613
00614
00615 UpgradeZone((UInt32)ran_zone[i].pos);
00616 WriteLog("Upgrade (%d, %d)(%ld)\n",
00617 (int)(ran_zone[i].pos % getMapWidth()),
00618 (int)(ran_zone[i].pos / getMapWidth()),
00619 ran_zone[i].score);
00620 ran_zone[i].pos = -1;
00621 }
00622
00623
00624 for (i = 0; i < downCount && i < (at_pos - upCount); i++) {
00625
00626 if (ran_zone[i].pos == -1) continue;
00627 if (ran_zone[i].score > 0) continue;
00628 DowngradeZone((UInt32)ran_zone[i].pos);
00629 WriteLog("Downgrade (%d, %d)(%ld)\n",
00630 (int)(ran_zone[i].pos % getMapWidth()),
00631 (int)(ran_zone[i].pos / getMapWidth()),
00632 ran_zone[i].score);
00633 ran_zone[i].pos = -1;
00634 }
00635 at_pos = 0;
00636 }
00637
00642 static void
00643 DowngradeZone(UInt32 pos)
00644 {
00645 welem_t type;
00646 welem_t ntype;
00647 UInt16 xpos = (UInt16)(pos % getMapWidth());
00648 UInt16 ypos = (UInt16)(pos / getMapWidth());
00649
00650 LockZone(lz_world);
00651 LockZone(lz_flags);
00652 type = getWorld(pos);
00653 ntype = type;
00654 if (IsCommercial(type) && type != Z_COMMERCIAL_SLUM) {
00655 ntype = (type == Z_COMMERCIAL_MIN) ?
00656 Z_COMMERCIAL_SLUM : (welem_t)(type - 1);
00657 vgame.BuildCount[bc_value_commercial]--;
00658 if (ntype == Z_COMMERCIAL_SLUM)
00659 vgame.BuildCount[bc_count_commercial]--;
00660 } else if (IsResidential(type) && type != Z_RESIDENTIAL_SLUM) {
00661 ntype = (type == Z_RESIDENTIAL_MIN) ?
00662 Z_RESIDENTIAL_SLUM : (welem_t)(type - 1);
00663 vgame.BuildCount[bc_value_residential]--;
00664 if (ntype == Z_RESIDENTIAL_SLUM)
00665 vgame.BuildCount[bc_count_residential]--;
00666 } else if (IsIndustrial(type) && type != Z_INDUSTRIAL_SLUM) {
00667 ntype = (type == Z_INDUSTRIAL_MIN) ?
00668 Z_INDUSTRIAL_SLUM : (welem_t)(type - 1);
00669 vgame.BuildCount[bc_value_industrial]--;
00670 if (ntype == Z_INDUSTRIAL_SLUM)
00671 vgame.BuildCount[bc_count_industrial]--;
00672 }
00673 if (ntype != type) {
00674 setWorld(pos, ntype);
00675
00676 DrawFieldWithoutInit(xpos, ypos);
00677 }
00678
00679 UnlockZone(lz_flags);
00680 UnlockZone(lz_world);
00681 }
00682
00683
00684
00685
00686
00687 static void
00688 UpgradeZone(UInt32 pos)
00689 {
00690 welem_t type, ntype;
00691 UInt16 xpos = (UInt16)(pos % getMapWidth());
00692 UInt16 ypos = (UInt16)(pos / getMapWidth());
00693
00694 LockZone(lz_world);
00695 LockZone(lz_flags);
00696 type = getWorld(pos);
00697 ntype = type;
00698 if (IsCommercial(type) && type < Z_COMMERCIAL_MAX) {
00699 ntype = (type == Z_COMMERCIAL_SLUM) ?
00700 Z_COMMERCIAL_MIN : (welem_t)(type + 1);
00701 vgame.BuildCount[bc_value_commercial]++;
00702 if (type == Z_COMMERCIAL_SLUM)
00703 vgame.BuildCount[bc_count_commercial]++;
00704 } else if (IsResidential(type) && type < Z_RESIDENTIAL_MAX) {
00705 ntype = (type == Z_RESIDENTIAL_SLUM) ?
00706 Z_RESIDENTIAL_MIN : (welem_t)(type + 1);
00707 vgame.BuildCount[bc_value_residential]++;
00708 if (type == Z_RESIDENTIAL_SLUM)
00709 vgame.BuildCount[bc_count_residential]++;
00710 } else if (IsIndustrial(type) && type < Z_INDUSTRIAL_MAX) {
00711 ntype = (type == Z_INDUSTRIAL_SLUM) ?
00712 Z_INDUSTRIAL_MIN : (welem_t)(type + 1);
00713 vgame.BuildCount[bc_value_industrial]++;
00714 if (type == Z_RESIDENTIAL_SLUM)
00715 vgame.BuildCount[bc_count_industrial]++;
00716 }
00717 if (ntype != type) {
00718 setWorld(pos, ntype);
00719
00720 DrawFieldWithoutInit(xpos, ypos);
00721 }
00722
00723 UnlockZone(lz_flags);
00724 UnlockZone(lz_world);
00725 }
00726
00731 static Int16
00732 DoTheRoadTrip(UInt32 startPos __attribute__((unused)))
00733 {
00734 return (1);
00735 }
00736
00744 long
00745 GetZoneScore(UInt32 pos)
00746 {
00747 long score = -1;
00748 Int16 x = (Int16)(pos % getMapWidth());
00749 Int16 y = (Int16)(pos / getMapWidth());
00750 int ax, ay;
00751 int maxx, maxy;
00752 int bRoad = 0;
00753 zoneType type = ztWhat;
00754 UInt8 zone;
00755
00756 LockZone(lz_world);
00757 LockZone(lz_flags);
00758 zone = getWorld(pos);
00759 type = (IsZone(zone, ztCommercial) ? ztCommercial :
00760 (IsZone(zone, ztResidential) ? ztResidential : ztIndustrial));
00761
00762 if (((getWorldFlags(pos) & POWEREDBIT) == 0) ||
00763 ((getWorldFlags(pos) & WATEREDBIT) == 0)) {
00764
00765 WriteLog("No Power || Water\n");
00766 goto unlock_ret;
00767 }
00768
00769 if (IsSlum(zone)) {
00770 score = 50;
00771 goto unlock_ret;
00772 }
00773
00774 if ((type == ztIndustrial) || (type == ztCommercial)) {
00775
00776
00777
00778
00779
00780 Int32 availPop = (Int32)((vgame.BuildCount[bc_value_residential]*25)
00781 - (vgame.BuildCount[bc_value_commercial]*25
00782 + vgame.BuildCount[bc_value_industrial]*25));
00783
00784 if (availPop <= 0) {
00785 WriteLog("Pop too low to promote ind || comm\n");
00786 goto unlock_ret;
00787 }
00788 } else if (type == ztResidential) {
00789
00790
00791
00792
00793
00794
00795
00796 Int32 availPop = (Int32)(((getMonthsElapsed() * getMonthsElapsed()) /
00797 35) + 30 - vgame.BuildCount[bc_value_residential]);
00798
00799 if (availPop <= 0) {
00800 WriteLog("No People\n");
00801 goto unlock_ret;
00802 }
00803 }
00804
00805 if (type == ztCommercial) {
00806
00807
00808
00809
00810
00811 Int32 availGoods =(Int32)((vgame.BuildCount[bc_value_industrial] /
00812 3 * 2) - (vgame.BuildCount[bc_value_commercial]));
00813
00814 if (availGoods <= 0) {
00815 WriteLog("No Goods\n");
00816 goto unlock_ret;
00817 }
00818 }
00819
00820
00821 maxx = (4 + x < getMapWidth()) ? 4 + x : getMapWidth() - 1;
00822 maxy = (4 + y < getMapHeight()) ? 4 + y : getMapHeight() - 1;
00823 ax = (x - 3 > 0) ? x - 3 : 0;
00824 while (ax < maxx) {
00825 ay = (y - 3 > 0) ? y - 3 : 0;
00826 while (ay < maxy) {
00827 score += GetScoreFor(type, getWorld(WORLDPOS(ax, ay)));
00828 if (IsRoad(getWorld(WORLDPOS(ax, ay))) && bRoad == 0) {
00829
00830
00831
00832
00833 bRoad = DoTheRoadTrip(WORLDPOS(ax, ay));
00834 if (!bRoad) {
00835 score = -1;
00836 goto unlock_ret;
00837 }
00838 }
00839 ay++;
00840 }
00841 ax++;
00842 }
00843
00844 unlock_ret:
00845 UnlockZone(lz_flags);
00846 UnlockZone(lz_world);
00847 return (score);
00848 }
00849
00856 Int16
00857 GetScoreFor(zoneType iamthis, welem_t what)
00858 {
00859 if (IsZone(what, ztCommercial)) {
00860 return (iamthis == ztCommercial) ? 1 :
00861 ((iamthis == ztResidential) ? 50 :
00862 ((iamthis == ztIndustrial) ? 50 : 50));
00863 }
00864 if (IsZone(what, ztResidential)) {
00865 return (iamthis == ztCommercial) ? 50 :
00866 ((iamthis == ztResidential) ? 1 :
00867 ((iamthis == ztIndustrial) ? 50 : 50));
00868 }
00869 if (IsZone(what, ztIndustrial)) {
00870 return (iamthis == ztCommercial) ? (-25) :
00871 ((iamthis == ztResidential) ? (-75) :
00872 ((iamthis == ztIndustrial) ? 1 : (-50)));
00873 }
00874 if (IsRoad(what)) {
00875 return (iamthis == ztCommercial) ? 75 :
00876 ((iamthis == ztResidential) ? 50 :
00877 ((iamthis == ztIndustrial) ? 75 : 66));
00878 }
00879 if (IsCoalPlant(what)) {
00880 return (iamthis == ztCommercial) ? (-75) :
00881 ((iamthis == ztResidential) ? (-100) :
00882 ((iamthis == ztIndustrial) ? 30 : (-75)));
00883 }
00884 if (IsNukePlant(what)) {
00885 return (iamthis == ztCommercial) ? (-150) :
00886 ((iamthis == ztResidential) ? (-200) :
00887 ((iamthis == ztIndustrial) ? 15 : (-175)));
00888 }
00889 if (IsRealTree(what)) {
00890 return (iamthis == ztCommercial) ? 50 :
00891 ((iamthis == ztResidential) ? 85 :
00892 ((iamthis == ztIndustrial)? 25 : 50));
00893 }
00894 if (IsFakeTree(what)) {
00895 return (iamthis == ztCommercial) ? 25 :
00896 ((iamthis == ztResidential) ? 42 :
00897 ((iamthis == ztIndustrial)? 12 : 25));
00898 }
00899 if (IsRealWater(what)) {
00900 return (iamthis == ztCommercial) ? 175 :
00901 ((iamthis == ztResidential) ? 550 :
00902 ((iamthis == ztIndustrial) ? 95 : 250));
00903 }
00904 if (IsFakeWater(what)) {
00905 return (iamthis == ztCommercial) ? 80 :
00906 ((iamthis == ztResidential) ? 80 :
00907 ((iamthis == ztIndustrial) ? 45 : 25));
00908 }
00909 return (0);
00910 }
00911
00918 static Int32
00919 GetRandomZone(void)
00920 {
00921 UInt32 pos = 0;
00922 UInt16 i;
00923 UInt8 type;
00924
00925 LockZone(lz_world);
00926 LockZone(lz_flags);
00927 for (i = 0; i < 5; i++) {
00928 pos = GetRandomNumber(MapMul());
00929 type = getWorld(pos);
00930 if (IsGrowable(type)) {
00931 UnlockZone(lz_world);
00932 return ((Int32)pos);
00933 }
00934 }
00935
00936 UnlockZone(lz_flags);
00937 UnlockZone(lz_world);
00938 return (-1);
00939 }
00940
00942 static const struct countCosts {
00943 BuildCount count;
00944 UInt32 cost;
00945 } countCosts[] = {
00946 { bc_value_residential, INCOME_RESIDENTIAL },
00947 { bc_value_commercial, INCOME_COMMERCIAL },
00948 { bc_value_industrial, INCOME_INDUSTRIAL },
00949 { bc_count_roads, UPKEEP_ROAD },
00950 { bc_count_trees, 0 },
00951 { bc_water, 0 },
00952 { bc_powerlines, UPKEEP_POWERLINE },
00953 { bc_coalplants, UPKEEP_POWERPLANT },
00954 { bc_nuclearplants, UPKEEP_NUCLEARPLANT },
00955 { bc_waste, 0 },
00956 { bc_fire, 0 },
00957 { bc_fire_stations, UPKEEP_FIRE_STATIONS },
00958 { bc_police_departments, UPKEEP_POLICE_STATIONS },
00959 { bc_military_bases, UPKEEP_MILITARY_BASES },
00960 { bc_waterpipes, 0 },
00961 { bc_waterpumps, 0 }
00962 };
00963
00964 #define CCSIZE (sizeof (countCosts) / sizeof (countCosts[0]))
00965
00971 static Int32
00972 costIt(BuildCode item)
00973 {
00974 UInt16 i;
00975 for (i = 0; i < CCSIZE; i++)
00976 if (countCosts[i].count == item)
00977 return ((Int32)(vgame.BuildCount[item] * countCosts[i].cost));
00978 WriteLog("Fell off the end of costIt here (%d)\n", item);
00979 return (0);
00980 }
00981
00982 Int32
00983 BudgetGetNumber(BudgetNumber type)
00984 {
00985 Int32 ret = 0;
00986 switch (type) {
00987 case bnResidential:
00988 ret = (Int32)costIt(bc_value_residential) * getTax() / 100;
00989 break;
00990 case bnCommercial:
00991 ret = (Int32)costIt(bc_value_commercial) * getTax() / 100;
00992 break;
00993 case bnIndustrial:
00994 ret = (Int32)costIt(bc_value_industrial) * getTax() / 100;
00995 break;
00996 case bnIncome:
00997 ret = ((costIt(bc_value_residential) +
00998 costIt(bc_value_commercial) +
00999 costIt(bc_value_industrial)) * getTax()) / 100;
01000 break;
01001 case bnTraffic:
01002 ret = (Int32)(costIt(bc_count_roads) *
01003 getUpkeep(ue_traffic)) / 100;
01004 break;
01005 case bnPower:
01006 ret = (Int32)((costIt(bc_powerlines) +
01007 costIt(bc_nuclearplants) + costIt(bc_coalplants)) *
01008 getUpkeep(ue_power)) / 100;
01009 break;
01010 case bnDefence:
01011 ret = (Int32)((costIt(bc_fire_stations) +
01012 costIt(bc_police_departments) +
01013 costIt(bc_military_bases)) *
01014 getUpkeep(ue_defense)) / 100;
01015 break;
01016 case bnCurrentBalance:
01017 ret = getCredits();
01018 break;
01019 case bnChange:
01020 ret = (Int32)BudgetGetNumber(bnIncome)
01021 - BudgetGetNumber(bnTraffic)
01022 - BudgetGetNumber(bnPower)
01023 - BudgetGetNumber(bnDefence);
01024 break;
01025 case bnNextMonth:
01026 ret = (Int32)BudgetGetNumber(bnCurrentBalance) +
01027 BudgetGetNumber(bnChange);
01028 break;
01029 }
01030 return (ret);
01031 }
01032
01036 void
01037 DoTaxes(void)
01038 {
01039 incCredits(BudgetGetNumber(bnIncome));
01040 }
01041
01045 void
01046 DoUpkeep(void)
01047 {
01048 UInt32 upkeep;
01049
01050 upkeep = (UInt32)(BudgetGetNumber(bnTraffic) +
01051 BudgetGetNumber(bnPower) + BudgetGetNumber(bnDefence));
01052
01053 WriteLog("Upkeep: %lu\n", (unsigned long)upkeep);
01054
01055 DoCommitmentNasties();
01056 if (upkeep <= (UInt32)getCredits()) {
01057 decCredits((Int32)upkeep);
01058 return;
01059 }
01060 WriteLog("*** Negative Cashflow\n");
01061 setCredits(0);
01062
01063
01064 DoNastyStuffTo(Z_ROAD, 1, 1);
01065 DoNastyStuffTo(Z_POWERLINE, 5, 1);
01066 DoNastyStuffTo(Z_COALPLANT, 15, 0);
01067 DoNastyStuffTo(Z_NUCLEARPLANT, 50, 0);
01068 DoNastyStuffTo(Z_FIRESTATION, 10, 1);
01069 DoNastyStuffTo(Z_POLICEDEPT, 12, 1);
01070 DoNastyStuffTo(Z_ARMYBASE, 35, 0);
01071 }
01072
01076 Int16
01077 Sim_DoPhase(Int16 nPhase)
01078 {
01079 switch (nPhase) {
01080 case 1:
01081 if (NeedsUpdate(GRID_POWER)) {
01082 WriteLog("Simulation phase 1 - power grid\n");
01083 Sim_Distribute_Specific(GRID_POWER);
01084 ClearUpdate(GRID_POWER);
01085 }
01086 nPhase = 2;
01087 break;
01088 case 2:
01089 if (NeedsUpdate(GRID_WATER)) {
01090 WriteLog("Simulation phase 2 - water grid\n");
01091 Sim_Distribute_Specific(GRID_WATER);
01092 ClearUpdate(GRID_WATER);
01093 }
01094 nPhase = 3;
01095 break;
01096 case 3:
01097 WriteLog("Simulation phase 3 - Find zones for upgrading\n");
01098 FindZonesForUpgrading();
01099 nPhase = 4;
01100
01101 WriteLog("Simulation phase 4 - Find score for zones\n");
01102 break;
01103 case 4:
01104 if (FindScoreForZones() == 0)
01105 nPhase = 5;
01106 break;
01107 case 5:
01108 WriteLog("Simulation phase 5 - Regrade Zones\n");
01109 reGradeZones();
01110 addGraphicUpdate(gu_desires);
01111 nPhase = 6;
01112 break;
01113 case 6:
01114 WriteLog("Simulation phase 6 - Update disasters\n");
01115
01116 DoRandomDisaster();
01117 addGraphicUpdate(gu_desires);
01118 nPhase = 7;
01119 break;
01120 case 7:
01121 WriteLog("Simulation phase 7 - Economics\n");
01122 DoTaxes();
01123 incrementTimeElapsed(4);
01124 nPhase = 0;
01125 addGraphicUpdate(gu_credits);
01126 addGraphicUpdate(gu_population);
01127 addGraphicUpdate(gu_desires);
01128 UICheckMoney();
01129 DoUpkeep();
01130 break;
01131 }
01132
01133 return (nPhase);
01134 }
01135
01136 void
01137 UpdateVolatiles(void)
01138 {
01139 UInt32 p;
01140
01141 LockZone(lz_world);
01142 LockZone(lz_flags);
01143
01144 for (p = 0; p < MapMul(); p++) {
01145 UInt8 elt = getWorld(p);
01146
01147
01148 if (IsCommercial(elt)) {
01149 vgame.BuildCount[bc_count_commercial]++;
01150 vgame.BuildCount[bc_value_commercial] += ZoneValue(elt);
01151 }
01152 if (IsResidential(elt)) {
01153 vgame.BuildCount[bc_count_residential]++;
01154 vgame.BuildCount[bc_value_residential] +=
01155 ZoneValue(elt);
01156 }
01157 if (IsIndustrial(elt)) {
01158 vgame.BuildCount[bc_count_industrial]++;
01159 vgame.BuildCount[bc_value_industrial] += ZoneValue(elt);
01160 }
01161 if (IsRoad(elt)) {
01162 vgame.BuildCount[bc_count_roads]++;
01163 vgame.BuildCount[bc_value_roads] += ZoneValue(elt);
01164 }
01165 if (IsFakeTree(elt)) {
01166 vgame.BuildCount[bc_count_trees]++;
01167 }
01168 if (IsFakeWater(elt)) {
01169 vgame.BuildCount[bc_water]++;
01170 }
01171 if (IsRoadPower(elt)) {
01172 vgame.BuildCount[bc_powerlines]++;
01173 vgame.BuildCount[bc_count_roads]++;
01174 vgame.BuildCount[bc_value_roads] += ZoneValue(elt);
01175 }
01176 if (IsPowerLine(elt))
01177 vgame.BuildCount[bc_powerlines]++;
01178 if (elt == Z_COALPLANT)
01179 vgame.BuildCount[bc_coalplants]++;
01180 if (elt == Z_NUCLEARPLANT)
01181 vgame.BuildCount[bc_nuclearplants]++;
01182 if (elt == Z_WASTE) vgame.BuildCount[bc_waste]++;
01183 if (elt >= Z_FIRE1 && elt <= Z_FIRE3)
01184 vgame.BuildCount[bc_fire]++;
01185 if (elt == Z_FIRESTATION)
01186 vgame.BuildCount[bc_fire_stations]++;
01187 if (elt == Z_POLICEDEPT)
01188 vgame.BuildCount[bc_police_departments]++;
01189 if (elt == Z_ARMYBASE)
01190 vgame.BuildCount[bc_military_bases]++;
01191 if (IsWaterPipe(elt))
01192 vgame.BuildCount[bc_waterpipes]++;
01193 if (IsRoadPipe(elt)) {
01194 vgame.BuildCount[bc_waterpipes]++;
01195 vgame.BuildCount[bc_count_roads]++;
01196 vgame.BuildCount[bc_value_roads] += ZoneValue(elt);
01197 }
01198 if (IsPowerWater(elt)) {
01199 vgame.BuildCount[bc_waterpipes]++;
01200 vgame.BuildCount[bc_powerlines]++;
01201 }
01202 if (IsPump(elt))
01203 vgame.BuildCount[bc_waterpumps]++;
01204 }
01205 UnlockZone(lz_flags);
01206 UnlockZone(lz_world);
01207 }
01208
01220 static UInt16
01221 ShuffleIndividualStatistic(UInt16 *ary, UInt16 load)
01222 {
01223 int atItem = STAT_ENTRIES - 1;
01224 UInt16 newvalue;
01225
01226 newvalue = ary[atItem];
01227 for (atItem = STAT_ENTRIES - 1; atItem > 0; atItem--) {
01228 ary[atItem] = ary[atItem-1];
01229 }
01230 ary[0] = load;
01231 return (newvalue);
01232 }
01233
01234 void
01235 UpdateCounters(void)
01236 {
01237
01238 if (vgame.prior_credit == 0) {
01239 vgame.prior_credit = (UInt32)getCredits();
01240 }
01241
01242
01243
01244
01245
01246 vgame.BuildCount[bc_cashflow] = (Int16)((OFFSET_FOR_CASHFLOW_BC -
01247 vgame.prior_credit) + getCredits());
01248
01249 vgame.prior_credit = (UInt32)getCredits();
01266 }
01267
01273 void
01274 RecordStatistics(void)
01275 {
01276 StatisticItem item;
01277 stat_item *stat;
01278 BuildCount offset;
01279 UInt16 stat_value;
01280 UInt32 tmpval;
01281
01282 for (item = st_cashflow; item < st_tail; item++) {
01283 offset = statvalues[item].offset;
01284 stat_value = (UInt16)vgame.BuildCount[offset];
01285 stat = getStatistics(offset);
01286
01287 tmpval = (UInt32)stat->last_ten[0] * 3 + stat_value;
01288 tmpval >>= 2;
01289
01290 if (tmpval > (UInt16)MAX_UINT16) {
01291 stat->last_ten[0] = MAX_UINT16;
01292 } else {
01293 stat->last_ten[0] = (UInt16)tmpval;
01294 }
01295
01296
01297 if ((getMonthsElapsed() & 3) == 3) {
01298 (void)ShuffleIndividualStatistic(
01299 &stat->last_ten[0], stat_value);
01300 }
01301 if ((getMonthsElapsed() & (3*12)) == (3 * 12)) {
01302 }
01303 }
01304 }
01305
01306 UInt32
01307 getPopulation(void)
01308 {
01309 return ((vgame.BuildCount[bc_value_residential] +
01310 (vgame.BuildCount[bc_value_commercial] * 8) +
01311 (vgame.BuildCount[bc_value_industrial] * 8)) * 20);
01312 }
01313
01317 Int16
01318 CarryPower(welem_t x)
01319 {
01320 return
01321
01322
01323
01324
01325
01326 ((IsPump(x)) ||
01327 ((x >= Z_POWERLINE) && (x <= Z_POWERROAD_PVER)) ||
01328 (IsRailPower(x)) ? 1 : 0);
01329 }
01330
01338 Int16
01339 CarryWater(welem_t x)
01340 {
01341 return (IsPump(x) || IsWaterPipe(x) ||
01342 ((x >= Z_POWERWATER_START) && (x <= Z_INDUSTRIAL_MAX)) ||
01343 IsRoadPipe(x) || IsRailPipe(x) ? 1 : 0);
01344 }
01345
01346 Int16
01347 IsPowerLine(welem_t x)
01348 {
01349 return ((((x >= Z_POWERLINE_START) && (x <= Z_POWERLINE_END))) ? 1 : 0);
01350 }
01351
01352 Int16
01353 IsRoad(welem_t x)
01354 {
01355 return (((x >= Z_ROAD_START) && (x <= Z_ROAD_END)) ? 1 : 0);
01356 }
01357
01358 Int16
01359 IsRoadBridge(welem_t x)
01360 {
01361 return (((x >= Z_BRIDGE_START) && (x <= Z_BRIDGE_END)) ? 1 : 0);
01362 }
01363
01364 Int16
01365 IsWaterPipe(welem_t x)
01366 {
01367 return (((x >= Z_PIPE_START) && (x <= Z_PIPE_END)) ? 1 : 0);
01368 }
01369
01370 Int16
01371 ZoneValue(welem_t x)
01372 {
01373 if ((x >= Z_COMMERCIAL_SLUM) && (x <= Z_INDUSTRIAL_SLUM))
01374 return (0);
01375 if ((x >= Z_COMMERCIAL_MIN) && (x <= Z_INDUSTRIAL_MAX)) {
01376 return (1 + ((x - Z_COMMERCIAL_MIN) % 10));
01377 }
01378 if (IsRoad(x))
01379 return ((x - Z_ROAD_START) + 1);
01380 if (IsRail(x))
01381 return ((x - Z_RAIL_START) + 1);
01382 else
01383 return (0);
01384 }
01385
01386 Int16
01387 IsRoadPipe(welem_t x)
01388 {
01389 return (((x >= Z_PIPEROAD_START) && (x <= Z_PIPEROAD_END)) ? 1 : 0);
01390 }
01391
01392 Int16
01393 IsRoadPower(welem_t x)
01394 {
01395 return (((x >= Z_POWERROAD_START) && (x <= Z_POWERROAD_END)) ? 1 : 0);
01396 }
01397
01398 Int16
01399 IsPowerWater(welem_t x)
01400 {
01401 return (((x >= Z_POWERWATER_START) && (x <= Z_POWERWATER_END)) ? 1 : 0);
01402 }
01403
01404 Int16
01405 IsZone(welem_t x, zoneType nType)
01406 {
01407
01408 if (x == nType)
01409 return (1);
01410 nType -= Z_COMMERCIAL_SLUM;
01411 if ((x >= (welem_t)(nType * 10 + Z_COMMERCIAL_MIN)) &&
01412 (x <= (welem_t)(nType * 10 + Z_COMMERCIAL_MAX)))
01413 return (1);
01414 return (0);
01415 }
01416
01417 Int16
01418 IsTransport(welem_t x)
01419 {
01420 return (IsRoad(x) || IsRoadPipe(x) || IsRoadPower(x) ||
01421 IsRoadBridge(x) ||
01422 IsRail(x) || IsRailPipe(x) || IsRailPower(x) || IsRailTunnel(x) ||
01423 IsRailOvRoad(x));
01424 }
01425
01426 Int16
01427 IsRoadOrBridge(welem_t x)
01428 {
01429 return (IsRoad(x) || IsRoadBridge(x));
01430 }
01431
01432 Int16
01433 IsRail(welem_t x)
01434 {
01435 return (((x >= Z_RAIL_START) && (x <= Z_RAIL_END)) ? 1 : 0);
01436 }
01437
01438 Int16
01439 IsRailPower(welem_t x)
01440 {
01441 return (((x >= Z_RAILPOWER_START) && (x <= Z_RAILPOWER_END)) ? 1 : 0);
01442 }
01443
01444 Int16
01445 IsRailPipe(welem_t x)
01446 {
01447 return (((x >= Z_RAILPIPE_START) && (x <= Z_RAILPIPE_END)) ? 1 : 0);
01448 }
01449
01450 Int16
01451 IsRailTunnel(welem_t x)
01452 {
01453 return (((x >= Z_RAILTUNNEL_START) && (x <= Z_RAILTUNNEL_END)) ? 1 : 0);
01454 }
01455
01456 Int16
01457 IsRailOrTunnel(welem_t x)
01458 {
01459 return (IsRail(x) || IsRailTunnel(x));
01460 }
01461
01462 Int16
01463 IsRailOvRoad(welem_t x)
01464 {
01465 return (((x >= Z_RAILOVROAD_START) && (x <= Z_RAILOVROAD_END)) ? 1 : 0);
01466 }
01467
01468 Int16
01469 IsOccupied(welem_t x)
01470 {
01471 return (!((x <= Z_REALWATER) || (x > Z_ENDMARKER)));
01472 }
01473
01474 UInt8
01475 CheckNextTo(UInt32 pos, UInt8 dirs, Int16 (*checkfn)(welem_t))
01476 {
01477 UInt8 rv = 0;
01478
01479 if ((dirs & DIR_UP) && (pos > getMapWidth()) &&
01480 checkfn(getWorld((UInt32)(pos - getMapWidth()))))
01481 rv |= DIR_UP;
01482 if ((dirs & DIR_DOWN) && ((Int32)pos < (MapMul() - getMapWidth())) &&
01483 checkfn(getWorld((UInt32)(pos + getMapWidth()))))
01484 rv |= DIR_DOWN;
01485 if ((dirs & DIR_LEFT) && (pos % getMapWidth()) &&
01486 checkfn(getWorld((UInt32)(pos - 1))))
01487 rv |= DIR_LEFT;
01488 if ((dirs & DIR_RIGHT) && (((pos % getMapWidth()) + 1) < getMapWidth())
01489 && checkfn(getWorld((UInt32)(pos + 1))))
01490 rv |= DIR_RIGHT;
01491 return (rv);
01492 }
01493
01494 UInt8
01495 CheckNextTo1(UInt32 pos, UInt8 dirs, carryfnarg_t checkfn, void *cfarg)
01496 {
01497 UInt8 rv = 0;
01498
01499 if ((dirs & DIR_UP) && (pos > getMapWidth()) &&
01500 checkfn(getWorld((UInt32)(pos - getMapWidth())), cfarg))
01501 rv |= DIR_UP;
01502 if ((dirs & DIR_DOWN) && ((Int32)pos < (MapMul() - getMapWidth())) &&
01503 checkfn(getWorld((UInt32)(pos + getMapWidth())), cfarg))
01504 rv |= DIR_DOWN;
01505 if ((dirs & DIR_LEFT) && (pos % getMapWidth()) &&
01506 checkfn(getWorld((UInt32)(pos - 1)), cfarg))
01507 rv |= DIR_LEFT;
01508 if ((dirs & DIR_RIGHT) && (((pos % getMapWidth()) + 1) < getMapWidth())
01509 && checkfn(getWorld((UInt32)(pos + 1)), cfarg))
01510 rv |= DIR_RIGHT;
01511 return (rv);
01512 }
01513
01514 void
01515 endSimulation(void)
01516 {
01517 if (ran_zone != NULL) {
01518 gFree(ran_zone);
01519 ran_zone = NULL;
01520 }
01521 }