Importing source code for MicropolisJ, the Java rewrite of Micropolis.
This edition of Micropolis, written for the Java desktop platform, is fairly feature complete. I believe the only missing functionality is that of loading the built-in scenarios, and this can be implemented if there is any demand for it. I will soon update the home page at http://code.google.com/p/micropolis/ with downloadable packages of this edition of the software. git-svn-id: https://micropolis.googlecode.com/svn/trunk/micropolis-java@528 d9718cc8-9f43-0410-858b-315f434eb58c
This commit is contained in:
parent
99e2b7dd01
commit
ed6795dfca
189 changed files with 13184 additions and 0 deletions
87
src/micropolisj/engine/AirplaneSprite.java
Normal file
87
src/micropolisj/engine/AirplaneSprite.java
Normal file
|
@ -0,0 +1,87 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
public class AirplaneSprite extends Sprite
|
||||
{
|
||||
int destX;
|
||||
int destY;
|
||||
|
||||
// Note: frames 1-8 used for regular movement
|
||||
// 9-11 used for Taking off
|
||||
static int [] CDx = { 0, 0, 6, 8, 6, 0, -6, -8, -6, 8, 8, 8 };
|
||||
static int [] CDy = { 0, -8, -6, 0, 6, 8, 6, 0, -6, 0, 0, 0 };
|
||||
|
||||
public AirplaneSprite(Micropolis engine, int xpos, int ypos)
|
||||
{
|
||||
super(engine, SpriteKind.AIR);
|
||||
this.x = xpos * 16 + 8;
|
||||
this.y = ypos * 16 + 8;
|
||||
this.width = 48;
|
||||
this.height = 48;
|
||||
this.offx = -24;
|
||||
this.offy = -24;
|
||||
|
||||
this.destY = this.y;
|
||||
if (xpos > engine.getWidth()-20) {
|
||||
// not enough room to east of airport for taking off
|
||||
this.destX = x - 200;
|
||||
this.frame = 7;
|
||||
}
|
||||
else {
|
||||
this.destX = x + 200;
|
||||
this.frame = 11;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveImpl()
|
||||
{
|
||||
int z = this.frame;
|
||||
|
||||
if (city.acycle % 5 == 0) {
|
||||
if (z > 8) { //plane is still taking off
|
||||
z--;
|
||||
if (z < 9) { z = 3; }
|
||||
this.frame = z;
|
||||
}
|
||||
else { // go to destination
|
||||
int d = getDir(x, y, destX, destY);
|
||||
z = turnTo(z, d);
|
||||
this.frame = z;
|
||||
}
|
||||
}
|
||||
|
||||
if (getDis(x, y, destX, destY) < 50) { // at destination
|
||||
//FIXME- original code allows destination to be off-the-map
|
||||
destX = city.PRNG.nextInt(city.getWidth()) * 16 + 8;
|
||||
destY = city.PRNG.nextInt(city.getHeight()) * 16 + 8;
|
||||
}
|
||||
|
||||
if (!city.noDisasters) {
|
||||
boolean explode = false;
|
||||
|
||||
for (Sprite s : city.allSprites()) {
|
||||
if (s != this &&
|
||||
(s.kind == SpriteKind.AIR || s.kind == SpriteKind.COP) &&
|
||||
checkSpriteCollision(s))
|
||||
{
|
||||
s.explodeSprite();
|
||||
explode = true;
|
||||
}
|
||||
}
|
||||
if (explode) {
|
||||
explodeSprite();
|
||||
}
|
||||
}
|
||||
|
||||
this.x += CDx[z];
|
||||
this.y += CDy[z];
|
||||
}
|
||||
}
|
145
src/micropolisj/engine/Animate.java
Normal file
145
src/micropolisj/engine/Animate.java
Normal file
|
@ -0,0 +1,145 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
import static micropolisj.engine.TileConstants.*;
|
||||
|
||||
public class Animate
|
||||
{
|
||||
static final char [] aniTile = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55,
|
||||
/* Fire */
|
||||
57, 58, 59, 60, 61, 62, 63, 56,
|
||||
/* No Traffic */
|
||||
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
|
||||
/* Light Traffic */
|
||||
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
|
||||
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
|
||||
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
|
||||
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
|
||||
/* Heavy Traffic */
|
||||
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
|
||||
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
|
||||
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
|
||||
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
|
||||
/* Wires & Rails */
|
||||
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
|
||||
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
|
||||
/* Residential */
|
||||
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255,
|
||||
256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271,
|
||||
272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287,
|
||||
288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303,
|
||||
304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319,
|
||||
320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335,
|
||||
336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351,
|
||||
352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367,
|
||||
368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383,
|
||||
384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399,
|
||||
400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415,
|
||||
416, 417, 418, 419, 420, 421, 422,
|
||||
/* Commercial */
|
||||
423, 424, 425, 426, 427, 428, 429, 430, 431,
|
||||
432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447,
|
||||
448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463,
|
||||
464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479,
|
||||
480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495,
|
||||
496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511,
|
||||
512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527,
|
||||
528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543,
|
||||
544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559,
|
||||
560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575,
|
||||
576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591,
|
||||
592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607,
|
||||
608, 609, 610, 611,
|
||||
/* Industrial */
|
||||
612, 613, 614, 615, 616, 617, 618, 619, 852, 621, 622, 623,
|
||||
624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639,
|
||||
640, 884, 642, 643, 888, 645, 646, 647, 648, 892, 896, 651, 652, 653, 654, 655,
|
||||
656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671,
|
||||
672, 673, 674, 675, 900, 904, 678, 679, 680, 681, 682, 683, 684, 685, 908, 687,
|
||||
688, 912, 690, 691, 692,
|
||||
/* SeaPort */
|
||||
693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703,
|
||||
704, 705, 706, 707, 708,
|
||||
/* AirPort */
|
||||
709, 710, 832, 712, 713, 714, 715, 716, 717, 718, 719,
|
||||
720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735,
|
||||
736, 737, 738, 739, 740, 741, 742, 743, 744,
|
||||
/* Coal power */
|
||||
745, 746, 916, 920, 749, 750, 924,
|
||||
928, 753, 754, 755, 756, 757, 758, 759, 760,
|
||||
/* Fire Dept */
|
||||
761, 762, 763, 764, 765, 766, 767,
|
||||
768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778,
|
||||
/* Stadium */
|
||||
779, 780, 781, 782, 783,
|
||||
784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794,
|
||||
/* Stadium Anims */
|
||||
795, 796, 797, 798, 799,
|
||||
800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810,
|
||||
/* Nuclear Power */
|
||||
811, 812, 813, 814, 815,
|
||||
816, 817, 818, 819, 952, 821, 822, 823, 824, 825, 826,
|
||||
/* Power out + Bridges */
|
||||
827, 828, 829, 830, 831,
|
||||
/* Radar dish */
|
||||
833, 834, 835, 836, 837, 838, 839, 832,
|
||||
/* Fountain / Flag */
|
||||
841, 842, 843, 840, 845, 846, 847, 848,
|
||||
849, 850, 851, 844, 853, 854, 855, 856, 857, 858, 859, 852,
|
||||
/* zone destruct & rubblize */
|
||||
861, 862, 863, 864,
|
||||
865, 866, 867, 867,
|
||||
/* totally unsure */
|
||||
868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879,
|
||||
880, 881, 882, 883,
|
||||
/* Smoke stacks */
|
||||
885, 886, 887, 884, 889, 890, 891, 888, 893, 894, 895, 892,
|
||||
897, 898, 899, 896, 901, 902, 903, 900, 905, 906, 907, 904, 909, 910, 911, 908,
|
||||
913, 914, 915, 912, 917, 918, 919, 916, 921, 922, 923, 920, 925, 926, 927, 924,
|
||||
929, 930, 931, 928,
|
||||
/* Stadium Playfield */
|
||||
933, 934, 935, 936, 937, 938, 939, 932, 941, 942, 943, 944,
|
||||
945, 946, 947, 940,
|
||||
/* Bridge up chars */
|
||||
948, 949, 950, 951,
|
||||
/* Nuclear swirl */
|
||||
953, 954, 955, 952,
|
||||
/* */
|
||||
};
|
||||
|
||||
static class Smoke
|
||||
{
|
||||
// There are eight full Industry-zone images in the tiles bank.
|
||||
// This array indicates which of those eight zones have an animation.
|
||||
static int [] AniThis = { 1, 0, 1, 1, 0, 0, 1, 1 };
|
||||
|
||||
// Up to two tiles can be animated. Arrays DX1,DXY indicate the relative
|
||||
// position of the first animated tile.
|
||||
static int [] DX1 = { -1, 0, 1, 0, 0, 0, 0, 1 };
|
||||
static int [] DY1 = { -1, 0, -1, -1, 0, 0, -1, -1 };
|
||||
|
||||
// Arrays DX2,DY2 indicate the second animated tile.
|
||||
static int [] DX2 = { -1, 0, 1, 1, 0, 0, 1, 1 };
|
||||
static int [] DY2 = { -1, 0, 0, -1, 0, 0, -1, 0 };
|
||||
|
||||
static int [] AniTabA = { 0, 0, 32, 40, 0, 0, 48, 56 };
|
||||
static int [] AniTabB = { 0, 0, 36, 44, 0, 0, 52, 60 };
|
||||
static int [] AniTabC = { IND1, 0, IND2, IND4, 0, 0, IND6, IND8 };
|
||||
static int [] AniTabD = { IND1, 0, IND3, IND5, 0, 0, IND7, IND9 };
|
||||
|
||||
static final int ASCBIT = (ANIMBIT | CONDBIT | BURNBIT);
|
||||
static final int REGBIT = (CONDBIT | BURNBIT);
|
||||
}
|
||||
|
||||
}
|
30
src/micropolisj/engine/BudgetNumbers.java
Normal file
30
src/micropolisj/engine/BudgetNumbers.java
Normal file
|
@ -0,0 +1,30 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
public class BudgetNumbers
|
||||
{
|
||||
public int taxRate;
|
||||
public int taxIncome;
|
||||
public int operatingExpenses;
|
||||
public int previousBalance;
|
||||
public int newBalance;
|
||||
|
||||
public int roadRequest;
|
||||
public int roadFunded;
|
||||
public double roadPercent;
|
||||
|
||||
public int fireRequest;
|
||||
public int fireFunded;
|
||||
public double firePercent;
|
||||
|
||||
public int policeRequest;
|
||||
public int policeFunded;
|
||||
public double policePercent;
|
||||
}
|
276
src/micropolisj/engine/CityEval.java
Normal file
276
src/micropolisj/engine/CityEval.java
Normal file
|
@ -0,0 +1,276 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class CityEval
|
||||
{
|
||||
private final Micropolis engine;
|
||||
private final Random PRNG;
|
||||
|
||||
public CityEval(Micropolis engine)
|
||||
{
|
||||
this.engine = engine;
|
||||
this.PRNG = engine.PRNG;
|
||||
|
||||
assert PRNG != null;
|
||||
}
|
||||
|
||||
/** Percentage of population "approving" the mayor. Derived from cityScore. */
|
||||
public int cityYes;
|
||||
|
||||
/** Percentage of population "disapproving" the mayor. Derived from cityScore. */
|
||||
public int cityNo;
|
||||
|
||||
/** City assessment value. */
|
||||
public int cityAssValue;
|
||||
|
||||
/** Player's score, 0-1000. */
|
||||
public int cityScore;
|
||||
|
||||
/** Change in cityScore since last evaluation. */
|
||||
public int deltaCityScore;
|
||||
|
||||
/** City population as of current evaluation. */
|
||||
public int cityPop;
|
||||
|
||||
/** Change in cityPopulation since last evaluation. */
|
||||
public int deltaCityPop;
|
||||
|
||||
/** Classification of city size. 0==village, 1==town, etc. */
|
||||
public int cityClass; // 0..5
|
||||
|
||||
/** City's top 4 (or fewer) problems as reported by citizens. */
|
||||
public CityProblem [] problemOrder = new CityProblem[0];
|
||||
|
||||
/** Number of votes given for the various problems identified by problemOrder[]. */
|
||||
public EnumMap<CityProblem,Integer> problemVotes = new EnumMap<>(CityProblem.class);
|
||||
|
||||
/** Score for various problems. */
|
||||
public EnumMap<CityProblem,Integer> problemTable = new EnumMap<>(CityProblem.class);
|
||||
|
||||
void cityEvaluation()
|
||||
{
|
||||
if (engine.totalPop != 0) {
|
||||
calculateAssValue();
|
||||
doPopNum();
|
||||
doProblems();
|
||||
calculateScore();
|
||||
doVotes();
|
||||
} else {
|
||||
evalInit();
|
||||
}
|
||||
engine.fireEvaluationChanged();
|
||||
}
|
||||
|
||||
/** Evaluate an empty city. */
|
||||
void evalInit()
|
||||
{
|
||||
cityYes = 0;
|
||||
cityNo = 0;
|
||||
cityAssValue = 0;
|
||||
cityClass = 0;
|
||||
cityScore = 500;
|
||||
deltaCityScore = 0;
|
||||
problemVotes.clear();
|
||||
problemOrder = new CityProblem[0];
|
||||
}
|
||||
|
||||
void calculateAssValue()
|
||||
{
|
||||
int z = 0;
|
||||
z += engine.roadTotal * 5;
|
||||
z += engine.railTotal * 10;
|
||||
z += engine.policeCount * 1000;
|
||||
z += engine.fireStationCount * 1000;
|
||||
z += engine.hospitalCount * 400;
|
||||
z += engine.stadiumCount * 3000;
|
||||
z += engine.seaportCount * 5000;
|
||||
z += engine.airportCount * 10000;
|
||||
z += engine.coalCount * 3000;
|
||||
z += engine.nuclearCount * 6000;
|
||||
cityAssValue = z * 1000;
|
||||
}
|
||||
|
||||
void doPopNum()
|
||||
{
|
||||
int oldCityPop = cityPop;
|
||||
cityPop = engine.getCityPopulation();
|
||||
deltaCityPop = cityPop - oldCityPop;
|
||||
|
||||
cityClass =
|
||||
cityPop > 500000 ? 5 : //megalopolis
|
||||
cityPop > 100000 ? 4 : //metropolis
|
||||
cityPop > 50000 ? 3 : //capital
|
||||
cityPop > 10000 ? 2 : //city
|
||||
cityPop > 2000 ? 1 : //town
|
||||
0; //village
|
||||
}
|
||||
|
||||
void doProblems()
|
||||
{
|
||||
problemTable.clear();
|
||||
problemTable.put(CityProblem.CRIME, engine.crimeAverage);
|
||||
problemTable.put(CityProblem.POLLUTION, engine.pollutionAverage);
|
||||
problemTable.put(CityProblem.HOUSING, (int)Math.round(engine.landValueAverage * 0.7));
|
||||
problemTable.put(CityProblem.TAXES, engine.cityTax * 10);
|
||||
problemTable.put(CityProblem.TRAFFIC, averageTrf());
|
||||
problemTable.put(CityProblem.UNEMPLOYMENT, getUnemployment());
|
||||
problemTable.put(CityProblem.FIRE, getFire());
|
||||
|
||||
problemVotes = voteProblems(problemTable);
|
||||
|
||||
CityProblem [] probOrder = CityProblem.values();
|
||||
Arrays.sort(probOrder, new Comparator<CityProblem>() {
|
||||
public int compare(CityProblem a, CityProblem b) {
|
||||
return -(problemVotes.get(a).compareTo(problemVotes.get(b)));
|
||||
}});
|
||||
|
||||
int c = 0;
|
||||
while (c < probOrder.length &&
|
||||
problemVotes.get(probOrder[c]).intValue() != 0 &&
|
||||
c < 4)
|
||||
c++;
|
||||
|
||||
problemOrder = new CityProblem[c];
|
||||
for (int i = 0; i < c; i++) {
|
||||
problemOrder[i] = probOrder[i];
|
||||
}
|
||||
}
|
||||
|
||||
EnumMap<CityProblem,Integer> voteProblems(Map<CityProblem,Integer> probTab)
|
||||
{
|
||||
CityProblem [] pp = CityProblem.values();
|
||||
int [] votes = new int[pp.length];
|
||||
|
||||
int countVotes = 0;
|
||||
for (int i = 0; i < 600; i++) {
|
||||
if (PRNG.nextInt(301) < probTab.get(pp[i%pp.length])) {
|
||||
votes[i%pp.length]++;
|
||||
countVotes++;
|
||||
if (countVotes >= 100)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EnumMap<CityProblem,Integer> rv = new EnumMap<>(CityProblem.class);
|
||||
for (int i = 0; i < pp.length; i++) {
|
||||
rv.put(pp[i], votes[i]);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
int averageTrf()
|
||||
{
|
||||
int count = 1;
|
||||
int total = 0;
|
||||
|
||||
for (int hy = 0; hy < engine.trfDensity.length; hy++) {
|
||||
for (int hx = 0; hx < engine.trfDensity[hy].length; hx++) {
|
||||
if (engine.landValueMem[hy][hx] != 0) {
|
||||
total += engine.trfDensity[hy][hx];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
engine.trafficAverage = (int)Math.round(((double)total / (double)count) * 2.4);
|
||||
return engine.trafficAverage;
|
||||
}
|
||||
|
||||
int getUnemployment()
|
||||
{
|
||||
int b = (engine.comPop + engine.indPop) * 8;
|
||||
if (b == 0)
|
||||
return 0;
|
||||
|
||||
double r = (double)engine.resPop / (double)b;
|
||||
b = (int)Math.floor((r-1.0)*255);
|
||||
if (b > 255) {
|
||||
b = 255;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
int getFire()
|
||||
{
|
||||
int z = engine.firePop * 5;
|
||||
return Math.min(255, z);
|
||||
}
|
||||
|
||||
static double clamp(double x, double min, double max)
|
||||
{
|
||||
return Math.max(min, Math.min(max, x));
|
||||
}
|
||||
|
||||
void calculateScore()
|
||||
{
|
||||
int oldCityScore = cityScore;
|
||||
|
||||
int x = 0;
|
||||
for (Integer z : problemTable.values()) {
|
||||
x += z.intValue();
|
||||
}
|
||||
|
||||
x /= 3;
|
||||
x = Math.min(256, x);
|
||||
|
||||
double z = clamp((256 - x) * 4, 0, 1000);
|
||||
|
||||
if (engine.resCap) { z = 0.85 * z; }
|
||||
if (engine.comCap) { z = 0.85 * z; }
|
||||
if (engine.indCap) { z = 0.85 * z; }
|
||||
if (engine.roadEffect < 32) { z -= (32 - engine.roadEffect); }
|
||||
if (engine.policeEffect < 1000) { z *= (0.9 + (engine.policeEffect / 10000.1)); }
|
||||
if (engine.fireEffect < 1000) { z *= (0.9 + (engine.fireEffect / 10000.1)); }
|
||||
if (engine.resValve < -1000) { z *= 0.85; }
|
||||
if (engine.comValve < -1000) { z *= 0.85; }
|
||||
if (engine.indValve < -1000) { z *= 0.85; }
|
||||
|
||||
double SM = 1.0;
|
||||
if (cityPop == 0 && deltaCityPop == 0) {
|
||||
SM = 1.0;
|
||||
}
|
||||
else if (deltaCityPop == cityPop) {
|
||||
SM = 1.0;
|
||||
}
|
||||
else if (deltaCityPop > 0) {
|
||||
SM = (double)deltaCityPop / (double)cityPop + 1.0;
|
||||
}
|
||||
else if (deltaCityPop < 0) {
|
||||
SM = 0.95 + ((double)deltaCityPop / (double)(cityPop-deltaCityPop));
|
||||
}
|
||||
z *= SM;
|
||||
z -= getFire();
|
||||
z -= engine.cityTax;
|
||||
|
||||
int TM = engine.unpoweredZoneCount + engine.poweredZoneCount;
|
||||
SM = TM != 0 ? ((double)engine.poweredZoneCount / (double)TM) : 1.0;
|
||||
z *= SM;
|
||||
|
||||
z = clamp(z, 0, 1000);
|
||||
|
||||
cityScore = (int)Math.round((cityScore + z) / 2.0);
|
||||
deltaCityScore = cityScore - oldCityScore;
|
||||
}
|
||||
|
||||
void doVotes()
|
||||
{
|
||||
cityYes = cityNo = 0;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
if (PRNG.nextInt(1001) < cityScore) {
|
||||
cityYes++;
|
||||
} else {
|
||||
cityNo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
45
src/micropolisj/engine/CityLocation.java
Normal file
45
src/micropolisj/engine/CityLocation.java
Normal file
|
@ -0,0 +1,45 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
public class CityLocation
|
||||
{
|
||||
public int x;
|
||||
public int y;
|
||||
|
||||
public CityLocation(int x, int y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return x*33+y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj instanceof CityLocation) {
|
||||
CityLocation rhs = (CityLocation)obj;
|
||||
return this.x == rhs.x && this.y == rhs.y;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "("+x+","+y+")";
|
||||
}
|
||||
}
|
23
src/micropolisj/engine/CityProblem.java
Normal file
23
src/micropolisj/engine/CityProblem.java
Normal file
|
@ -0,0 +1,23 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
/**
|
||||
* Enumeration of various city problems that the citizens complain about.
|
||||
*/
|
||||
public enum CityProblem
|
||||
{
|
||||
CRIME,
|
||||
POLLUTION,
|
||||
HOUSING,
|
||||
TAXES,
|
||||
TRAFFIC,
|
||||
UNEMPLOYMENT,
|
||||
FIRE;
|
||||
}
|
22
src/micropolisj/engine/Disaster.java
Normal file
22
src/micropolisj/engine/Disaster.java
Normal file
|
@ -0,0 +1,22 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
/**
|
||||
* Lists the disasters that the user can invoke.
|
||||
*/
|
||||
public enum Disaster
|
||||
{
|
||||
MONSTER,
|
||||
FIRE,
|
||||
FLOOD,
|
||||
MELTDOWN,
|
||||
TORNADO,
|
||||
EARTHQUAKE;
|
||||
}
|
17
src/micropolisj/engine/EarthquakeListener.java
Normal file
17
src/micropolisj/engine/EarthquakeListener.java
Normal file
|
@ -0,0 +1,17 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
/**
|
||||
* The listener interface for receiving earthquake notifications.
|
||||
*/
|
||||
public interface EarthquakeListener
|
||||
{
|
||||
void earthquakeStarted();
|
||||
}
|
63
src/micropolisj/engine/ExplosionSprite.java
Normal file
63
src/micropolisj/engine/ExplosionSprite.java
Normal file
|
@ -0,0 +1,63 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
import static micropolisj.engine.TileConstants.*;
|
||||
|
||||
public class ExplosionSprite extends Sprite
|
||||
{
|
||||
public ExplosionSprite(Micropolis engine, int x, int y)
|
||||
{
|
||||
super(engine, SpriteKind.EXP);
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = 48;
|
||||
this.height = 48;
|
||||
this.offx = -24;
|
||||
this.offy = -24;
|
||||
this.frame = 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveImpl()
|
||||
{
|
||||
if (city.acycle % 2 == 0) {
|
||||
if (this.frame == 1) {
|
||||
city.makeSound(x/16, y/16, Sound.EXPLOSION_HIGH);
|
||||
city.sendMessageAt(MicropolisMessage.EXPLOSION_REPORT, x/16, y/16);
|
||||
}
|
||||
this.frame++;
|
||||
}
|
||||
|
||||
if (this.frame > 6) {
|
||||
this.frame = 0;
|
||||
|
||||
startFire(x/16, y/16);
|
||||
startFire(x/16-1, y/16-1);
|
||||
startFire(x/16+1, y/16-1);
|
||||
startFire(x/16-1, y/16+1);
|
||||
startFire(x/16+1, y/16+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void startFire(int xpos, int ypos)
|
||||
{
|
||||
if (!city.testBounds(xpos, ypos))
|
||||
return;
|
||||
|
||||
int z = city.getTile(xpos, ypos);
|
||||
int t = z & LOMASK;
|
||||
if ((z & BURNBIT) == 0 && t != DIRT)
|
||||
return;
|
||||
if ((z & ZONEBIT) != 0)
|
||||
return;
|
||||
city.setTile(xpos, ypos, (char)(FIRE + city.PRNG.nextInt(4) + ANIMBIT));
|
||||
}
|
||||
}
|
34
src/micropolisj/engine/GameLevel.java
Normal file
34
src/micropolisj/engine/GameLevel.java
Normal file
|
@ -0,0 +1,34 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
public class GameLevel
|
||||
{
|
||||
public static final int MIN_LEVEL = 0;
|
||||
public static final int MAX_LEVEL = 2;
|
||||
|
||||
public static boolean isValid(int lev)
|
||||
{
|
||||
return lev >= MIN_LEVEL && lev <= MAX_LEVEL;
|
||||
}
|
||||
|
||||
public static int getStartingFunds(int lev)
|
||||
{
|
||||
switch (lev) {
|
||||
case 0: return 20000;
|
||||
case 1: return 10000;
|
||||
case 2: return 5000;
|
||||
default:
|
||||
throw new Error("unexpected game level: "+lev);
|
||||
}
|
||||
}
|
||||
|
||||
//prevent this class from being instantiated
|
||||
private GameLevel() {}
|
||||
}
|
101
src/micropolisj/engine/HelicopterSprite.java
Normal file
101
src/micropolisj/engine/HelicopterSprite.java
Normal file
|
@ -0,0 +1,101 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
public class HelicopterSprite extends Sprite
|
||||
{
|
||||
int count;
|
||||
int destX;
|
||||
int destY;
|
||||
int origX;
|
||||
int origY;
|
||||
|
||||
static int [] CDx = { 0, 0, 3, 5, 3, 0, -3, -5, -3 };
|
||||
static int [] CDy = { 0, -5, -3, 0, 3, 5, 3, 0, -3 };
|
||||
static final int SOUND_FREQ = 200;
|
||||
|
||||
public HelicopterSprite(Micropolis engine, int xpos, int ypos)
|
||||
{
|
||||
super(engine, SpriteKind.COP);
|
||||
this.x = xpos * 16 + 8;
|
||||
this.y = ypos * 16 + 8;
|
||||
this.width = 32;
|
||||
this.height = 32;
|
||||
this.offx = -16;
|
||||
this.offy = -16;
|
||||
|
||||
this.destX = city.PRNG.nextInt(city.getWidth()) * 16 + 8;
|
||||
this.destY = city.PRNG.nextInt(city.getHeight()) * 16 + 8;
|
||||
|
||||
this.origX = x;
|
||||
this.origY = y;
|
||||
this.count = 1500;
|
||||
this.frame = 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveImpl()
|
||||
{
|
||||
if (this.count > 0) {
|
||||
this.count--;
|
||||
}
|
||||
|
||||
if (this.count == 0) {
|
||||
|
||||
// attract copter to monster and tornado so it blows up more often
|
||||
if (city.hasSprite(SpriteKind.GOD)) {
|
||||
|
||||
MonsterSprite monster = (MonsterSprite) city.getSprite(SpriteKind.GOD);
|
||||
this.destX = monster.x;
|
||||
this.destY = monster.y;
|
||||
|
||||
}
|
||||
else if (city.hasSprite(SpriteKind.TOR)) {
|
||||
|
||||
TornadoSprite tornado = (TornadoSprite) city.getSprite(SpriteKind.TOR);
|
||||
this.destX = tornado.x;
|
||||
this.destY = tornado.y;
|
||||
|
||||
}
|
||||
else {
|
||||
this.destX = origX;
|
||||
this.destY = origY;
|
||||
}
|
||||
|
||||
if (getDis(x, y, origX, origY) < 30) {
|
||||
// made it back to airport, go ahead and land.
|
||||
this.frame = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (city.acycle % SOUND_FREQ == 0) {
|
||||
// send report, if hovering over high traffic area
|
||||
int xpos = this.x / 16;
|
||||
int ypos = this.y / 16;
|
||||
|
||||
if (city.getTrafficDensity(xpos, ypos) > 170 &&
|
||||
city.PRNG.nextInt(8) == 0)
|
||||
{
|
||||
city.sendMessageAt(MicropolisMessage.HEAVY_TRAFFIC_REPORT,
|
||||
xpos, ypos);
|
||||
city.makeSound(xpos, ypos, Sound.HEAVYTRAFFIC);
|
||||
}
|
||||
}
|
||||
|
||||
int z = this.frame;
|
||||
if (city.acycle % 3 == 0) {
|
||||
int d = getDir(x, y, destX, destY);
|
||||
z = turnTo(z, d);
|
||||
this.frame = z;
|
||||
}
|
||||
x += CDx[z];
|
||||
y += CDy[z];
|
||||
}
|
||||
}
|
541
src/micropolisj/engine/MapGenerator.java
Normal file
541
src/micropolisj/engine/MapGenerator.java
Normal file
|
@ -0,0 +1,541 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static micropolisj.engine.TileConstants.*;
|
||||
|
||||
public class MapGenerator
|
||||
{
|
||||
Micropolis engine;
|
||||
char [][] map;
|
||||
Random PRNG;
|
||||
|
||||
/**
|
||||
* Three settings on whether to generate a new map as an island.
|
||||
*/
|
||||
static enum CreateIsland
|
||||
{
|
||||
NEVER,
|
||||
ALWAYS,
|
||||
SELDOM; // seldom == 10% of the time
|
||||
}
|
||||
CreateIsland createIsland = CreateIsland.SELDOM;
|
||||
|
||||
public MapGenerator(Micropolis engine)
|
||||
{
|
||||
assert engine != null;
|
||||
this.engine = engine;
|
||||
this.map = engine.map;
|
||||
}
|
||||
|
||||
private int getWidth()
|
||||
{
|
||||
return map[0].length;
|
||||
}
|
||||
|
||||
private int getHeight()
|
||||
{
|
||||
return map.length;
|
||||
}
|
||||
|
||||
public void generateNewCity()
|
||||
{
|
||||
long r = Micropolis.DEFAULT_PRNG.nextLong();
|
||||
generateSomeCity(r);
|
||||
}
|
||||
|
||||
public void generateSomeCity(long r)
|
||||
{
|
||||
engine.totalFunds = GameLevel.getStartingFunds(engine.gameLevel);
|
||||
generateMap(r);
|
||||
engine.fireWholeMapChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Level for tree creation.
|
||||
* If positive, this is (roughly) the number of trees to randomly place.
|
||||
* If negative, then the number of trees is randomly chosen.
|
||||
* If zero, then no trees are generated.
|
||||
*/
|
||||
int treeLevel = -1; //level for tree creation
|
||||
|
||||
int curveLevel = -1; //level for river curviness; -1==auto, 0==none, >0==level
|
||||
|
||||
int lakeLevel = -1; //level for lake creation; -1==auto, 0==none, >0==level
|
||||
|
||||
void generateMap(long r)
|
||||
{
|
||||
PRNG = new Random(r);
|
||||
|
||||
if (createIsland == CreateIsland.SELDOM)
|
||||
{
|
||||
if (PRNG.nextInt(100) < 10) //chance that island is generated
|
||||
{
|
||||
makeIsland();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (createIsland == CreateIsland.ALWAYS)
|
||||
{
|
||||
makeNakedIsland();
|
||||
}
|
||||
else
|
||||
{
|
||||
clearMap();
|
||||
}
|
||||
|
||||
getRandStart();
|
||||
|
||||
if (curveLevel != 0)
|
||||
{
|
||||
doRivers();
|
||||
}
|
||||
|
||||
if (lakeLevel != 0)
|
||||
{
|
||||
makeLakes();
|
||||
}
|
||||
|
||||
smoothRiver();
|
||||
|
||||
if (treeLevel != 0)
|
||||
{
|
||||
doTrees();
|
||||
}
|
||||
}
|
||||
|
||||
private void makeIsland()
|
||||
{
|
||||
makeNakedIsland();
|
||||
smoothRiver();
|
||||
doTrees();
|
||||
}
|
||||
|
||||
private int erand(int limit)
|
||||
{
|
||||
return Math.min(
|
||||
PRNG.nextInt(limit),
|
||||
PRNG.nextInt(limit)
|
||||
);
|
||||
}
|
||||
|
||||
private void makeNakedIsland()
|
||||
{
|
||||
final int ISLAND_RADIUS = 18;
|
||||
final int WORLD_X = getWidth();
|
||||
final int WORLD_Y = getHeight();
|
||||
|
||||
for (int y = 0; y < WORLD_Y; y++)
|
||||
{
|
||||
for (int x = 0; x < WORLD_X; x++)
|
||||
{
|
||||
map[y][x] = RIVER;
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = 5; y < WORLD_Y - 5; y++)
|
||||
{
|
||||
for (int x = 5; x < WORLD_X - 5; x++)
|
||||
{
|
||||
map[y][x] = DIRT;
|
||||
}
|
||||
}
|
||||
|
||||
for (int x = 0; x < WORLD_X - 5; x += 2)
|
||||
{
|
||||
mapX = x;
|
||||
mapY = erand(ISLAND_RADIUS+1);
|
||||
BRivPlop();
|
||||
mapY = (WORLD_Y - 10) - erand(ISLAND_RADIUS+1);
|
||||
BRivPlop();
|
||||
mapY = 0;
|
||||
SRivPlop();
|
||||
mapY = WORLD_Y - 6;
|
||||
SRivPlop();
|
||||
}
|
||||
|
||||
for (int y = 0; y < WORLD_Y - 5; y += 2)
|
||||
{
|
||||
mapY = y;
|
||||
mapX = erand(ISLAND_RADIUS+1);
|
||||
BRivPlop();
|
||||
mapX = (WORLD_X - 10) - erand(ISLAND_RADIUS+1);
|
||||
BRivPlop();
|
||||
mapX = 0;
|
||||
SRivPlop();
|
||||
mapX = (WORLD_X - 6);
|
||||
SRivPlop();
|
||||
}
|
||||
}
|
||||
|
||||
private void clearMap()
|
||||
{
|
||||
for (int y = 0; y < map.length; y++)
|
||||
{
|
||||
for (int x = 0; x < map[y].length; x++)
|
||||
{
|
||||
map[y][x] = DIRT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int xStart;
|
||||
int yStart;
|
||||
int mapX;
|
||||
int mapY;
|
||||
int dir;
|
||||
int lastDir;
|
||||
|
||||
private void getRandStart()
|
||||
{
|
||||
xStart = 40 + PRNG.nextInt(getWidth() - 79);
|
||||
yStart = 33 + PRNG.nextInt(getHeight() - 66);
|
||||
|
||||
mapX = xStart;
|
||||
mapY = yStart;
|
||||
}
|
||||
|
||||
private void makeLakes()
|
||||
{
|
||||
int lim1;
|
||||
if (lakeLevel < 0)
|
||||
lim1 = PRNG.nextInt(11);
|
||||
else
|
||||
lim1 = lakeLevel / 2;
|
||||
|
||||
for (int t = 0; t < lim1; t++)
|
||||
{
|
||||
int x = PRNG.nextInt(getWidth() - 20) + 10;
|
||||
int y = PRNG.nextInt(getHeight() - 19) + 10;
|
||||
int lim2 = PRNG.nextInt(13) + 2;
|
||||
|
||||
for (int z = 0; z < lim2; z++)
|
||||
{
|
||||
mapX = x - 6 + PRNG.nextInt(13);
|
||||
mapY = y - 6 + PRNG.nextInt(13);
|
||||
|
||||
if (PRNG.nextInt(5) != 0)
|
||||
SRivPlop();
|
||||
else
|
||||
BRivPlop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doRivers()
|
||||
{
|
||||
dir = lastDir = PRNG.nextInt(4);
|
||||
doBRiv();
|
||||
|
||||
mapX = xStart;
|
||||
mapY = yStart;
|
||||
dir = lastDir = lastDir ^ 4;
|
||||
doBRiv();
|
||||
|
||||
mapX = xStart;
|
||||
mapY = yStart;
|
||||
lastDir = PRNG.nextInt(4);
|
||||
doSRiv();
|
||||
}
|
||||
|
||||
private void doBRiv()
|
||||
{
|
||||
int r1, r2;
|
||||
if (curveLevel < 0)
|
||||
{
|
||||
r1 = 100;
|
||||
r2 = 200;
|
||||
}
|
||||
else
|
||||
{
|
||||
r1 = curveLevel + 10;
|
||||
r2 = curveLevel + 100;
|
||||
}
|
||||
|
||||
while (engine.testBounds(mapX + 4, mapY + 4))
|
||||
{
|
||||
BRivPlop();
|
||||
if (PRNG.nextInt(r1+1) < 10)
|
||||
{
|
||||
dir = lastDir;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PRNG.nextInt(r2+1) > 90)
|
||||
{
|
||||
dir++;
|
||||
}
|
||||
if (PRNG.nextInt(r2+1) > 90)
|
||||
{
|
||||
dir--;
|
||||
}
|
||||
}
|
||||
moveMap(dir);
|
||||
}
|
||||
}
|
||||
|
||||
private void doSRiv()
|
||||
{
|
||||
int r1, r2;
|
||||
if (curveLevel < 0)
|
||||
{
|
||||
r1 = 100;
|
||||
r2 = 200;
|
||||
}
|
||||
else
|
||||
{
|
||||
r1 = curveLevel + 10;
|
||||
r2 = curveLevel + 100;
|
||||
}
|
||||
|
||||
while (engine.testBounds(mapX + 3, mapY + 3))
|
||||
{
|
||||
SRivPlop();
|
||||
if (PRNG.nextInt(r1+1) < 10)
|
||||
{
|
||||
dir = lastDir;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PRNG.nextInt(r2+1) > 90)
|
||||
{
|
||||
dir++;
|
||||
}
|
||||
if (PRNG.nextInt(r2+1) > 90)
|
||||
{
|
||||
dir--;
|
||||
}
|
||||
}
|
||||
moveMap(dir);
|
||||
}
|
||||
}
|
||||
|
||||
static final char [][] BRMatrix = new char[][] {
|
||||
{ 0, 0, 0, 3, 3, 3, 0, 0, 0 },
|
||||
{ 0, 0, 3, 2, 2, 2, 3, 0, 0 },
|
||||
{ 0, 3, 2, 2, 2, 2, 2, 3, 0 },
|
||||
{ 3, 2, 2, 2, 2, 2, 2, 2, 3 },
|
||||
{ 3, 2, 2, 2, 4, 2, 2, 2, 3 },
|
||||
{ 3, 2, 2, 2, 2, 2, 2, 2, 3 },
|
||||
{ 0, 3, 2, 2, 2, 2, 2, 3, 0 },
|
||||
{ 0, 0, 3, 2, 2, 2, 3, 0, 0 },
|
||||
{ 0, 0, 0, 3, 3, 3, 0, 0, 0 }
|
||||
};
|
||||
|
||||
private void BRivPlop()
|
||||
{
|
||||
for (int x = 0; x < 9; x++)
|
||||
{
|
||||
for (int y = 0; y < 9; y++)
|
||||
{
|
||||
putOnMap(BRMatrix[y][x], x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final char [][] SRMatrix = new char[][] {
|
||||
{ 0, 0, 3, 3, 0, 0 },
|
||||
{ 0, 3, 2, 2, 3, 0 },
|
||||
{ 3, 2, 2, 2, 2, 3 },
|
||||
{ 3, 2, 2, 2, 2, 3 },
|
||||
{ 0, 3, 2, 2, 3, 0 },
|
||||
{ 0, 0, 3, 3, 0, 0 }
|
||||
};
|
||||
|
||||
private void SRivPlop()
|
||||
{
|
||||
for (int x = 0; x < 6; x++)
|
||||
{
|
||||
for (int y = 0; y < 6; y++)
|
||||
{
|
||||
putOnMap(SRMatrix[y][x], x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void putOnMap(char mapChar, int xoff, int yoff)
|
||||
{
|
||||
if (mapChar == 0)
|
||||
return;
|
||||
|
||||
int xloc = mapX + xoff;
|
||||
int yloc = mapY + yoff;
|
||||
|
||||
if (!engine.testBounds(xloc, yloc))
|
||||
return;
|
||||
|
||||
char tmp = map[yloc][xloc];
|
||||
if (tmp != DIRT)
|
||||
{
|
||||
tmp &= LOMASK;
|
||||
if (tmp == RIVER && mapChar != CHANNEL)
|
||||
return;
|
||||
if (tmp == CHANNEL)
|
||||
return;
|
||||
}
|
||||
map[yloc][xloc] = mapChar;
|
||||
}
|
||||
|
||||
static final char [] REdTab = new char[] {
|
||||
13 + BULLBIT, 13 + BULLBIT, 17 + BULLBIT, 15 + BULLBIT,
|
||||
5 + BULLBIT, 2, 19 + BULLBIT, 17 + BULLBIT,
|
||||
9 + BULLBIT, 11 + BULLBIT, 2, 13 + BULLBIT,
|
||||
7 + BULLBIT, 9 + BULLBIT, 5 + BULLBIT, 2
|
||||
};
|
||||
|
||||
private void smoothRiver()
|
||||
{
|
||||
for (int mapY = 0; mapY < map.length; mapY++)
|
||||
{
|
||||
for (int mapX = 0; mapX < map[mapY].length; mapX++)
|
||||
{
|
||||
if (map[mapY][mapX] == REDGE)
|
||||
{
|
||||
int bitindex = 0;
|
||||
|
||||
for (int z = 0; z < 4; z++)
|
||||
{
|
||||
bitindex <<= 1;
|
||||
int xtem = mapX + DX[z];
|
||||
int ytem = mapY + DY[z];
|
||||
if (engine.testBounds(xtem, ytem) &&
|
||||
((map[ytem][xtem] & LOMASK) != DIRT) &&
|
||||
(((map[ytem][xtem] & LOMASK) < WOODS_LOW) ||
|
||||
((map[ytem][xtem] & LOMASK) > WOODS_HIGH)))
|
||||
{
|
||||
bitindex |= 1;
|
||||
}
|
||||
}
|
||||
|
||||
char temp = REdTab[bitindex & 15];
|
||||
if ((temp != RIVER) && PRNG.nextInt(2) != 0)
|
||||
temp++;
|
||||
map[mapY][mapX] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doTrees()
|
||||
{
|
||||
int amount;
|
||||
|
||||
if (treeLevel < 0)
|
||||
{
|
||||
amount = PRNG.nextInt(101) + 50;
|
||||
}
|
||||
else
|
||||
{
|
||||
amount = treeLevel + 3;
|
||||
}
|
||||
|
||||
for (int x = 0; x < amount; x++)
|
||||
{
|
||||
int xloc = PRNG.nextInt(getWidth());
|
||||
int yloc = PRNG.nextInt(getHeight());
|
||||
treeSplash(xloc, yloc);
|
||||
}
|
||||
|
||||
smoothTrees();
|
||||
smoothTrees();
|
||||
}
|
||||
|
||||
private void treeSplash(int xloc, int yloc)
|
||||
{
|
||||
int dis;
|
||||
if (treeLevel < 0)
|
||||
{
|
||||
dis = PRNG.nextInt(151) + 50;
|
||||
}
|
||||
else
|
||||
{
|
||||
dis = PRNG.nextInt(101 + (treeLevel*2)) + 50;
|
||||
}
|
||||
|
||||
mapX = xloc;
|
||||
mapY = yloc;
|
||||
|
||||
for (int z = 0; z < dis; z++)
|
||||
{
|
||||
int dir = PRNG.nextInt(8);
|
||||
moveMap(dir);
|
||||
|
||||
if (!engine.testBounds(mapX, mapY))
|
||||
return;
|
||||
|
||||
if ((map[mapY][mapX] & LOMASK) == DIRT)
|
||||
{
|
||||
map[mapY][mapX] = WOODS + BLBNBIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final int [] DIRECTION_TABX = new int[] { 0, 1, 1, 1, 0, -1, -1, -1 };
|
||||
static final int [] DIRECTION_TABY = new int[] { -1, -1, 0, 1, 1, 1, 0, -1 };
|
||||
private void moveMap(int dir)
|
||||
{
|
||||
dir = dir & 7;
|
||||
mapX += DIRECTION_TABX[dir];
|
||||
mapY += DIRECTION_TABY[dir];
|
||||
}
|
||||
|
||||
static final int [] DX = new int[] { -1, 0, 1, 0 };
|
||||
static final int [] DY = new int[] { 0, 1, 0, -1 };
|
||||
static final char [] TEdTab = new char[] {
|
||||
0, 0, 0, 34,
|
||||
0, 0, 36, 35,
|
||||
0, 32, 0, 33,
|
||||
30, 31, 29, 37
|
||||
};
|
||||
|
||||
private void smoothTrees()
|
||||
{
|
||||
for (int mapY = 0; mapY < map.length; mapY++)
|
||||
{
|
||||
for (int mapX = 0; mapX < map[mapY].length; mapX++)
|
||||
{
|
||||
if (isTree(map[mapY][mapX]))
|
||||
{
|
||||
int bitindex = 0;
|
||||
for (int z = 0; z < 4; z++)
|
||||
{
|
||||
bitindex <<= 1;
|
||||
int xtem = mapX + DX[z];
|
||||
int ytem = mapY + DY[z];
|
||||
if (engine.testBounds(xtem, ytem) &&
|
||||
isTree(map[ytem][xtem]))
|
||||
{
|
||||
bitindex |= 1;
|
||||
}
|
||||
}
|
||||
char temp = TEdTab[bitindex & 15];
|
||||
if (temp != 0)
|
||||
{
|
||||
if (temp != WOODS)
|
||||
{
|
||||
if (((mapX + mapY) & 1) != 0)
|
||||
{
|
||||
temp -= 8;
|
||||
}
|
||||
}
|
||||
map[mapY][mapX] = (char)(temp + BLBNBIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
map[mapY][mapX] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
28
src/micropolisj/engine/MapListener.java
Normal file
28
src/micropolisj/engine/MapListener.java
Normal file
|
@ -0,0 +1,28 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
/**
|
||||
* The listener interface for receiving notifications whenever a tile on
|
||||
* the city map changes, or when a sprite moves or changes.
|
||||
*/
|
||||
public interface MapListener
|
||||
{
|
||||
/** Called whenever data for a specific overlay has changed. */
|
||||
void mapOverlayDataChanged(MapState overlayDataType);
|
||||
|
||||
/** Called when a sprite moves. */
|
||||
void spriteMoved(Sprite sprite);
|
||||
|
||||
/** Called when a map tile changes, including for animations. */
|
||||
void tileChanged(int xpos, int ypos);
|
||||
|
||||
/** Called when the entire map should be reread and rendered. */
|
||||
void wholeMapChanged();
|
||||
}
|
1234
src/micropolisj/engine/MapScanner.java
Normal file
1234
src/micropolisj/engine/MapScanner.java
Normal file
File diff suppressed because it is too large
Load diff
30
src/micropolisj/engine/MapState.java
Normal file
30
src/micropolisj/engine/MapState.java
Normal file
|
@ -0,0 +1,30 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
/**
|
||||
* Lists the various map overlay options that are available.
|
||||
*/
|
||||
public enum MapState
|
||||
{
|
||||
ALL, //ALMAP
|
||||
RESIDENTIAL, //REMAP
|
||||
COMMERCIAL, //COMAP
|
||||
INDUSTRIAL, //INMAP
|
||||
TRANSPORT, //RDMAP
|
||||
POPDEN_OVERLAY, //PDMAP
|
||||
GROWTHRATE_OVERLAY, //RGMAP
|
||||
LANDVALUE_OVERLAY, //LVMAP
|
||||
CRIME_OVERLAY, //CRMAP
|
||||
POLLUTE_OVERLAY, //PLMAP
|
||||
TRAFFIC_OVERLAY, //TDMAP
|
||||
POWER_OVERLAY, //PRMAP
|
||||
FIRE_OVERLAY, //FIMAP
|
||||
POLICE_OVERLAY; //POMAP
|
||||
}
|
2575
src/micropolisj/engine/Micropolis.java
Normal file
2575
src/micropolisj/engine/Micropolis.java
Normal file
File diff suppressed because it is too large
Load diff
64
src/micropolisj/engine/MicropolisMessage.java
Normal file
64
src/micropolisj/engine/MicropolisMessage.java
Normal file
|
@ -0,0 +1,64 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
/**
|
||||
* Enumeration of every possible message for the user generated by the game engine.
|
||||
*/
|
||||
public enum MicropolisMessage
|
||||
{
|
||||
//orig_num generated last tested/verified
|
||||
NEED_RES, // 1 doMessages 1/19
|
||||
NEED_COM, // 2 doMessages 1/19
|
||||
NEED_IND, // 3 doMessages 1/19
|
||||
NEED_ROADS, // 4 doMessages 1/19
|
||||
NEED_RAILS, // 5 doMessages 1/20
|
||||
NEED_POWER, // 6 doMessages 1/19
|
||||
NEED_STADIUM, // 7 doMessages 1/20
|
||||
NEED_SEAPORT, // 8 doMessages 1/20
|
||||
NEED_AIRPORT, // 9 doMessages
|
||||
HIGH_POLLUTION, // 10 doMessages 1/20
|
||||
HIGH_CRIME, // 11 doMessages 1/19
|
||||
HIGH_TRAFFIC, // 12 doMessages 1/20
|
||||
NEED_FIRESTATION, // 13 doMessages 1/19
|
||||
NEED_POLICE, // 14 doMessages 1/19
|
||||
BLACKOUTS, // 15 doMessages 1/19
|
||||
HIGH_TAXES, // 16 doMessages 1/19
|
||||
ROADS_NEED_FUNDING, // 17 doMessages
|
||||
FIRE_NEED_FUNDING, // 18 doMessages
|
||||
POLICE_NEED_FUNDING, // 19 doMessages
|
||||
FIRE_REPORT, // 20
|
||||
MONSTER_REPORT,
|
||||
TORNADO_REPORT,
|
||||
EARTHQUAKE_REPORT, // 23 makeEarthquake
|
||||
PLANECRASH_REPORT,
|
||||
SHIPWRECK_REPORT,
|
||||
TRAIN_CRASH_REPORT,
|
||||
COPTER_CRASH_REPORT,
|
||||
HIGH_UNEMPLOYMENT,
|
||||
OUT_OF_FUNDS_REPORT,
|
||||
FIREBOMBING_REPORT, //30
|
||||
NEED_PARKS,
|
||||
EXPLOSION_REPORT,
|
||||
INSUFFICIENT_FUNDS, // 33 MainWindow.applyCurrentTool
|
||||
BULLDOZE_FIRST, // 34 MainWindow.applyCurrentTool
|
||||
POP_2K_REACHED, // 35 checkGrowth 1/19
|
||||
POP_10K_REACHED, // 36 checkGrowth
|
||||
POP_50K_REACHED, // 37 checkGrowth
|
||||
POP_100K_REACHED, // 38 checkGrowth
|
||||
POP_500K_REACHED, // 39 checkGrowth
|
||||
BROWNOUTS_REPORT, // 40 1/20
|
||||
HEAVY_TRAFFIC_REPORT, // 41 HelicopterSprite
|
||||
FLOOD_REPORT,
|
||||
MELTDOWN_REPORT, // 43 doMeltdown
|
||||
RIOTING_REPORT,
|
||||
|
||||
// added by Jason
|
||||
NO_NUCLEAR_PLANTS;
|
||||
}
|
1138
src/micropolisj/engine/MicropolisTool.java
Normal file
1138
src/micropolisj/engine/MicropolisTool.java
Normal file
File diff suppressed because it is too large
Load diff
181
src/micropolisj/engine/MonsterSprite.java
Normal file
181
src/micropolisj/engine/MonsterSprite.java
Normal file
|
@ -0,0 +1,181 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
import static micropolisj.engine.TileConstants.*;
|
||||
|
||||
public class MonsterSprite extends Sprite
|
||||
{
|
||||
int count;
|
||||
int soundCount;
|
||||
int destX;
|
||||
int destY;
|
||||
int origX;
|
||||
int origY;
|
||||
int step;
|
||||
boolean flag; //true if the monster wants to return home
|
||||
|
||||
//GODZILLA FRAMES
|
||||
// 1...3 : northeast
|
||||
// 4...6 : southeast
|
||||
// 7...9 : southwest
|
||||
// 10..12 : northwest
|
||||
// 13 : north
|
||||
// 14 : east
|
||||
// 15 : south
|
||||
// 16 : west
|
||||
|
||||
// movement deltas
|
||||
static int [] Gx = { 2, 2, -2, -2, 0 };
|
||||
static int [] Gy = { -2, 2, 2, -2, 0 };
|
||||
|
||||
static int [] ND1 = { 0, 1, 2, 3 };
|
||||
static int [] ND2 = { 1, 2, 3, 0 };
|
||||
static int [] nn1 = { 2, 5, 8, 11 };
|
||||
static int [] nn2 = { 11, 2, 5, 8 };
|
||||
|
||||
public MonsterSprite(Micropolis engine, int xpos, int ypos)
|
||||
{
|
||||
super(engine, SpriteKind.GOD);
|
||||
this.x = xpos * 16 + 8;
|
||||
this.y = ypos * 16 + 8;
|
||||
this.width = 48;
|
||||
this.height = 48;
|
||||
this.offx = -24;
|
||||
this.offy = -24;
|
||||
|
||||
this.origX = x;
|
||||
this.origY = y;
|
||||
|
||||
this.frame = xpos > city.getWidth() / 2 ?
|
||||
(ypos > city.getHeight() / 2 ? 10 : 7) :
|
||||
(ypos > city.getHeight() / 2 ? 1 : 4);
|
||||
|
||||
this.count = 1000;
|
||||
CityLocation p = city.getLocationOfMaxPollution();
|
||||
this.destX = p.x * 16 + 8;
|
||||
this.destY = p.y * 16 + 8;
|
||||
this.flag = false;
|
||||
this.step = 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveImpl()
|
||||
{
|
||||
if (this.frame == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (soundCount > 0) {
|
||||
soundCount--;
|
||||
}
|
||||
|
||||
int d = (this.frame - 1) / 3; // basic direction
|
||||
int z = (this.frame - 1) % 3; // step index (only valid for d<4)
|
||||
|
||||
if (d < 4) { //turn n s e w
|
||||
assert step == -1 || step == 1;
|
||||
if (z == 2) step = -1;
|
||||
if (z == 0) step = 1;
|
||||
z += step;
|
||||
|
||||
if (getDis(x, y, destX, destY) < 60) {
|
||||
|
||||
// reached destination
|
||||
|
||||
if (!flag) {
|
||||
// destination was the pollution center;
|
||||
// now head for home
|
||||
flag = true;
|
||||
destX = origX;
|
||||
destY = origY;
|
||||
}
|
||||
else {
|
||||
// destination was origX, origY;
|
||||
// hide the sprite
|
||||
this.frame = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int c = getDir(x, y, destX, destY);
|
||||
c = (c - 1) / 2; //convert to one of four basic headings
|
||||
assert c >= 0 && c < 4;
|
||||
|
||||
if ((c != d) && city.PRNG.nextInt(11) == 0) {
|
||||
// randomly determine direction to turn
|
||||
if (city.PRNG.nextInt(2) == 0) {
|
||||
z = ND1[d];
|
||||
}
|
||||
else {
|
||||
z = ND2[d];
|
||||
}
|
||||
d = 4; //transition heading
|
||||
|
||||
if (soundCount == 0) {
|
||||
city.makeSound(x/16, y/16, Sound.MONSTER);
|
||||
soundCount = 50 + city.PRNG.nextInt(101);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert this.frame >= 13 && this.frame <= 16;
|
||||
|
||||
int z2 = (this.frame - 13) % 4;
|
||||
|
||||
if (city.PRNG.nextInt(4) == 0) {
|
||||
int newFrame;
|
||||
if (city.PRNG.nextInt(2) == 0) {
|
||||
newFrame = nn1[z2];
|
||||
} else {
|
||||
newFrame = nn2[z2];
|
||||
}
|
||||
d = (newFrame-1) / 3;
|
||||
z = (newFrame-1) % 3;
|
||||
|
||||
assert d < 4;
|
||||
}
|
||||
else {
|
||||
d = 4;
|
||||
}
|
||||
}
|
||||
|
||||
this.frame = ((d * 3) + z) + 1;
|
||||
|
||||
assert this.frame >= 1 && this.frame <= 16;
|
||||
|
||||
this.x += Gx[d];
|
||||
this.y += Gy[d];
|
||||
|
||||
if (this.count > 0) {
|
||||
this.count--;
|
||||
}
|
||||
|
||||
int c = getChar(x, y);
|
||||
if (c == -1 ||
|
||||
(c == RIVER && this.count != 0 && false)
|
||||
) {
|
||||
this.frame = 0; //kill zilla
|
||||
}
|
||||
|
||||
for (Sprite s : city.allSprites())
|
||||
{
|
||||
if (checkSpriteCollision(s) &&
|
||||
(s.kind == SpriteKind.AIR ||
|
||||
s.kind == SpriteKind.COP ||
|
||||
s.kind == SpriteKind.SHI ||
|
||||
s.kind == SpriteKind.TRA)
|
||||
) {
|
||||
s.explodeSprite();
|
||||
}
|
||||
}
|
||||
|
||||
destroyTile(x / 16, y / 16);
|
||||
}
|
||||
}
|
136
src/micropolisj/engine/ShipSprite.java
Normal file
136
src/micropolisj/engine/ShipSprite.java
Normal file
|
@ -0,0 +1,136 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
import static micropolisj.engine.TileConstants.*;
|
||||
|
||||
public class ShipSprite extends Sprite
|
||||
{
|
||||
static int [] BDx = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
|
||||
static int [] BDy = { 0, -1, -1, 0, 1, 1, 1, 0, -1 };
|
||||
static int [] BPx = { 0, 0, 2, 2, 2, 0, -2, -2, -2 };
|
||||
static int [] BPy = { 0, -2, -2, 0, 2, 2, 2, 0, -2 };
|
||||
static int [] BtClrTab = { RIVER, CHANNEL, POWERBASE, POWERBASE+1,
|
||||
RAILBASE, RAILBASE+1, BRWH, BRWV };
|
||||
|
||||
int newDir;
|
||||
int count;
|
||||
int soundCount;
|
||||
|
||||
public static final int NORTH_EDGE = 5;
|
||||
public static final int EAST_EDGE = 7;
|
||||
public static final int SOUTH_EDGE = 1;
|
||||
public static final int WEST_EDGE = 3;
|
||||
|
||||
public ShipSprite(Micropolis engine, int xpos, int ypos, int edge)
|
||||
{
|
||||
super(engine, SpriteKind.SHI);
|
||||
this.x = xpos * 16 + 8;
|
||||
this.y = ypos * 16 + 8;
|
||||
this.width = 48;
|
||||
this.height = 48;
|
||||
this.offx = -24;
|
||||
this.offy = -24;
|
||||
this.frame = edge;
|
||||
this.newDir = edge;
|
||||
this.dir = 10;
|
||||
this.count = 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveImpl()
|
||||
{
|
||||
int t = RIVER;
|
||||
|
||||
this.soundCount--;
|
||||
if (this.soundCount <= 0) {
|
||||
if (city.PRNG.nextInt(4) == 0) {
|
||||
city.makeSound(x/16,y/16,Sound.HONKHONK_LOW);
|
||||
}
|
||||
this.soundCount = 200;
|
||||
}
|
||||
|
||||
this.count--;
|
||||
if (this.count <= 0) {
|
||||
this.count = 9;
|
||||
if (this.newDir != this.frame) {
|
||||
this.frame = turnTo(this.frame, this.newDir);
|
||||
return;
|
||||
}
|
||||
int tem = city.PRNG.nextInt(8);
|
||||
int pem;
|
||||
for (pem = tem; pem < (tem + 8); pem++) {
|
||||
int z = (pem % 8) + 1;
|
||||
if (z == this.dir)
|
||||
continue;
|
||||
|
||||
int xpos = this.x / 16 + BDx[z];
|
||||
int ypos = this.y / 16 + BDy[z];
|
||||
|
||||
if (city.testBounds(xpos, ypos)) {
|
||||
t = city.getTile(xpos, ypos) & LOMASK;
|
||||
if ((t == CHANNEL) || (t == BRWH) || (t == BRWV) ||
|
||||
tryOther(t, this.dir, z))
|
||||
{
|
||||
this.newDir = z;
|
||||
this.frame = turnTo(this.frame, this.newDir);
|
||||
this.dir = z + 4;
|
||||
if (this.dir > 8) { this.dir -= 8; }
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pem == (tem + 8)) {
|
||||
this.dir = 10;
|
||||
this.newDir = city.PRNG.nextInt(8)+1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int z = this.frame;
|
||||
if (z == this.newDir) {
|
||||
this.x += BPx[z];
|
||||
this.y += BPy[z];
|
||||
}
|
||||
}
|
||||
|
||||
if (!spriteInBounds()) {
|
||||
this.frame = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
boolean found = false;
|
||||
for (int z : BtClrTab) {
|
||||
if (t == z) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
explodeSprite();
|
||||
destroyTile(x/16, y/16);
|
||||
}
|
||||
}
|
||||
|
||||
boolean tryOther(int tile, int oldDir, int newDir)
|
||||
{
|
||||
int z = oldDir + 4;
|
||||
if (z > 8) z -= 8;
|
||||
if (newDir != z) return false;
|
||||
|
||||
return (tile == POWERBASE || tile == POWERBASE+1 ||
|
||||
tile == RAILBASE || tile == RAILBASE+1);
|
||||
}
|
||||
|
||||
boolean spriteInBounds()
|
||||
{
|
||||
int xpos = x / 16;
|
||||
int ypos = y / 16;
|
||||
return city.testBounds(xpos, ypos);
|
||||
}
|
||||
}
|
47
src/micropolisj/engine/Sound.java
Normal file
47
src/micropolisj/engine/Sound.java
Normal file
|
@ -0,0 +1,47 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Enumerates the various sounds that the city may produce.
|
||||
* The engine is not responsible for actually playing the sound. That task
|
||||
* belongs to the front-end (i.e. the user interface).
|
||||
*/
|
||||
public enum Sound
|
||||
{
|
||||
EXPLOSION_LOW ("explosion-low"),
|
||||
EXPLOSION_HIGH("explosion-high"),
|
||||
EXPLOSION_BOTH("explosion-low"),
|
||||
UHUH ("bop"),
|
||||
SORRY ("bop"),
|
||||
BUILD ("layzone"),
|
||||
BULLDOZE (null),
|
||||
HONKHONK_LOW ("honkhonk-low"),
|
||||
HONKHONK_MED ("honkhonk-med"),
|
||||
HONKHONK_HIGH ("honkhonk-high"),
|
||||
HONKHONK_HI ("honkhonk-hi"),
|
||||
SIREN ("siren"),
|
||||
HEAVYTRAFFIC ("heavytraffic"),
|
||||
MONSTER ("zombie-roar-5");
|
||||
|
||||
String wavName;
|
||||
private Sound(String wavName)
|
||||
{
|
||||
this.wavName = wavName;
|
||||
}
|
||||
|
||||
public URL getAudioFile()
|
||||
{
|
||||
String n2 = "/sounds/"+wavName+".wav";
|
||||
URL u = Sound.class.getResource(n2);
|
||||
return u;
|
||||
}
|
||||
}
|
37
src/micropolisj/engine/Speed.java
Normal file
37
src/micropolisj/engine/Speed.java
Normal file
|
@ -0,0 +1,37 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
/**
|
||||
* Lists the simulation speeds available.
|
||||
*/
|
||||
public enum Speed
|
||||
{
|
||||
PAUSED ( 999,999, 0),
|
||||
SLOW ( 500, 5, 1), //one step every 2500 ms
|
||||
NORMAL ( 125, 2, 1), //one step every 250 ms
|
||||
FAST ( 50, 1, 2), //one step every 25 ms
|
||||
SUPER_FAST( 25, 1, 10); //one step every 2.5 ms
|
||||
|
||||
/** The animation speed, expressed as an interval in milliseconds. */
|
||||
public final int animationDelay;
|
||||
/** For slower speeds, how many animation occur for every simulation step.
|
||||
* Faster speeds should set this to one. */
|
||||
public final int aniFramesPerStep;
|
||||
/** For faster speeds, how many simulation steps should occur for every
|
||||
* update to the screen. */
|
||||
public final int simStepsPerUpdate;
|
||||
|
||||
private Speed(int delay, int aniFrames, int simSteps)
|
||||
{
|
||||
this.animationDelay = delay;
|
||||
this.aniFramesPerStep = aniFrames;
|
||||
this.simStepsPerUpdate = simSteps;
|
||||
}
|
||||
}
|
201
src/micropolisj/engine/Sprite.java
Normal file
201
src/micropolisj/engine/Sprite.java
Normal file
|
@ -0,0 +1,201 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
import static micropolisj.engine.TileConstants.*;
|
||||
|
||||
public abstract class Sprite
|
||||
{
|
||||
Micropolis city;
|
||||
|
||||
//TODO- enforce read-only nature of the following properties
|
||||
// (i.e. do not let them be modified directly by other classes)
|
||||
|
||||
public SpriteKind kind;
|
||||
|
||||
public int offx;
|
||||
public int offy;
|
||||
public int width = 32;
|
||||
public int height = 32;
|
||||
|
||||
public int frame;
|
||||
public int x;
|
||||
public int y;
|
||||
|
||||
public int lastX;
|
||||
public int lastY;
|
||||
|
||||
int dir;
|
||||
|
||||
protected Sprite(Micropolis engine, SpriteKind kind)
|
||||
{
|
||||
this.city = engine;
|
||||
this.kind = kind;
|
||||
}
|
||||
|
||||
protected final int getChar(int x, int y)
|
||||
{
|
||||
int xpos = x / 16;
|
||||
int ypos = y / 16;
|
||||
if (city.testBounds(xpos, ypos)) {
|
||||
return (city.getTile(xpos, ypos) & LOMASK);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For subclasses to override. Actually does the movement and animation
|
||||
* of this particular sprite. Setting this.frame to zero will cause the
|
||||
* sprite to be unallocated.
|
||||
*/
|
||||
protected abstract void moveImpl();
|
||||
|
||||
public final void move()
|
||||
{
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
moveImpl();
|
||||
city.fireSpriteMoved(this);
|
||||
}
|
||||
|
||||
public final boolean isVisible()
|
||||
{
|
||||
return this.frame != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes direction from one point to another.
|
||||
* @return integer between 1 and 8, with
|
||||
* 1 == north,
|
||||
* 3 == east,
|
||||
* 5 == south,
|
||||
* 7 == west.
|
||||
*/
|
||||
static final int getDir(int orgX, int orgY, int desX, int desY)
|
||||
{
|
||||
final int Gdtab [] = { 0, 3, 2, 1, 3, 4, 5, 7, 6, 5, 7, 8, 1 };
|
||||
int dispX = desX - orgX;
|
||||
int dispY = desY - orgY;
|
||||
|
||||
int z = dispX < 0 ? (dispY < 0 ? 11 : 8) : (dispY < 0 ? 2 : 5);
|
||||
|
||||
dispX = Math.abs(dispX);
|
||||
dispY = Math.abs(dispY);
|
||||
int absDist = dispX + dispY;
|
||||
|
||||
if (dispX * 2 < dispY) z++;
|
||||
else if (dispY * 2 < dispX) z--;
|
||||
|
||||
if (z >= 1 && z <= 12) {
|
||||
return Gdtab[z];
|
||||
}
|
||||
else {
|
||||
assert false;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes manhatten distance between two points.
|
||||
*/
|
||||
static final int getDis(int x0, int y0, int x1, int y1)
|
||||
{
|
||||
return Math.abs(x0-x1) + Math.abs(y0-y1);
|
||||
}
|
||||
|
||||
final void explodeSprite()
|
||||
{
|
||||
this.frame = 0;
|
||||
|
||||
city.makeExplosionAt(x, y);
|
||||
int xpos = x/16;
|
||||
int ypos = y/16;
|
||||
|
||||
switch (kind) {
|
||||
case AIR:
|
||||
city.crashLocation = new CityLocation(xpos, ypos);
|
||||
city.sendMessageAtPic(MicropolisMessage.PLANECRASH_REPORT, xpos, ypos);
|
||||
break;
|
||||
case SHI:
|
||||
city.crashLocation = new CityLocation(xpos, ypos);
|
||||
city.sendMessageAtPic(MicropolisMessage.SHIPWRECK_REPORT, xpos, ypos);
|
||||
break;
|
||||
case TRA:
|
||||
case BUS:
|
||||
city.crashLocation = new CityLocation(xpos, ypos);
|
||||
city.sendMessageAtPic(MicropolisMessage.TRAIN_CRASH_REPORT, xpos, ypos);
|
||||
break;
|
||||
case COP:
|
||||
city.crashLocation = new CityLocation(xpos, ypos);
|
||||
city.sendMessageAtPic(MicropolisMessage.COPTER_CRASH_REPORT, xpos, ypos);
|
||||
break;
|
||||
}
|
||||
|
||||
city.makeSound(xpos, ypos, Sound.EXPLOSION_HIGH);
|
||||
}
|
||||
|
||||
final boolean checkSpriteCollision(Sprite otherSprite)
|
||||
{
|
||||
if (!isVisible()) return false;
|
||||
if (!otherSprite.isVisible()) return false;
|
||||
|
||||
return (getDis(this.x, this.y, otherSprite.x, otherSprite.y) < 30);
|
||||
}
|
||||
|
||||
final void destroyTile(int xpos, int ypos)
|
||||
{
|
||||
if (!city.testBounds(xpos, ypos))
|
||||
return;
|
||||
|
||||
int z = city.getTile(xpos, ypos);
|
||||
int t = z & LOMASK;
|
||||
|
||||
if (t >= TREEBASE) {
|
||||
if (TileConstants.isBridge(z)) {
|
||||
city.setTile(xpos, ypos, RIVER);
|
||||
return;
|
||||
}
|
||||
if ((z & BURNBIT) == 0) {
|
||||
return; //cannot destroy it
|
||||
}
|
||||
if ((z & ZONEBIT) != 0) {
|
||||
city.fireZone(xpos, ypos, z);
|
||||
if (t > RZB) {
|
||||
city.makeExplosion(xpos, ypos);
|
||||
}
|
||||
}
|
||||
if (TileConstants.checkWet(t)) {
|
||||
city.setTile(xpos, ypos, RIVER);
|
||||
}
|
||||
else {
|
||||
city.setTile(xpos, ypos,
|
||||
(char) (TINYEXP | BULLBIT | ANIMBIT));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final int turnTo(int p, int d)
|
||||
{
|
||||
if (p == d)
|
||||
return p;
|
||||
if (p < d) {
|
||||
if (d - p < 4) p++;
|
||||
else p--;
|
||||
}
|
||||
else {
|
||||
if (p - d < 4) p--;
|
||||
else p++;
|
||||
}
|
||||
if (p > 8) return 1;
|
||||
if (p < 1) return 8;
|
||||
return p;
|
||||
}
|
||||
|
||||
}
|
33
src/micropolisj/engine/SpriteKind.java
Normal file
33
src/micropolisj/engine/SpriteKind.java
Normal file
|
@ -0,0 +1,33 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
/**
|
||||
* Enumeration of the various kinds of sprites that may appear in the city.
|
||||
*/
|
||||
public enum SpriteKind
|
||||
{
|
||||
TRA(1,5),
|
||||
COP(2,8),
|
||||
AIR(3,11),
|
||||
SHI(4,8),
|
||||
GOD(5,16),
|
||||
TOR(6,3),
|
||||
EXP(7,6),
|
||||
BUS(8,4);
|
||||
|
||||
public final int objectId;
|
||||
public final int numFrames;
|
||||
|
||||
private SpriteKind(int objectId, int numFrames)
|
||||
{
|
||||
this.objectId = objectId;
|
||||
this.numFrames = numFrames;
|
||||
}
|
||||
}
|
327
src/micropolisj/engine/TileConstants.java
Normal file
327
src/micropolisj/engine/TileConstants.java
Normal file
|
@ -0,0 +1,327 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class TileConstants
|
||||
{
|
||||
//
|
||||
// terrain mapping
|
||||
//
|
||||
public static final char DIRT = 0;
|
||||
public static final char RIVER = 2;
|
||||
public static final char REDGE = 3;
|
||||
public static final char CHANNEL = 4;
|
||||
public static final char FIRSTRIVEDGE = 5;
|
||||
public static final char LASTRIVEDGE = 20;
|
||||
public static final char TREEBASE = 21;
|
||||
public static final char WOODS_LOW = TREEBASE;
|
||||
public static final char WOODS = 37;
|
||||
public static final char UNUSED_TRASH2 = 39;
|
||||
public static final char WOODS_HIGH = UNUSED_TRASH2;
|
||||
public static final char WOODS2 = 40;
|
||||
public static final char WOODS5 = 43;
|
||||
public static final char RUBBLE = 44;
|
||||
public static final char LASTRUBBLE = 47;
|
||||
public static final char FLOOD = 48;
|
||||
public static final char LASTFLOOD = 51;
|
||||
public static final char RADTILE = 52;
|
||||
public static final char FIRE = 56;
|
||||
public static final char FIREBASE = 56;
|
||||
public static final char ROADBASE = 64;
|
||||
public static final char HBRIDGE = 64;
|
||||
public static final char VBRIDGE = 65;
|
||||
public static final char ROADS = 66;
|
||||
public static final char ROADS2 = 67;
|
||||
public static final char ROADS3 = 68;
|
||||
public static final char ROADS4 = 69;
|
||||
public static final char ROADS5 = 70;
|
||||
public static final char ROADS6 = 71;
|
||||
public static final char ROADS7 = 72;
|
||||
public static final char ROADS8 = 73;
|
||||
public static final char ROADS9 = 74;
|
||||
public static final char ROADS10 = 75;
|
||||
public static final char INTERSECTION = 76;
|
||||
public static final char HROADPOWER = 77;
|
||||
public static final char VROADPOWER = 78;
|
||||
public static final char BRWH = 79;
|
||||
public static final char LTRFBASE = 80;
|
||||
public static final char BRWV = 95;
|
||||
public static final char HTRFBASE = 144;
|
||||
public static final char LASTROAD = 206;
|
||||
public static final char POWERBASE = 208;
|
||||
public static final char HPOWER = 208;
|
||||
public static final char VPOWER = 209;
|
||||
public static final char LHPOWER = 210;
|
||||
public static final char LVPOWER = 211;
|
||||
public static final char LVPOWER2 = 212;
|
||||
public static final char LVPOWER3 = 213;
|
||||
public static final char LVPOWER4 = 214;
|
||||
public static final char LVPOWER5 = 215;
|
||||
public static final char LVPOWER6 = 216;
|
||||
public static final char LVPOWER7 = 217;
|
||||
public static final char LVPOWER8 = 218;
|
||||
public static final char LVPOWER9 = 219;
|
||||
public static final char LVPOWER10 = 220;
|
||||
public static final char RAILHPOWERV = 221;
|
||||
public static final char RAILVPOWERH = 222;
|
||||
public static final char LASTPOWER = 222;
|
||||
public static final char RAILBASE = 224;
|
||||
public static final char HRAIL = 224;
|
||||
public static final char VRAIL = 225;
|
||||
public static final char LHRAIL = 226;
|
||||
public static final char LVRAIL = 227;
|
||||
public static final char LVRAIL2 = 228;
|
||||
public static final char LVRAIL3 = 229;
|
||||
public static final char LVRAIL4 = 230;
|
||||
public static final char LVRAIL5 = 231;
|
||||
public static final char LVRAIL6 = 232;
|
||||
public static final char LVRAIL7 = 233;
|
||||
public static final char LVRAIL8 = 234;
|
||||
public static final char LVRAIL9 = 235;
|
||||
public static final char LVRAIL10 = 236;
|
||||
public static final char HRAILROAD = 237;
|
||||
public static final char VRAILROAD = 238;
|
||||
public static final char LASTRAIL = 238;
|
||||
public static final char RESBASE = 240;
|
||||
public static final char FREEZ = 244; //free zone?
|
||||
public static final char HOUSE = 249;
|
||||
public static final char LHTHR = 249; //12 house tiles
|
||||
public static final char HHTHR = 260;
|
||||
public static final char RZB = 265; //residential zone base
|
||||
public static final char HOSPITAL = 409;
|
||||
public static final char CHURCH = 418;
|
||||
public static final char COMBASE = 423;
|
||||
public static final char COMCLR = 427;
|
||||
public static final char CZB = 436; //commercial zone base
|
||||
public static final char COMLAST = 609;
|
||||
public static final char INDBASE = 612;
|
||||
public static final char INDCLR = 616;
|
||||
public static final char LASTIND = 620;
|
||||
public static final char IND1 = 621;
|
||||
public static final char IZB = 625;
|
||||
public static final char IND2 = 641;
|
||||
public static final char IND3 = 644;
|
||||
public static final char IND4 = 649;
|
||||
public static final char IND5 = 650;
|
||||
public static final char IND6 = 676;
|
||||
public static final char IND7 = 677;
|
||||
public static final char IND8 = 686;
|
||||
public static final char IND9 = 689;
|
||||
public static final char PORTBASE = 693;
|
||||
public static final char PORT = 698;
|
||||
public static final char LASTPORT = 708;
|
||||
public static final char AIRPORTBASE = 709;
|
||||
public static final char RADAR = 711;
|
||||
public static final char AIRPORT = 716;
|
||||
public static final char COALBASE = 745;
|
||||
public static final char POWERPLANT = 750;
|
||||
public static final char LASTPOWERPLANT = 760;
|
||||
public static final char FIRESTBASE = 761;
|
||||
public static final char FIRESTATION = 765;
|
||||
public static final char POLICESTBASE = 770;
|
||||
public static final char POLICESTATION = 774;
|
||||
public static final char STADIUMBASE = 779;
|
||||
public static final char STADIUM = 784;
|
||||
public static final char FULLSTADIUM = 800;
|
||||
public static final char NUCLEARBASE = 811;
|
||||
public static final char NUCLEAR = 816;
|
||||
public static final char LASTZONE = 826;
|
||||
public static final char LIGHTNINGBOLT = 827;
|
||||
public static final char HBRDG0 = 828;
|
||||
public static final char HBRDG1 = 829;
|
||||
public static final char HBRDG2 = 830;
|
||||
public static final char HBRDG3 = 831;
|
||||
public static final char RADAR_ANIM = 832;
|
||||
public static final char FOUNTAIN = 840;
|
||||
public static final char INDBASE2 = 844;
|
||||
public static final char SMOKEBASE = 852;
|
||||
public static final char TINYEXP = 860;
|
||||
public static final char SOMETINYEXP = 864;
|
||||
public static final char LASTTINYEXP = 867;
|
||||
public static final char COALSMOKE1 = 916;
|
||||
public static final char COALSMOKE2 = 920;
|
||||
public static final char COALSMOKE3 = 924;
|
||||
public static final char COALSMOKE4 = 928;
|
||||
public static final char FOOTBALLGAME1 = 932;
|
||||
public static final char FOOTBALLGAME2 = 940;
|
||||
public static final char VBRDG0 = 948;
|
||||
public static final char VBRDG1 = 949;
|
||||
public static final char VBRDG2 = 950;
|
||||
public static final char VBRDG3 = 951;
|
||||
public static final char URANIUM_FUEL = 952;
|
||||
public static final char LAST_TILE = 956;
|
||||
|
||||
static final char [] RoadTable = new char[] {
|
||||
ROADS, ROADS2, ROADS, ROADS3,
|
||||
ROADS2, ROADS2, ROADS4, ROADS8,
|
||||
ROADS, ROADS6, ROADS, ROADS7,
|
||||
ROADS5, ROADS10, ROADS9, INTERSECTION
|
||||
};
|
||||
|
||||
static final char [] RailTable = new char[] {
|
||||
LHRAIL, LVRAIL, LHRAIL, LVRAIL2,
|
||||
LVRAIL, LVRAIL, LVRAIL3, LVRAIL7,
|
||||
LHRAIL, LVRAIL5, LHRAIL, LVRAIL6,
|
||||
LVRAIL4, LVRAIL9, LVRAIL8, LVRAIL10
|
||||
};
|
||||
|
||||
static final char [] WireTable = new char[] {
|
||||
LHPOWER, LVPOWER, LHPOWER, LVPOWER2,
|
||||
LVPOWER, LVPOWER, LVPOWER3, LVPOWER7,
|
||||
LHPOWER, LVPOWER5, LHPOWER, LVPOWER6,
|
||||
LVPOWER4, LVPOWER9, LVPOWER8, LVPOWER10
|
||||
};
|
||||
|
||||
//
|
||||
// status bits
|
||||
//
|
||||
public static final char PWRBIT = 32768; // bit 15 ... currently powered
|
||||
public static final char CONDBIT = 16384; // bit 14 ... can conduct power
|
||||
public static final char BURNBIT = 8192; // bit 13 ... is combustible
|
||||
public static final char BULLBIT = 4096; // bit 12 ... is bulldozable
|
||||
public static final char ANIMBIT = 2048; // bit 11 ... animates
|
||||
public static final char ZONEBIT = 1024; // bit 10 ... is the special tile for a zone
|
||||
|
||||
public static final char ALLBITS = 64512; // mask for upper 6 bits
|
||||
public static final char LOMASK = 1023; //mask for low 10 bits
|
||||
|
||||
public static final char BLBNBIT = (BULLBIT | BURNBIT);
|
||||
public static final char BLBNCNBIT = (BULLBIT | BURNBIT | CONDBIT);
|
||||
public static final char BNCNBIT = (BURNBIT | CONDBIT);
|
||||
|
||||
private TileConstants() {}
|
||||
|
||||
private static int [] buildingBases = {
|
||||
DIRT, RIVER, TREEBASE, RUBBLE,
|
||||
FLOOD, RADTILE, FIRE, ROADBASE,
|
||||
POWERBASE, RAILBASE, RESBASE, COMBASE,
|
||||
INDBASE, PORTBASE, AIRPORTBASE, COALBASE,
|
||||
FIRESTBASE, POLICESTBASE, STADIUMBASE, NUCLEARBASE,
|
||||
HBRDG0, RADAR_ANIM, FOUNTAIN, INDBASE2,
|
||||
FOOTBALLGAME1, VBRDG0, URANIUM_FUEL, LAST_TILE
|
||||
};
|
||||
|
||||
//used by queryZoneStatus
|
||||
public static int getBuildingId(int tile)
|
||||
{
|
||||
tile &= LOMASK;
|
||||
int i = Arrays.binarySearch(buildingBases, tile);
|
||||
if (i >= 0) {
|
||||
return i;
|
||||
} else {
|
||||
return -i - 2;
|
||||
}
|
||||
}
|
||||
|
||||
//used by setFire()
|
||||
public static boolean isArsonable(int tile)
|
||||
{
|
||||
return (
|
||||
(tile & ZONEBIT) == 0 &&
|
||||
(tile & LOMASK) >= LHTHR &&
|
||||
(tile & LOMASK) <= LASTZONE
|
||||
);
|
||||
}
|
||||
|
||||
//used by Sprite::destroyTile
|
||||
public static boolean isBridge(int tile)
|
||||
{
|
||||
return (((tile & LOMASK) >= ROADBASE && (tile & LOMASK) <= LASTROAD)
|
||||
&& ((tile & BURNBIT) == 0));
|
||||
}
|
||||
|
||||
public static boolean isOverWater(char cell)
|
||||
{
|
||||
switch (cell & LOMASK)
|
||||
{
|
||||
case HBRIDGE:
|
||||
case VBRIDGE:
|
||||
case BRWV:
|
||||
case BRWH:
|
||||
case HBRDG0:
|
||||
case HBRDG1:
|
||||
case HBRDG2:
|
||||
case HBRDG3:
|
||||
case VBRDG0:
|
||||
case VBRDG1:
|
||||
case VBRDG2:
|
||||
case VBRDG3:
|
||||
case HPOWER:
|
||||
case VPOWER:
|
||||
case HRAIL:
|
||||
case VRAIL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isRubble(char cell)
|
||||
{
|
||||
return (((cell & LOMASK) >= RUBBLE) &&
|
||||
((cell & LOMASK) <= LASTRUBBLE));
|
||||
}
|
||||
|
||||
public static boolean isTree(char cell)
|
||||
{
|
||||
return (((cell & LOMASK) >= WOODS_LOW) &&
|
||||
((cell & LOMASK) <= WOODS_HIGH));
|
||||
}
|
||||
|
||||
//used by makeEarthquake
|
||||
public static boolean isVulnerable(int tile)
|
||||
{
|
||||
int tem2 = tile & LOMASK;
|
||||
if (tem2 < RESBASE ||
|
||||
tem2 > LASTZONE ||
|
||||
(tile & ZONEBIT) != 0
|
||||
) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean checkWet(int tile)
|
||||
{
|
||||
int x = tile & LOMASK;
|
||||
return (x == POWERBASE ||
|
||||
x == POWERBASE+1 ||
|
||||
x == RAILBASE ||
|
||||
x == RAILBASE + 1 ||
|
||||
x == BRWH ||
|
||||
x == BRWV);
|
||||
}
|
||||
|
||||
public static int getZoneSizeFor(int tile)
|
||||
{
|
||||
int ch = tile & LOMASK;
|
||||
if (ch < PORTBASE) {
|
||||
return 3;
|
||||
}
|
||||
else if (ch == AIRPORT) {
|
||||
return 6;
|
||||
}
|
||||
else {
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isRiverEdge(int tile)
|
||||
{
|
||||
return (tile & LOMASK) > 4 && (tile & LOMASK) < 21;
|
||||
}
|
||||
|
||||
static boolean isFloodable(int tile)
|
||||
{
|
||||
return (tile == DIRT || ((tile & BULLBIT) != 0 && (tile & BURNBIT) != 0));
|
||||
}
|
||||
}
|
20
src/micropolisj/engine/ToolResult.java
Normal file
20
src/micropolisj/engine/ToolResult.java
Normal file
|
@ -0,0 +1,20 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
/**
|
||||
* Lists the various results that may occur when applying a tool.
|
||||
*/
|
||||
public enum ToolResult
|
||||
{
|
||||
SUCCESS, // 1
|
||||
NONE, // 0
|
||||
UH_OH, // -1; invalid position
|
||||
INSUFFICIENT_FUNDS; // -2
|
||||
}
|
82
src/micropolisj/engine/TornadoSprite.java
Normal file
82
src/micropolisj/engine/TornadoSprite.java
Normal file
|
@ -0,0 +1,82 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
public class TornadoSprite extends Sprite
|
||||
{
|
||||
static int [] CDx = { 2, 3, 2, 0, -2, -3 };
|
||||
static int [] CDy = { -2, 0, 2, 3, 2, 0 };
|
||||
|
||||
boolean flag;
|
||||
int count;
|
||||
|
||||
public TornadoSprite(Micropolis engine, int xpos, int ypos)
|
||||
{
|
||||
super(engine, SpriteKind.TOR);
|
||||
this.x = xpos * 16 + 8;
|
||||
this.y = ypos * 16 + 8;
|
||||
this.width = 48;
|
||||
this.height = 48;
|
||||
this.offx = -24;
|
||||
this.offy = -40;
|
||||
|
||||
this.frame = 1;
|
||||
this.count = 200;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveImpl()
|
||||
{
|
||||
int z = this.frame;
|
||||
|
||||
if (z == 2) {
|
||||
//cycle animation
|
||||
|
||||
if (this.flag)
|
||||
z = 3;
|
||||
else
|
||||
z = 1;
|
||||
}
|
||||
else {
|
||||
this.flag = (z == 1);
|
||||
z = 2;
|
||||
}
|
||||
|
||||
if (this.count > 0) {
|
||||
this.count--;
|
||||
}
|
||||
|
||||
this.frame = z;
|
||||
|
||||
for (Sprite s : city.allSprites()) {
|
||||
if (checkSpriteCollision(s) &&
|
||||
(s.kind == SpriteKind.AIR ||
|
||||
s.kind == SpriteKind.COP ||
|
||||
s.kind == SpriteKind.SHI ||
|
||||
s.kind == SpriteKind.TRA)
|
||||
) {
|
||||
s.explodeSprite();
|
||||
}
|
||||
}
|
||||
|
||||
int zz = city.PRNG.nextInt(CDx.length);
|
||||
x += CDx[zz];
|
||||
y += CDy[zz];
|
||||
|
||||
if (!city.testBounds(x/16, y/16)) {
|
||||
// out of bounds
|
||||
this.frame = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME- the original code checks here for an ending condition
|
||||
|
||||
destroyTile(x/16, y/16);
|
||||
}
|
||||
}
|
245
src/micropolisj/engine/TrafficGen.java
Normal file
245
src/micropolisj/engine/TrafficGen.java
Normal file
|
@ -0,0 +1,245 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
import java.util.*;
|
||||
import static micropolisj.engine.TileConstants.*;
|
||||
|
||||
class TrafficGen
|
||||
{
|
||||
Micropolis engine;
|
||||
int mapX;
|
||||
int mapY;
|
||||
Micropolis.ZoneType sourceZone;
|
||||
|
||||
int lastdir;
|
||||
Stack<Position> positions = new Stack<>();
|
||||
|
||||
static final int MAX_TRAFFIC_DISTANCE = 30;
|
||||
|
||||
public TrafficGen(Micropolis engine)
|
||||
{
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
int makeTraffic()
|
||||
{
|
||||
if (findPerimeterRoad()) //look for road on this zone's perimeter
|
||||
{
|
||||
if (tryDrive()) //attempt to drive somewhere
|
||||
{
|
||||
// success; incr trafdensity
|
||||
setTrafficMem();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no road found
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void setTrafficMem()
|
||||
{
|
||||
while (!positions.isEmpty())
|
||||
{
|
||||
Position pos = positions.pop();
|
||||
mapX = pos.x;
|
||||
mapY = pos.y;
|
||||
assert engine.testBounds(mapX, mapY);
|
||||
|
||||
int tile = engine.getTile(mapX, mapY) & LOMASK;
|
||||
if (tile >= ROADBASE && tile < POWERBASE)
|
||||
{
|
||||
// check for rail
|
||||
int z = engine.trfDensity[mapY/2][mapX/2];
|
||||
z += 50;
|
||||
|
||||
if (z > 240 && engine.PRNG.nextInt(6) == 0)
|
||||
{
|
||||
z = 240;
|
||||
engine.trafficMaxLocationX = mapX;
|
||||
engine.trafficMaxLocationY = mapY;
|
||||
|
||||
HelicopterSprite copter = (HelicopterSprite) engine.getSprite(SpriteKind.COP);
|
||||
if (copter != null) {
|
||||
copter.destX = mapX;
|
||||
copter.destY = mapY;
|
||||
}
|
||||
}
|
||||
|
||||
engine.trfDensity[mapY/2][mapX/2] = z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final int [] PerimX = { -1, 0, 1, 2, 2, 2, 1, 0,-1, -2,-2,-2 };
|
||||
static final int [] PerimY = { -2,-2,-2, -1, 0, 1, 2, 2, 2, 1, 0,-1 };
|
||||
boolean findPerimeterRoad()
|
||||
{
|
||||
for (int z = 0; z < 12; z++)
|
||||
{
|
||||
int tx = mapX + PerimX[z];
|
||||
int ty = mapY + PerimY[z];
|
||||
|
||||
if (engine.testBounds(tx, ty)
|
||||
&& roadTest(tx, ty))
|
||||
{
|
||||
mapX = tx;
|
||||
mapY = ty;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean roadTest(int tx, int ty)
|
||||
{
|
||||
char c = engine.getTile(tx, ty);
|
||||
c &= LOMASK;
|
||||
|
||||
if (c < ROADBASE)
|
||||
return false;
|
||||
else if (c > LASTRAIL)
|
||||
return false;
|
||||
else if (c >= POWERBASE && c < LASTPOWER)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean tryDrive()
|
||||
{
|
||||
lastdir = 5;
|
||||
positions.clear();
|
||||
|
||||
for (int z = 0; z < MAX_TRAFFIC_DISTANCE; z++) //maximum distance to try
|
||||
{
|
||||
if (tryGo(z))
|
||||
{
|
||||
// got a road
|
||||
if (driveDone())
|
||||
{
|
||||
// destination reached
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// deadend, try backing up
|
||||
if (!positions.isEmpty())
|
||||
{
|
||||
positions.pop();
|
||||
z += 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// gone maxdis
|
||||
return false;
|
||||
}
|
||||
|
||||
static final int [] DX = { 0, 1, 0, -1 };
|
||||
static final int [] DY = { -1, 0, 1, 0 };
|
||||
boolean tryGo(int z)
|
||||
{
|
||||
// random starting direction
|
||||
int rdir = engine.PRNG.nextInt(4);
|
||||
|
||||
for (int d = rdir; d < rdir + 4; d++)
|
||||
{
|
||||
int realdir = d % 4;
|
||||
if (realdir == lastdir)
|
||||
continue;
|
||||
|
||||
if (roadTest(mapX + DX[realdir], mapY + DY[realdir]))
|
||||
{
|
||||
mapX += DX[realdir];
|
||||
mapY += DY[realdir];
|
||||
lastdir = (realdir + 2) % 4;
|
||||
|
||||
if (z % 2 == 1)
|
||||
{
|
||||
// save pos every other move
|
||||
positions.push(new Position(mapX, mapY));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static class Position
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
Position(int x, int y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
boolean driveDone()
|
||||
{
|
||||
int low, high;
|
||||
switch (sourceZone)
|
||||
{
|
||||
case RESIDENTIAL:
|
||||
low = COMBASE;
|
||||
high = NUCLEAR;
|
||||
break;
|
||||
case COMMERCIAL:
|
||||
low = LHTHR;
|
||||
high = PORT;
|
||||
break;
|
||||
case INDUSTRIAL:
|
||||
low = LHTHR;
|
||||
high = COMBASE;
|
||||
break;
|
||||
default:
|
||||
throw new Error("unreachable");
|
||||
}
|
||||
|
||||
if (mapY > 0)
|
||||
{
|
||||
int tile = engine.getTile(mapX, mapY-1) & LOMASK;
|
||||
if (tile >= low && tile <= high)
|
||||
return true;
|
||||
}
|
||||
if (mapX + 1 < engine.getWidth())
|
||||
{
|
||||
int tile = engine.getTile(mapX + 1, mapY) & LOMASK;
|
||||
if (tile >= low && tile <= high)
|
||||
return true;
|
||||
}
|
||||
if (mapY + 1 < engine.getHeight())
|
||||
{
|
||||
int tile = engine.getTile(mapX, mapY + 1) & LOMASK;
|
||||
if (tile >= low && tile <= high)
|
||||
return true;
|
||||
}
|
||||
if (mapX > 0)
|
||||
{
|
||||
int tile = engine.getTile(mapX - 1, mapY) & LOMASK;
|
||||
if (tile >= low && tile <= high)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
90
src/micropolisj/engine/TrainSprite.java
Normal file
90
src/micropolisj/engine/TrainSprite.java
Normal file
|
@ -0,0 +1,90 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
import static micropolisj.engine.TileConstants.*;
|
||||
|
||||
public class TrainSprite extends Sprite
|
||||
{
|
||||
static int [] Cx = { 0, 16, 0, -16 };
|
||||
static int [] Cy = { -16, 0, 16, 0 };
|
||||
static int [] Dx = { 0, 4, 0, -4, 0 };
|
||||
static int [] Dy = { -4, 0, 4, 0, 0 };
|
||||
static int [] TrainPic2 = { 1, 2, 1, 2, 5 };
|
||||
static final int TRA_GROOVE_X = 8;
|
||||
static final int TRA_GROOVE_Y = 8;
|
||||
|
||||
static final int FRAME_NORTHSOUTH = 1;
|
||||
static final int FRAME_EASTWEST = 2;
|
||||
static final int FRAME_NW_SE = 3;
|
||||
static final int FRAME_SW_NE = 4;
|
||||
static final int FRAME_UNDERWATER = 5;
|
||||
|
||||
public TrainSprite(Micropolis engine, int xpos, int ypos)
|
||||
{
|
||||
super(engine, SpriteKind.TRA);
|
||||
this.x = xpos * 16 + TRA_GROOVE_X;
|
||||
this.y = ypos * 16 + TRA_GROOVE_Y;
|
||||
this.offx = -16;
|
||||
this.offy = -16;
|
||||
this.dir = 4; //not moving
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveImpl()
|
||||
{
|
||||
if (frame == 3 || frame == 4) {
|
||||
frame = TrainPic2[this.dir];
|
||||
}
|
||||
x += Dx[this.dir];
|
||||
y += Dy[this.dir];
|
||||
if (city.acycle % 4 == 0) {
|
||||
// should be at the center of a cell, if not, correct it
|
||||
x = (x/16) * 16 + TRA_GROOVE_X;
|
||||
y = (y/16) * 16 + TRA_GROOVE_Y;
|
||||
int d1 = city.PRNG.nextInt(4);
|
||||
for (int z = d1; z < d1 + 4; z++) {
|
||||
int d2 = z % 4;
|
||||
if (this.dir != 4) { //impossible?
|
||||
if (d2 == (this.dir + 2) % 4)
|
||||
continue;
|
||||
}
|
||||
|
||||
int c = getChar(this.x + Cx[d2], this.y + Cy[d2]);
|
||||
if (((c >= RAILBASE) && (c <= LASTRAIL)) || //track?
|
||||
(c == RAILVPOWERH) ||
|
||||
(c == RAILHPOWERV))
|
||||
{
|
||||
if ((this.dir != d2) && (this.dir != 4)) {
|
||||
if (this.dir + d2 == 3)
|
||||
this.frame = 3;
|
||||
else
|
||||
this.frame = 4;
|
||||
}
|
||||
else {
|
||||
this.frame = TrainPic2[d2];
|
||||
}
|
||||
|
||||
if ((c == RAILBASE) || (c == (RAILBASE+1))) {
|
||||
//underwater
|
||||
this.frame = 5;
|
||||
}
|
||||
this.dir = d2;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this.dir == 4) {
|
||||
// train has nowhere to go, so retire
|
||||
this.frame = 0;
|
||||
return;
|
||||
}
|
||||
this.dir = 4;
|
||||
}
|
||||
}
|
||||
}
|
19
src/micropolisj/engine/ZoneStatus.java
Normal file
19
src/micropolisj/engine/ZoneStatus.java
Normal file
|
@ -0,0 +1,19 @@
|
|||
// This file is part of MicropolisJ.
|
||||
// Copyright (C) 2013 Jason Long
|
||||
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
||||
//
|
||||
// MicropolisJ is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU GPLv3, with additional terms.
|
||||
// See the README file, included in this distribution, for details.
|
||||
|
||||
package micropolisj.engine;
|
||||
|
||||
public class ZoneStatus
|
||||
{
|
||||
public int building;
|
||||
public int popDensity;
|
||||
public int landValue;
|
||||
public int crimeLevel;
|
||||
public int pollution;
|
||||
public int growthRate;
|
||||
}
|
4
src/micropolisj/engine/package.html
Normal file
4
src/micropolisj/engine/package.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
<html><head></head>
|
||||
<body>
|
||||
<p>Contains the backend classes that implement the actual city simulation.</p>
|
||||
</body></html>
|
Reference in a new issue