mirror of
https://github.com/tonytins/citylimits.git
synced 2025-03-15 12:21:22 +00:00
2040 lines
49 KiB
C++
2040 lines
49 KiB
C++
|
/* sprite.cpp
|
||
|
*
|
||
|
* Micropolis, Unix Version. This game was released for the Unix platform
|
||
|
* in or about 1990 and has been modified for inclusion in the One Laptop
|
||
|
* Per Child program. Copyright (C) 1989 - 2007 Electronic Arts Inc. If
|
||
|
* you need assistance with this program, you may contact:
|
||
|
* http://wiki.laptop.org/go/Micropolis or email micropolis@laptop.org.
|
||
|
*
|
||
|
* This program is free software: you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||
|
* your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful, but
|
||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* General Public License for more details. You should have received a
|
||
|
* copy of the GNU General Public License along with this program. If
|
||
|
* not, see <http://www.gnu.org/licenses/>.
|
||
|
*
|
||
|
* ADDITIONAL TERMS per GNU GPL Section 7
|
||
|
*
|
||
|
* No trademark or publicity rights are granted. This license does NOT
|
||
|
* give you any right, title or interest in the trademark SimCity or any
|
||
|
* other Electronic Arts trademark. You may not distribute any
|
||
|
* modification of this program using the trademark SimCity or claim any
|
||
|
* affliation or association with Electronic Arts Inc. or its employees.
|
||
|
*
|
||
|
* Any propagation or conveyance of this program must include this
|
||
|
* copyright notice and these terms.
|
||
|
*
|
||
|
* If you convey this program (or any modifications of it) and assume
|
||
|
* contractual liability for the program to recipients of it, you agree
|
||
|
* to indemnify Electronic Arts for any liability that those contractual
|
||
|
* assumptions impose on Electronic Arts.
|
||
|
*
|
||
|
* You may not misrepresent the origins of this program; modified
|
||
|
* versions of the program must be marked as such and not identified as
|
||
|
* the original program.
|
||
|
*
|
||
|
* This disclaimer supplements the one included in the General Public
|
||
|
* License. TO THE FULLEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW, THIS
|
||
|
* PROGRAM IS PROVIDED TO YOU "AS IS," WITH ALL FAULTS, WITHOUT WARRANTY
|
||
|
* OF ANY KIND, AND YOUR USE IS AT YOUR SOLE RISK. THE ENTIRE RISK OF
|
||
|
* SATISFACTORY QUALITY AND PERFORMANCE RESIDES WITH YOU. ELECTRONIC ARTS
|
||
|
* DISCLAIMS ANY AND ALL EXPRESS, IMPLIED OR STATUTORY WARRANTIES,
|
||
|
* INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY, SATISFACTORY QUALITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT OF THIRD PARTY
|
||
|
* RIGHTS, AND WARRANTIES (IF ANY) ARISING FROM A COURSE OF DEALING,
|
||
|
* USAGE, OR TRADE PRACTICE. ELECTRONIC ARTS DOES NOT WARRANT AGAINST
|
||
|
* INTERFERENCE WITH YOUR ENJOYMENT OF THE PROGRAM; THAT THE PROGRAM WILL
|
||
|
* MEET YOUR REQUIREMENTS; THAT OPERATION OF THE PROGRAM WILL BE
|
||
|
* UNINTERRUPTED OR ERROR-FREE, OR THAT THE PROGRAM WILL BE COMPATIBLE
|
||
|
* WITH THIRD PARTY SOFTWARE OR THAT ANY ERRORS IN THE PROGRAM WILL BE
|
||
|
* CORRECTED. NO ORAL OR WRITTEN ADVICE PROVIDED BY ELECTRONIC ARTS OR
|
||
|
* ANY AUTHORIZED REPRESENTATIVE SHALL CREATE A WARRANTY. SOME
|
||
|
* JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF OR LIMITATIONS ON IMPLIED
|
||
|
* WARRANTIES OR THE LIMITATIONS ON THE APPLICABLE STATUTORY RIGHTS OF A
|
||
|
* CONSUMER, SO SOME OR ALL OF THE ABOVE EXCLUSIONS AND LIMITATIONS MAY
|
||
|
* NOT APPLY TO YOU.
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* @file sprite.cpp
|
||
|
* @brief Manages sprite objects and their behavior in Micropolis.
|
||
|
*
|
||
|
* This file defines functions related to the creation, movement,
|
||
|
* interaction, and destruction of various sprites such as trains,
|
||
|
* helicopters, airplanes, ships, monsters, tornadoes, and explosions
|
||
|
* within the Micropolis game. It handles the logic for sprite
|
||
|
* behavior, including movement patterns, interactions with other
|
||
|
* sprites and the game map, and event-triggered actions like
|
||
|
* explosions and sound effects.
|
||
|
*/
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
|
||
|
#include "micropolis.h"
|
||
|
#include "text.h"
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
|
||
|
#define TRA_GROOVE_X -39
|
||
|
#define TRA_GROOVE_Y 6
|
||
|
#define BUS_GROOVE_X -39
|
||
|
#define BUS_GROOVE_Y 6
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Create and initialize a sprite.
|
||
|
* @param name Name of the sprite (always \c "").
|
||
|
* @param type Type pf the sprite. @see SpriteType.
|
||
|
* @param x X coordinate of the sprite (in pixels).
|
||
|
* @param y Y coordinate of the sprite (in pixels).
|
||
|
* @return New sprite object.
|
||
|
*/
|
||
|
SimSprite *Micropolis::newSprite(const std::string &name, int type, int x, int y)
|
||
|
{
|
||
|
SimSprite *sprite;
|
||
|
|
||
|
// If a sprite is available at the pool, use one.
|
||
|
// else, allocate a new one.
|
||
|
if (freeSprites) {
|
||
|
sprite = freeSprites;
|
||
|
freeSprites = sprite->next;
|
||
|
} else {
|
||
|
sprite = (SimSprite *)newPtr(sizeof (SimSprite));
|
||
|
}
|
||
|
|
||
|
sprite->name = name;
|
||
|
sprite->type = type;
|
||
|
|
||
|
initSprite(sprite, x, y);
|
||
|
|
||
|
sprite->next = spriteList;
|
||
|
spriteList = sprite;
|
||
|
|
||
|
return sprite;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** Re-initialize an existing sprite.
|
||
|
* @param sprite Sprite to re-use.
|
||
|
* @param x New x coordinate of the sprite (in pixels?).
|
||
|
* @param y New y coordinate of the sprite (in pixels?).
|
||
|
* @todo Make derived classes for each type.
|
||
|
* @todo Move code to (derived) #SimSprite methods.
|
||
|
*/
|
||
|
void Micropolis::initSprite(SimSprite *sprite, int x, int y)
|
||
|
{
|
||
|
sprite->x = x;
|
||
|
sprite->y = y;
|
||
|
sprite->frame = 0;
|
||
|
sprite->origX = 0;
|
||
|
sprite->origY = 0;
|
||
|
sprite->destX = 0;
|
||
|
sprite->destY = 0;
|
||
|
sprite->count = 0;
|
||
|
sprite->soundCount = 0;
|
||
|
sprite->dir = 0;
|
||
|
sprite->newDir = 0;
|
||
|
sprite->step = 0;
|
||
|
sprite->flag = 0;
|
||
|
sprite->control = -1;
|
||
|
sprite->turn = 0;
|
||
|
sprite->accel = 0;
|
||
|
sprite->speed = 100;
|
||
|
|
||
|
if (globalSprites[sprite->type] == NULL) {
|
||
|
globalSprites[sprite->type] = sprite;
|
||
|
}
|
||
|
|
||
|
switch (sprite->type) {
|
||
|
|
||
|
case SPRITE_TRAIN:
|
||
|
sprite->width = 32;
|
||
|
sprite->height = 32;
|
||
|
sprite->xOffset = 32;
|
||
|
sprite->yOffset = -16;
|
||
|
sprite->xHot = 40;
|
||
|
sprite->yHot = -8;
|
||
|
sprite->frame = 1;
|
||
|
sprite->dir = 4;
|
||
|
break;
|
||
|
|
||
|
case SPRITE_SHIP:
|
||
|
sprite->width = 48;
|
||
|
sprite->height = 48;
|
||
|
sprite->xOffset = 32;
|
||
|
sprite->yOffset = -16;
|
||
|
sprite->xHot = 48;
|
||
|
sprite->yHot = 0;
|
||
|
|
||
|
if (x < (4 <<4)) {
|
||
|
sprite->frame = 3;
|
||
|
} else if (x >= ((WORLD_W - 4) <<4)) {
|
||
|
sprite->frame = 7;
|
||
|
} else if (y < (4 <<4)) {
|
||
|
sprite->frame = 5;
|
||
|
} else if (y >= ((WORLD_H - 4) <<4)) {
|
||
|
sprite->frame = 1;
|
||
|
} else {
|
||
|
sprite->frame = 3;
|
||
|
}
|
||
|
|
||
|
sprite->newDir = sprite->frame;
|
||
|
sprite->dir = 10;
|
||
|
sprite->count = 1;
|
||
|
break;
|
||
|
|
||
|
case SPRITE_MONSTER:
|
||
|
sprite->width = 48;
|
||
|
sprite->height = 48;
|
||
|
sprite->xOffset = 24;
|
||
|
sprite->yOffset = 0;
|
||
|
sprite->xHot = 40;
|
||
|
sprite->yHot = 16;
|
||
|
|
||
|
if (x > ((WORLD_W <<4) / 2)) {
|
||
|
if (y > ((WORLD_H <<4) / 2)) {
|
||
|
sprite->frame = 10;
|
||
|
} else {
|
||
|
sprite->frame = 7;
|
||
|
}
|
||
|
} else if (y > ((WORLD_H <<4) / 2)) {
|
||
|
sprite->frame = 1;
|
||
|
} else {
|
||
|
sprite->frame = 4;
|
||
|
}
|
||
|
|
||
|
sprite->count = 1000;
|
||
|
sprite->destX = pollutionMaxX <<4;
|
||
|
sprite->destY = pollutionMaxY <<4;
|
||
|
sprite->origX = sprite->x;
|
||
|
sprite->origY = sprite->y;
|
||
|
break;
|
||
|
|
||
|
case SPRITE_HELICOPTER:
|
||
|
sprite->width = 32;
|
||
|
sprite->height = 32;
|
||
|
sprite->xOffset = 32;
|
||
|
sprite->yOffset = -16;
|
||
|
sprite->xHot = 40;
|
||
|
sprite->yHot = -8;
|
||
|
sprite->frame = 5;
|
||
|
sprite->count = 1500;
|
||
|
sprite->destX = getRandom((WORLD_W <<4) - 1);
|
||
|
sprite->destY = getRandom((WORLD_H <<4) - 1);
|
||
|
sprite->origX = x - 30;
|
||
|
sprite->origY = y;
|
||
|
break;
|
||
|
|
||
|
case SPRITE_AIRPLANE:
|
||
|
sprite->width = 48;
|
||
|
sprite->height = 48;
|
||
|
sprite->xOffset = 24;
|
||
|
sprite->yOffset = 0;
|
||
|
sprite->xHot = 48;
|
||
|
sprite->yHot = 16;
|
||
|
if (x > ((WORLD_W - 20) <<4)) {
|
||
|
sprite->x -= 100 + 48;
|
||
|
sprite->destX = sprite->x - 200;
|
||
|
sprite->frame = 7;
|
||
|
} else {
|
||
|
sprite->destX = sprite->x + 200;
|
||
|
sprite->frame = 11;
|
||
|
}
|
||
|
sprite->destY = sprite->y;
|
||
|
break;
|
||
|
|
||
|
case SPRITE_TORNADO:
|
||
|
sprite->width = 48;
|
||
|
sprite->height = 48;
|
||
|
sprite->xOffset = 24;
|
||
|
sprite->yOffset = 0;
|
||
|
sprite->xHot = 40;
|
||
|
sprite->yHot = 36;
|
||
|
sprite->frame = 1;
|
||
|
sprite->count = 200;
|
||
|
break;
|
||
|
|
||
|
case SPRITE_EXPLOSION:
|
||
|
sprite->width = 48;
|
||
|
sprite->height = 48;
|
||
|
sprite->xOffset = 24;
|
||
|
sprite->yOffset = 0;
|
||
|
sprite->xHot = 40;
|
||
|
sprite->yHot = 16;
|
||
|
sprite->frame = 1;
|
||
|
break;
|
||
|
|
||
|
case SPRITE_BUS:
|
||
|
sprite->width = 32;
|
||
|
sprite->height = 32;
|
||
|
sprite->xOffset = 30;
|
||
|
sprite->yOffset = -18;
|
||
|
sprite->xHot = 40;
|
||
|
sprite->yHot = -8;
|
||
|
sprite->frame = 1;
|
||
|
sprite->dir = 1;
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Destroy all sprites by de-activating them all (setting their
|
||
|
* SimSprite::frame to 0).
|
||
|
*/
|
||
|
void Micropolis::destroyAllSprites()
|
||
|
{
|
||
|
SimSprite *sprite;
|
||
|
|
||
|
for (sprite = spriteList; sprite != NULL; sprite = sprite->next) {
|
||
|
sprite->frame = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Destroy the sprite by taking it out of the active list.
|
||
|
* @param sprite Sprite to destroy.
|
||
|
* @todo Break the connection between any views that are following this sprite.
|
||
|
*/
|
||
|
void Micropolis::destroySprite(SimSprite *sprite)
|
||
|
{
|
||
|
SimSprite **sp;
|
||
|
|
||
|
if (globalSprites[sprite->type] == sprite) {
|
||
|
globalSprites[sprite->type] = (SimSprite *)NULL;
|
||
|
}
|
||
|
|
||
|
for (sp = &spriteList; *sp != NULL; sp = &((*sp)->next)) {
|
||
|
if (sprite == (*sp)) {
|
||
|
*sp = sprite->next;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sprite->next = freeSprites;
|
||
|
freeSprites = sprite;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Return the sprite of the give type, if available.
|
||
|
* @param type Type of the sprite.
|
||
|
* @return Pointer to the active sprite if avaiable, else \c NULL.
|
||
|
*/
|
||
|
SimSprite *Micropolis::getSprite(int type)
|
||
|
{
|
||
|
SimSprite *sprite = globalSprites[type];
|
||
|
if (sprite == NULL || sprite->frame == 0) {
|
||
|
return (SimSprite *)NULL;
|
||
|
} else {
|
||
|
return sprite;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Make a sprite either by re-using the old one, or by making a new one.
|
||
|
* @param type Sprite type of the new sprite.
|
||
|
* @param x X coordinate of the new sprite.
|
||
|
* @param y Y coordinate of the new sprite.
|
||
|
*/
|
||
|
SimSprite *Micropolis::makeSprite(int type, int x, int y)
|
||
|
{
|
||
|
SimSprite *sprite;
|
||
|
|
||
|
sprite = globalSprites[type];
|
||
|
if (sprite == NULL) {
|
||
|
sprite = newSprite("", type, x, y);
|
||
|
} else {
|
||
|
initSprite(sprite, x, y);
|
||
|
}
|
||
|
return sprite;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Get character from the map.
|
||
|
* @param x X coordinate in pixels.
|
||
|
* @param y Y coordinate in pixels.
|
||
|
* @return Map character if on-map, or \c -1 if off-map.
|
||
|
*/
|
||
|
short Micropolis::getChar(int x, int y)
|
||
|
{
|
||
|
// Convert sprite coordinates to tile coordinates.
|
||
|
x >>= 4;
|
||
|
y >>= 4;
|
||
|
|
||
|
if (!testBounds(x, y)) {
|
||
|
return -1;
|
||
|
} else {
|
||
|
return map[x][y] & LOMASK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Turn.
|
||
|
* @param p Present direction (1..8).
|
||
|
* @param d Destination direction (1..8).
|
||
|
* @return New direction.
|
||
|
* @todo Remove local magic constants and document the code.
|
||
|
*/
|
||
|
short Micropolis::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) {
|
||
|
p = 1;
|
||
|
}
|
||
|
|
||
|
if (p < 1) {
|
||
|
p = 8;
|
||
|
}
|
||
|
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
/** ???
|
||
|
* @todo Figure out what this function is doing.
|
||
|
* @todo Remove local magic constants and document the code.
|
||
|
*/
|
||
|
bool Micropolis::tryOther(int Tpoo, int Told, int Tnew)
|
||
|
{
|
||
|
short z;
|
||
|
|
||
|
z = Told + 4;
|
||
|
|
||
|
if (z > 8) {
|
||
|
z -= 8;
|
||
|
}
|
||
|
|
||
|
if (Tnew != z) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (Tpoo == POWERBASE || Tpoo == POWERBASE + 1
|
||
|
|| Tpoo == RAILBASE || Tpoo == RAILBASE + 1) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Check whether a sprite is still entirely on-map.
|
||
|
* @param sprite Sprite to check.
|
||
|
* @return Sprite is at least partly off-map.
|
||
|
*/
|
||
|
bool Micropolis::spriteNotInBounds(SimSprite *sprite)
|
||
|
{
|
||
|
int x = sprite->x + sprite->xHot;
|
||
|
int y = sprite->y + sprite->yHot;
|
||
|
|
||
|
return x < 0 || y < 0 || x >= (WORLD_W <<4) || y >= (WORLD_H <<4);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get direction (0..8?) to get from starting point to destination point.
|
||
|
* @param orgX X coordinate starting point.
|
||
|
* @param orgY Y coordinate starting point.
|
||
|
* @param desX X coordinate destination point.
|
||
|
* @param desY Y coordinate destination point.
|
||
|
* @return Direction to go in.
|
||
|
* @todo Remove local magic constants and document the code.
|
||
|
* @bug Has a condition that never holds.
|
||
|
*/
|
||
|
short Micropolis::getDir(int orgX, int orgY, int desX, int desY)
|
||
|
{
|
||
|
static const short Gdtab[13] = { 0, 3, 2, 1, 3, 4, 5, 7, 6, 5, 7, 8, 1 };
|
||
|
int dispX, dispY, z;
|
||
|
|
||
|
dispX = desX - orgX;
|
||
|
dispY = desY - orgY;
|
||
|
|
||
|
if (dispX < 0) {
|
||
|
if (dispY < 0) {
|
||
|
z = 11;
|
||
|
} else {
|
||
|
z = 8;
|
||
|
}
|
||
|
} else {
|
||
|
if (dispY < 0) {
|
||
|
z = 2;
|
||
|
} else {
|
||
|
z = 5;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dispX = absoluteValue(dispX);
|
||
|
dispY = absoluteValue(dispY);
|
||
|
absDist = dispX + dispY;
|
||
|
|
||
|
if (dispX * 2 < dispY) {
|
||
|
z++;
|
||
|
} else if (dispY * 2 < dispY) { // XXX This never holds!!
|
||
|
z--;
|
||
|
}
|
||
|
|
||
|
if (z < 0 || z > 12) {
|
||
|
z = 0;
|
||
|
}
|
||
|
|
||
|
return Gdtab[z];
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Compute Manhattan distance between two points.
|
||
|
* @param x1 X coordinate first point.
|
||
|
* @param y1 Y coordinate first point.
|
||
|
* @param x2 X coordinate second point.
|
||
|
* @param y2 Y coordinate second point.
|
||
|
* @return Manhattan distance between both points.
|
||
|
*/
|
||
|
int Micropolis::getDistance(int x1, int y1, int x2, int y2)
|
||
|
{
|
||
|
return absoluteValue(x1 - x2) + absoluteValue(y1 - y2);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Check whether two sprites collide with each other.
|
||
|
* @param s1 First sprite.
|
||
|
* @param s2 Second sprite.
|
||
|
* @return Sprites are colliding.
|
||
|
*/
|
||
|
bool Micropolis::checkSpriteCollision(SimSprite *s1, SimSprite *s2)
|
||
|
{
|
||
|
return s1->frame != 0 && s2->frame != 0 &&
|
||
|
getDistance(s1->x + s1->xHot, s1->y + s1->yHot,
|
||
|
s2->x + s2->xHot, s2->y + s2->yHot) < 30;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Move all sprites.
|
||
|
*
|
||
|
* Sprites with SimSprite::frame == 0 are removed.
|
||
|
* @todo It uses SimSprite::name[0] == '\0' as condition which seems stupid.
|
||
|
* @todo Micropolis::destroySprite modifies the Micropolis::spriteList
|
||
|
* while we loop over it.
|
||
|
*/
|
||
|
void Micropolis::moveObjects()
|
||
|
{
|
||
|
SimSprite *sprite;
|
||
|
|
||
|
if (!simSpeed) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
spriteCycle++;
|
||
|
|
||
|
for (sprite = spriteList; sprite != NULL;) {
|
||
|
if (sprite->frame > 0) {
|
||
|
switch (sprite->type) {
|
||
|
|
||
|
case SPRITE_TRAIN:
|
||
|
doTrainSprite(sprite);
|
||
|
break;
|
||
|
|
||
|
case SPRITE_HELICOPTER:
|
||
|
doCopterSprite(sprite);
|
||
|
break;
|
||
|
|
||
|
case SPRITE_AIRPLANE:
|
||
|
doAirplaneSprite(sprite);
|
||
|
break;
|
||
|
|
||
|
case SPRITE_SHIP:
|
||
|
doShipSprite(sprite);
|
||
|
break;
|
||
|
|
||
|
case SPRITE_MONSTER:
|
||
|
doMonsterSprite(sprite);
|
||
|
break;
|
||
|
|
||
|
case SPRITE_TORNADO:
|
||
|
doTornadoSprite(sprite);
|
||
|
break;
|
||
|
|
||
|
case SPRITE_EXPLOSION:
|
||
|
doExplosionSprite(sprite);
|
||
|
break;
|
||
|
|
||
|
case SPRITE_BUS:
|
||
|
doBusSprite(sprite);
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
sprite = sprite->next;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
if (sprite->name[0] == '\0') {
|
||
|
SimSprite *s = sprite;
|
||
|
sprite = sprite->next;
|
||
|
destroySprite(s);
|
||
|
} else {
|
||
|
sprite = sprite->next;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Move train sprite.
|
||
|
* @param sprite Train sprite.
|
||
|
* @todo Remove local magic constants and document the code.
|
||
|
*/
|
||
|
void Micropolis::doTrainSprite(SimSprite *sprite)
|
||
|
{
|
||
|
/* Offset in pixels of sprite x and y to map tile */
|
||
|
static const short Cx[4] = { 0, 16, 0, -16 };
|
||
|
static const short Cy[4] = { -16, 0, 16, 0 };
|
||
|
/* X and Y movement of the sprite in pixels */
|
||
|
static const short Dx[5] = { 0, 4, 0, -4, 0 };
|
||
|
static const short Dy[5] = { -4, 0, 4, 0, 0 };
|
||
|
|
||
|
static const short TrainPic2[5] = { 1, 2, 1, 2, 5 };
|
||
|
short z, dir, dir2;
|
||
|
short c;
|
||
|
|
||
|
if (sprite->frame == 3 || sprite->frame == 4) {
|
||
|
sprite->frame = TrainPic2[sprite->dir];
|
||
|
}
|
||
|
|
||
|
sprite->x += Dx[sprite->dir];
|
||
|
sprite->y += Dy[sprite->dir];
|
||
|
|
||
|
if ((spriteCycle & 3) == 0) {
|
||
|
|
||
|
dir = getRandom16() & 3;
|
||
|
for (z = dir; z < dir + 4; z++) {
|
||
|
dir2 = z & 3;
|
||
|
|
||
|
if (sprite->dir != 4) {
|
||
|
if (dir2 == ((sprite->dir + 2) & 3)) {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
c = getChar(sprite->x + Cx[dir2] + 48, sprite->y + Cy[dir2]);
|
||
|
|
||
|
if ((c >= RAILBASE && c <= LASTRAIL) /* track? */
|
||
|
|| c == RAILVPOWERH || c == RAILHPOWERV) {
|
||
|
|
||
|
if (sprite->dir != dir2 && sprite->dir != 4) {
|
||
|
|
||
|
if (sprite->dir + dir2 == 3) {
|
||
|
sprite->frame = 3;
|
||
|
} else {
|
||
|
sprite->frame = 4;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
sprite->frame = TrainPic2[dir2];
|
||
|
}
|
||
|
|
||
|
if (c == HRAIL || c == VRAIL) {
|
||
|
sprite->frame = 5;
|
||
|
}
|
||
|
|
||
|
sprite->dir = dir2;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (sprite->dir == 4) {
|
||
|
sprite->frame = 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sprite->dir = 4;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Move helicopter sprite.
|
||
|
* @param sprite Helicopter sprite.
|
||
|
* @todo Remove local magic constants and document the code.
|
||
|
*/
|
||
|
void Micropolis::doCopterSprite(
|
||
|
SimSprite *sprite)
|
||
|
{
|
||
|
static const short CDx[9] = { 0, 0, 3, 5, 3, 0, -3, -5, -3 };
|
||
|
static const short CDy[9] = { 0, -5, -3, 0, 3, 5, 3, 0, -3 };
|
||
|
|
||
|
if (sprite->soundCount > 0) {
|
||
|
sprite->soundCount--;
|
||
|
}
|
||
|
|
||
|
if (sprite->control < 0) {
|
||
|
|
||
|
if (sprite->count > 0) {
|
||
|
sprite->count--;
|
||
|
}
|
||
|
|
||
|
if (sprite->count == 0) {
|
||
|
|
||
|
/* Attract copter to monster so it blows up more often */
|
||
|
SimSprite *s = getSprite(SPRITE_MONSTER);
|
||
|
|
||
|
if (s != NULL) {
|
||
|
sprite->destX = s->x;
|
||
|
sprite->destY = s->y;
|
||
|
} else {
|
||
|
|
||
|
/* Attract copter to tornado so it blows up more often */
|
||
|
s = getSprite(SPRITE_TORNADO);
|
||
|
|
||
|
if (s != NULL) {
|
||
|
sprite->destX = s->x;
|
||
|
sprite->destY = s->y;
|
||
|
} else {
|
||
|
sprite->destX = sprite->origX;
|
||
|
sprite->destY = sprite->origY;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (sprite->count == 0) { /* land */
|
||
|
getDir(sprite->x, sprite->y, sprite->origX, sprite->origY);
|
||
|
|
||
|
if (absDist < 30) {
|
||
|
sprite->frame = 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
getDir(sprite->x, sprite->y, sprite->destX, sprite->destY);
|
||
|
|
||
|
if (absDist < 16) {
|
||
|
sprite->destX = sprite->origX;
|
||
|
sprite->destY = sprite->origY;
|
||
|
sprite->control = -1;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if (sprite->soundCount == 0) { /* send report */
|
||
|
|
||
|
// Convert sprite coordinates to world coordinates.
|
||
|
short x = (sprite->x + 48) / 16;
|
||
|
short y = sprite->y / 16;
|
||
|
|
||
|
if (x >= 0 && x < WORLD_W && y >= 0 && y < WORLD_H) {
|
||
|
|
||
|
/* Don changed from 160 to 170 to shut the #$%#$% thing up! */
|
||
|
|
||
|
int chopperX = x + 1;
|
||
|
int chopperY = y + 1;
|
||
|
if (trafficDensityMap.worldGet(x, y) > 170 && (getRandom16() & 7) == 0) {
|
||
|
sendMessage(MESSAGE_HEAVY_TRAFFIC, chopperX, chopperY, true);
|
||
|
makeSound("city", "HeavyTraffic", chopperX, chopperY); /* chopper */
|
||
|
sprite->soundCount = 200;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
short z = sprite->frame;
|
||
|
|
||
|
if ((spriteCycle & 3) == 0) {
|
||
|
short d = getDir(sprite->x, sprite->y, sprite->destX, sprite->destY);
|
||
|
z = turnTo(z, d);
|
||
|
sprite->frame = z;
|
||
|
}
|
||
|
|
||
|
sprite->x += CDx[z];
|
||
|
sprite->y += CDy[z];
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Move airplane sprite.
|
||
|
* @param sprite Airplane sprite.
|
||
|
* @todo Remove local magic constants and document the code.
|
||
|
* @todo absDist gets updated by Micropolis::getDir(), which is not always
|
||
|
* called before reading it (or worse, we just turned towards the old
|
||
|
* destination).
|
||
|
*/
|
||
|
void Micropolis::doAirplaneSprite(
|
||
|
SimSprite *sprite)
|
||
|
{
|
||
|
static const short CDx[12] = { 0, 0, 6, 8, 6, 0, -6, -8, -6, 8, 8, 8 };
|
||
|
static const short CDy[12] = { 0, -8, -6, 0, 6, 8, 6, 0, -6, 0, 0, 0 };
|
||
|
|
||
|
short z = sprite->frame;
|
||
|
|
||
|
if ((spriteCycle % 5) == 0) {
|
||
|
|
||
|
if (z > 8) { /* TakeOff */
|
||
|
z--;
|
||
|
if (z < 9) {
|
||
|
z = 3;
|
||
|
}
|
||
|
sprite->frame = z;
|
||
|
} else { /* goto destination */
|
||
|
short d = getDir(sprite->x, sprite->y, sprite->destX, sprite->destY);
|
||
|
z = turnTo(z, d);
|
||
|
sprite->frame = z;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if (absDist < 50) { /* at destination */
|
||
|
sprite->destX = getRandom((WORLD_W * 16) + 100) - 50;
|
||
|
sprite->destY = getRandom((WORLD_H * 16) + 100) - 50;
|
||
|
}
|
||
|
|
||
|
/* deh added test for enableDisasters */
|
||
|
if (enableDisasters) {
|
||
|
SimSprite *s;
|
||
|
bool explode = false;
|
||
|
|
||
|
/* Check whether another sprite is near enough to collide with */
|
||
|
for (s = spriteList; s != NULL; s = s->next) {
|
||
|
if (s->frame == 0 || s == sprite) {
|
||
|
/* Non-active sprite, or self: skip */
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ((s->type == SPRITE_HELICOPTER || s->type == SPRITE_AIRPLANE)
|
||
|
&& checkSpriteCollision(sprite, s)) {
|
||
|
explodeSprite(s);
|
||
|
explode = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (explode) {
|
||
|
explodeSprite(sprite);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sprite->x += CDx[z];
|
||
|
sprite->y += CDy[z];
|
||
|
|
||
|
if (spriteNotInBounds(sprite)) {
|
||
|
sprite->frame = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Move ship sprite.
|
||
|
* @param sprite Ship sprite.
|
||
|
* @todo Remove local magic constants and document the code.
|
||
|
*/
|
||
|
void Micropolis::doShipSprite(SimSprite *sprite)
|
||
|
{
|
||
|
static const short BDx[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
|
||
|
static const short BDy[9] = { 0, -1, -1, 0, 1, 1, 1, 0, -1 };
|
||
|
static const short BPx[9] = { 0, 0, 2, 2, 2, 0, -2, -2, -2 };
|
||
|
static const short BPy[9] = { 0, -2, -2, 0, 2, 2, 2, 0, -2 };
|
||
|
static const short BtClrTab[8] = { RIVER, CHANNEL, POWERBASE, POWERBASE + 1,
|
||
|
RAILBASE, RAILBASE + 1, BRWH, BRWV };
|
||
|
short x, y, z, t = RIVER;
|
||
|
short tem, pem;
|
||
|
|
||
|
if (sprite->soundCount > 0) {
|
||
|
sprite->soundCount--;
|
||
|
}
|
||
|
|
||
|
if (!sprite->soundCount) {
|
||
|
|
||
|
if ((getRandom16() & 3) == 1) {
|
||
|
|
||
|
// Convert sprite coordinates to tile coordinates.
|
||
|
int shipX = sprite->x >>4;
|
||
|
int shipY = sprite->y >>4;
|
||
|
|
||
|
if (scenario == SC_SAN_FRANCISCO && getRandom(10) < 5) {
|
||
|
makeSound("city", "FogHornLow", shipX, shipY);
|
||
|
} else {
|
||
|
makeSound("city", "HonkHonkLow", shipX, shipY);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
sprite->soundCount = 200;
|
||
|
}
|
||
|
|
||
|
if (sprite->count > 0) {
|
||
|
sprite->count--;
|
||
|
}
|
||
|
|
||
|
if (sprite->count == 0) {
|
||
|
|
||
|
sprite->count = 9;
|
||
|
|
||
|
if (sprite->frame != sprite->newDir) {
|
||
|
sprite->frame = turnTo(sprite->frame, sprite->newDir);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
tem = getRandom16() & 7;
|
||
|
|
||
|
for (pem = tem; pem < (tem + 8); pem++) {
|
||
|
|
||
|
z = (pem & 7) + 1;
|
||
|
|
||
|
if (z == sprite->dir) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
x = ((sprite->x + (48 - 1)) >>4) + BDx[z];
|
||
|
y = (sprite->y >>4) + BDy[z];
|
||
|
|
||
|
if (testBounds(x, y)) {
|
||
|
|
||
|
t = map[x][y] & LOMASK;
|
||
|
|
||
|
if (t == CHANNEL || t == BRWH || t == BRWV
|
||
|
|| tryOther(t, sprite->dir, z)) {
|
||
|
|
||
|
sprite->newDir = z;
|
||
|
sprite->frame = turnTo(sprite->frame, sprite->newDir);
|
||
|
sprite->dir = z + 4;
|
||
|
|
||
|
if (sprite->dir > 8) {
|
||
|
sprite->dir -= 8;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pem == (tem + 8)) {
|
||
|
sprite->dir = 10;
|
||
|
sprite->newDir = (getRandom16() & 7) + 1;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
z = sprite->frame;
|
||
|
|
||
|
if (z == sprite->newDir) {
|
||
|
sprite->x += BPx[z];
|
||
|
sprite->y += BPy[z];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (spriteNotInBounds(sprite)) {
|
||
|
sprite->frame = 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (z = 0; z < 8; z++) {
|
||
|
|
||
|
if (t == BtClrTab[z]) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (z == 7) {
|
||
|
explodeSprite(sprite);
|
||
|
destroyMapTile(sprite->x + 48, sprite->y);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Move monster sprite.
|
||
|
*
|
||
|
* There are 16 monster sprite frames:
|
||
|
*
|
||
|
* Frame 0: NorthEast Left Foot
|
||
|
* Frame 1: NorthEast Both Feet
|
||
|
* Frame 2: NorthEast Right Foot
|
||
|
* Frame 3: SouthEast Right Foot
|
||
|
* Frame 4: SouthEast Both Feet
|
||
|
* Frame 5: SouthEast Left Foot
|
||
|
* Frame 6: SouthWest Right Foot
|
||
|
* Frame 7: SouthWest Both Feet
|
||
|
* Frame 8: SouthWest Left Foot
|
||
|
* Frame 9: NorthWest Left Foot
|
||
|
* Frame 10: NorthWest Both Feet
|
||
|
* Frame 11: NorthWest Right Foot
|
||
|
* Frame 12: North Left Foot
|
||
|
* Frame 13: East Left Foot
|
||
|
* Frame 14: South Right Foot
|
||
|
* Frame 15: West Right Foot
|
||
|
*
|
||
|
* @param sprite Monster sprite.
|
||
|
* @todo Remove local magic constants and document the code.
|
||
|
*/
|
||
|
void Micropolis::doMonsterSprite(SimSprite *sprite)
|
||
|
{
|
||
|
static const short Gx[5] = { 2, 2, -2, -2, 0 };
|
||
|
static const short Gy[5] = { -2, 2, 2, -2, 0 };
|
||
|
static const short ND1[4] = { 0, 1, 2, 3 };
|
||
|
static const short ND2[4] = { 1, 2, 3, 0 };
|
||
|
static const short nn1[4] = { 2, 5, 8, 11 };
|
||
|
static const short nn2[4] = { 11, 2, 5, 8 };
|
||
|
short d, z, c;
|
||
|
|
||
|
if (sprite->soundCount > 0) {
|
||
|
sprite->soundCount--;
|
||
|
}
|
||
|
|
||
|
if (sprite->control < 0) {
|
||
|
/* business as usual */
|
||
|
|
||
|
if (sprite->control == -2) {
|
||
|
|
||
|
d = (sprite->frame - 1) / 3;
|
||
|
z = (sprite->frame - 1) % 3;
|
||
|
|
||
|
if (z == 2) {
|
||
|
sprite->step = 0;
|
||
|
}
|
||
|
|
||
|
if (z == 0) {
|
||
|
sprite->step = 1;
|
||
|
}
|
||
|
|
||
|
if (sprite->step) {
|
||
|
z++;
|
||
|
} else {
|
||
|
z--;
|
||
|
}
|
||
|
|
||
|
c = getDir(sprite->x, sprite->y, sprite->destX, sprite->destY);
|
||
|
|
||
|
if (absDist < 18) {
|
||
|
|
||
|
sprite->control = -1;
|
||
|
sprite->count = 1000;
|
||
|
sprite->flag = 1;
|
||
|
sprite->destX = sprite->origX;
|
||
|
sprite->destY = sprite->origY;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
c = (c - 1) / 2;
|
||
|
|
||
|
if ((c != d && getRandom(5) == 0) || getRandom(20) == 0) {
|
||
|
|
||
|
int diff = (c - d) & 3;
|
||
|
|
||
|
if (diff == 1 || diff == 3) {
|
||
|
d = c;
|
||
|
} else {
|
||
|
|
||
|
if (getRandom16() & 1) {
|
||
|
d++;
|
||
|
} else {
|
||
|
d--;
|
||
|
}
|
||
|
|
||
|
d &= 3;
|
||
|
}
|
||
|
} else {
|
||
|
|
||
|
if (getRandom(20) == 0) {
|
||
|
|
||
|
if (getRandom16() & 1) {
|
||
|
d++;
|
||
|
} else {
|
||
|
d--;
|
||
|
}
|
||
|
|
||
|
d &= 3;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
|
||
|
d = (sprite->frame - 1) / 3;
|
||
|
|
||
|
if (d < 4) { /* turn n s e w */
|
||
|
|
||
|
z = (sprite->frame - 1) % 3;
|
||
|
|
||
|
if (z == 2) {
|
||
|
sprite->step = 0;
|
||
|
}
|
||
|
|
||
|
if (z == 0) {
|
||
|
sprite->step = 1;
|
||
|
}
|
||
|
|
||
|
if (sprite->step) {
|
||
|
z++;
|
||
|
} else {
|
||
|
z--;
|
||
|
}
|
||
|
|
||
|
getDir(sprite->x, sprite->y, sprite->destX, sprite->destY);
|
||
|
|
||
|
if (absDist < 60) {
|
||
|
|
||
|
if (sprite->flag == 0) {
|
||
|
|
||
|
sprite->flag = 1;
|
||
|
sprite->destX = sprite->origX;
|
||
|
sprite->destY = sprite->origY;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
sprite->frame = 0;
|
||
|
return;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
c = getDir(sprite->x, sprite->y, sprite->destX, sprite->destY);
|
||
|
c = (c - 1) / 2;
|
||
|
|
||
|
if ((c != d) && (!getRandom(10))) {
|
||
|
|
||
|
if (getRandom16() & 1) {
|
||
|
z = ND1[d];
|
||
|
} else {
|
||
|
z = ND2[d];
|
||
|
}
|
||
|
|
||
|
d = 4;
|
||
|
|
||
|
if (!sprite->soundCount) {
|
||
|
// Convert sprite coordinates to tile coordinates.
|
||
|
int monsterX = sprite->x >>4;
|
||
|
int monsterY = sprite->y >>4;
|
||
|
makeSound("city", "Monster", monsterX, monsterY); /* monster */
|
||
|
sprite->soundCount = 50 + getRandom(100);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
d = 4;
|
||
|
c = sprite->frame;
|
||
|
z = (c - 13) & 3;
|
||
|
|
||
|
if (!(getRandom16() & 3)) {
|
||
|
|
||
|
if (getRandom16() & 1) {
|
||
|
z = nn1[z];
|
||
|
} else {
|
||
|
z = nn2[z];
|
||
|
}
|
||
|
|
||
|
d = (z - 1) / 3;
|
||
|
z = (z - 1) % 3;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
/* somebody's taken control of the monster */
|
||
|
|
||
|
d = sprite->control;
|
||
|
z = (sprite->frame - 1) % 3;
|
||
|
|
||
|
if (z == 2) {
|
||
|
sprite->step = 0;
|
||
|
}
|
||
|
|
||
|
if (z == 0) {
|
||
|
sprite->step = 1;
|
||
|
}
|
||
|
|
||
|
if (sprite->step) {
|
||
|
z++;
|
||
|
} else {
|
||
|
z--;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
z = d * 3 + z + 1;
|
||
|
|
||
|
if (z > 16) {
|
||
|
z = 16;
|
||
|
}
|
||
|
|
||
|
sprite->frame = z;
|
||
|
|
||
|
sprite->x += Gx[d];
|
||
|
sprite->y += Gy[d];
|
||
|
|
||
|
if (sprite->count > 0) {
|
||
|
sprite->count--;
|
||
|
}
|
||
|
|
||
|
c = getChar(sprite->x + sprite->xHot, sprite->y + sprite->yHot);
|
||
|
|
||
|
if (c == -1
|
||
|
|| (c == RIVER && sprite->count != 0 && sprite->control == -1)) {
|
||
|
sprite->frame = 0; /* kill scary monster */
|
||
|
}
|
||
|
|
||
|
{
|
||
|
SimSprite *s;
|
||
|
for (s = spriteList; s != NULL; s = s->next) {
|
||
|
if (s->frame != 0 &&
|
||
|
(s->type == SPRITE_AIRPLANE || s->type == SPRITE_HELICOPTER
|
||
|
|| s->type == SPRITE_SHIP || s->type == SPRITE_TRAIN) &&
|
||
|
checkSpriteCollision(sprite, s)) {
|
||
|
explodeSprite(s);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
destroyMapTile(sprite->x + 48, sprite->y + 16);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Move tornado.
|
||
|
* @param sprite Tornado sprite to move.
|
||
|
* @todo Remove local magic constants and document the code.
|
||
|
*/
|
||
|
void Micropolis::doTornadoSprite(SimSprite *sprite)
|
||
|
{
|
||
|
static const short CDx[9] = { 2, 3, 2, 0, -2, -3 };
|
||
|
static const short CDy[9] = { -2, 0, 2, 3, 2, 0 };
|
||
|
short z;
|
||
|
|
||
|
z = sprite->frame;
|
||
|
|
||
|
if (z == 2) {
|
||
|
|
||
|
/* cycle animation... post Rel */
|
||
|
|
||
|
if (sprite->flag) {
|
||
|
z = 3;
|
||
|
} else {
|
||
|
z = 1;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
if (z == 1) {
|
||
|
sprite->flag = 1;
|
||
|
} else {
|
||
|
sprite->flag = 0;
|
||
|
}
|
||
|
|
||
|
z = 2;
|
||
|
}
|
||
|
|
||
|
if (sprite->count > 0) {
|
||
|
sprite->count--;
|
||
|
}
|
||
|
|
||
|
sprite->frame = z;
|
||
|
|
||
|
{
|
||
|
SimSprite *s;
|
||
|
for (s = spriteList; s != NULL; s = s->next) {
|
||
|
if (s->frame != 0 &&
|
||
|
(s->type == SPRITE_AIRPLANE || s->type == SPRITE_HELICOPTER
|
||
|
|| s->type == SPRITE_SHIP || s->type == SPRITE_TRAIN) &&
|
||
|
checkSpriteCollision(sprite, s)) {
|
||
|
explodeSprite(s);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
z = getRandom(5);
|
||
|
sprite->x += CDx[z];
|
||
|
sprite->y += CDy[z];
|
||
|
|
||
|
if (spriteNotInBounds(sprite)) {
|
||
|
sprite->frame = 0;
|
||
|
}
|
||
|
|
||
|
if (sprite->count != 0 && getRandom(500) == 0) {
|
||
|
sprite->frame = 0;
|
||
|
}
|
||
|
|
||
|
destroyMapTile(sprite->x + 48, sprite->y + 40);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* 'Move' fire sprite.
|
||
|
* @param sprite Fire sprite.
|
||
|
*/
|
||
|
void Micropolis::doExplosionSprite(SimSprite *sprite)
|
||
|
{
|
||
|
short x, y;
|
||
|
|
||
|
if ((spriteCycle & 1) == 0) {
|
||
|
|
||
|
if (sprite->frame == 1) {
|
||
|
// Convert sprite coordinates to tile coordinates.
|
||
|
int explosionX = sprite->x >>4;
|
||
|
int explosionY = sprite->y >>4;
|
||
|
makeSound("city", "ExplosionHigh", explosionX, explosionY); /* explosion */
|
||
|
x = (sprite->x >>4) + 3;
|
||
|
y = (sprite->y >>4);
|
||
|
sendMessage(MESSAGE_EXPLOSION_REPORTED, x, y);
|
||
|
}
|
||
|
|
||
|
sprite->frame++;
|
||
|
}
|
||
|
|
||
|
if (sprite->frame > 6) {
|
||
|
sprite->frame = 0;
|
||
|
|
||
|
startFire(sprite->x + 48 - 8, sprite->y + 16);
|
||
|
startFire(sprite->x + 48 - 24, sprite->y);
|
||
|
startFire(sprite->x + 48 + 8, sprite->y);
|
||
|
startFire(sprite->x + 48 - 24, sprite->y + 32);
|
||
|
startFire(sprite->x + 48 + 8, sprite->y + 32);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Move bus sprite.
|
||
|
* @param sprite Bus sprite.
|
||
|
* @todo Remove local magic constants and document the code.
|
||
|
*/
|
||
|
void Micropolis::doBusSprite(SimSprite *sprite)
|
||
|
{
|
||
|
static const short Dx[5] = { 0, 1, 0, -1, 0 };
|
||
|
static const short Dy[5] = { -1, 0, 1, 0, 0 };
|
||
|
static const short Dir2Frame[4] = { 1, 2, 1, 2 };
|
||
|
int dx, dy, tx, ty, otx, oty;
|
||
|
int turned = 0;
|
||
|
int speed = 0;
|
||
|
int z;
|
||
|
|
||
|
#ifdef DEBUGBUS
|
||
|
printf("Bus dir %d turn %d frame %d\n",
|
||
|
sprite->dir, sprite->turn, sprite->frame);
|
||
|
#endif
|
||
|
|
||
|
if (sprite->turn) {
|
||
|
|
||
|
if (sprite->turn < 0) { /* ccw */
|
||
|
|
||
|
if (sprite->dir & 1) { /* up or down */
|
||
|
sprite->frame = 4;
|
||
|
} else { /* left or right */
|
||
|
sprite->frame = 3;
|
||
|
}
|
||
|
|
||
|
sprite->turn++;
|
||
|
sprite->dir = (sprite->dir - 1) & 3;
|
||
|
|
||
|
} else { /* cw */
|
||
|
|
||
|
if (sprite->dir & 1) { /* up or down */
|
||
|
sprite->frame = 3;
|
||
|
} else { /* left or right */
|
||
|
sprite->frame = 4;
|
||
|
}
|
||
|
|
||
|
sprite->turn--;
|
||
|
sprite->dir = (sprite->dir + 1) & 3;
|
||
|
|
||
|
}
|
||
|
|
||
|
turned = 1;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
/* finish turn */
|
||
|
if ((sprite->frame == 3) ||
|
||
|
(sprite->frame == 4)) {
|
||
|
turned = 1;
|
||
|
sprite->frame = Dir2Frame[sprite->dir];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (sprite->speed == 0) {
|
||
|
|
||
|
/* brake */
|
||
|
dx = 0; dy = 0;
|
||
|
|
||
|
} else { /* cruise at traffic speed */
|
||
|
|
||
|
tx = (sprite->x + sprite->xHot) >>5;
|
||
|
ty = (sprite->y + sprite->yHot) >>5;
|
||
|
|
||
|
if (tx >= 0 && tx < WORLD_W_2 && ty >= 0 && ty < WORLD_H_2) {
|
||
|
|
||
|
z = trafficDensityMap.worldGet(tx << 1, ty << 1) >>6;
|
||
|
|
||
|
if (z > 1) {
|
||
|
z--;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
z = 0;
|
||
|
|
||
|
}
|
||
|
|
||
|
switch (z) {
|
||
|
|
||
|
case 0:
|
||
|
speed = 8;
|
||
|
break;
|
||
|
|
||
|
case 1:
|
||
|
speed = 4;
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
speed = 1;
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
/* govern speed */
|
||
|
if (speed > sprite->speed) {
|
||
|
speed = sprite->speed;
|
||
|
}
|
||
|
|
||
|
if (turned) {
|
||
|
|
||
|
#ifdef DEBUGBUS
|
||
|
printf("turned\n");
|
||
|
#endif
|
||
|
|
||
|
if (speed > 1) {
|
||
|
speed = 1;
|
||
|
}
|
||
|
|
||
|
dx = Dx[sprite->dir] * speed;
|
||
|
dy = Dy[sprite->dir] * speed;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
dx = Dx[sprite->dir] * speed;
|
||
|
dy = Dy[sprite->dir] * speed;
|
||
|
|
||
|
tx = (sprite->x + sprite->xHot) >>4;
|
||
|
ty = (sprite->y + sprite->yHot) >>4;
|
||
|
|
||
|
/* drift into the right lane */
|
||
|
switch (sprite->dir) {
|
||
|
|
||
|
case 0: /* up */
|
||
|
|
||
|
z = ((tx <<4) + 4) - (sprite->x + sprite->xHot);
|
||
|
|
||
|
if (z < 0) {
|
||
|
dx = -1;
|
||
|
} else if (z > 0) {
|
||
|
dx = 1;
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUGBUS
|
||
|
printf("moving up x %x z %d dx %d\n", sprite->x + sprite->xHot, z, dx);
|
||
|
#endif
|
||
|
|
||
|
break;
|
||
|
|
||
|
case 1: /* right */
|
||
|
|
||
|
z = ((ty <<4) + 4) - (sprite->y + sprite->yHot);
|
||
|
|
||
|
if (z < 0) {
|
||
|
dy = -1;
|
||
|
} else if (z > 0) {
|
||
|
dy = 1;
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUGBUS
|
||
|
printf("moving right y %x z %d dy %d\n", sprite->y + sprite->yHot, z, dy);
|
||
|
#endif
|
||
|
|
||
|
break;
|
||
|
|
||
|
case 2: /* down */
|
||
|
|
||
|
z = (tx <<4) - (sprite->x + sprite->xHot);
|
||
|
|
||
|
if (z < 0) {
|
||
|
dx = -1;
|
||
|
} else if (z > 0) {
|
||
|
dx = 1;
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUGBUS
|
||
|
printf("moving down x %x z %d dx %d\n", sprite->x + sprite->xHot, z, dx);
|
||
|
#endif
|
||
|
|
||
|
break;
|
||
|
|
||
|
case 3: /* left */
|
||
|
|
||
|
z = (ty <<4) - (sprite->y + sprite->yHot);
|
||
|
|
||
|
if (z < 0) {
|
||
|
dy = -1;
|
||
|
} else if (z > 0) {
|
||
|
dy = 1;
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUGBUS
|
||
|
printf("moving left y %x z %d dy %d\n", sprite->y + sprite->yHot, z, dy);
|
||
|
#endif
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUGBUS
|
||
|
printf("speed dx %d dy %d\n", dx, dy);
|
||
|
#endif
|
||
|
|
||
|
#define AHEAD 8
|
||
|
|
||
|
otx = (sprite->x + sprite->xHot + (Dx[sprite->dir] * AHEAD)) >>4;
|
||
|
oty = (sprite->y + sprite->yHot + (Dy[sprite->dir] * AHEAD)) >>4;
|
||
|
|
||
|
otx = clamp(otx, 0, WORLD_W - 1);
|
||
|
oty = clamp(oty, 0, WORLD_H - 1);
|
||
|
|
||
|
tx = (sprite->x + sprite->xHot + dx + (Dx[sprite->dir] * AHEAD)) >>4;
|
||
|
ty = (sprite->y + sprite->yHot + dy + (Dy[sprite->dir] * AHEAD)) >>4;
|
||
|
|
||
|
tx = clamp(tx, 0, WORLD_W - 1);
|
||
|
ty = clamp(ty, 0, WORLD_H - 1);
|
||
|
|
||
|
if (tx != otx || ty != oty) {
|
||
|
|
||
|
#ifdef DEBUGBUS
|
||
|
printf("drive from tile %d %d to %d %d\n",
|
||
|
otx, oty, tx, ty);
|
||
|
#endif
|
||
|
|
||
|
z = canDriveOn(tx, ty);
|
||
|
|
||
|
if (z == 0) {
|
||
|
|
||
|
/* can't drive forward into a new tile */
|
||
|
if (speed == 8) {
|
||
|
bulldozerTool(tx, ty);
|
||
|
} else {
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
/* drive forward into a new tile */
|
||
|
if (z > 0) {
|
||
|
/* smooth */
|
||
|
} else {
|
||
|
/* bumpy */
|
||
|
dx /= 2;
|
||
|
dy /= 2;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
tx = (sprite->x + sprite->xHot + dx) >>4;
|
||
|
ty = (sprite->y + sprite->yHot + dy) >>4;
|
||
|
|
||
|
z = canDriveOn(tx, ty);
|
||
|
|
||
|
if (z > 0) {
|
||
|
/* cool, cruise along */
|
||
|
} else {
|
||
|
if (z < 0) {
|
||
|
/* bumpy */
|
||
|
} else {
|
||
|
/* something in the way */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sprite->x += dx;
|
||
|
sprite->y += dy;
|
||
|
|
||
|
if (enableDisasters) {
|
||
|
SimSprite *s;
|
||
|
int explode = 0;
|
||
|
|
||
|
for (s = spriteList; s != NULL; s = s->next) {
|
||
|
if (sprite != s && s->frame != 0
|
||
|
&& (s->type == SPRITE_BUS
|
||
|
|| (s->type == SPRITE_TRAIN && s->frame != 5))
|
||
|
&& checkSpriteCollision(sprite, s)) {
|
||
|
explodeSprite(s);
|
||
|
explode = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (explode) {
|
||
|
explodeSprite(sprite);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Can one drive at the specified tile?
|
||
|
* @param x X coordinate at map.
|
||
|
* @param y Y coordinate at map.
|
||
|
* @return 0 if not, 1 if you can, -1 otherwise
|
||
|
*/
|
||
|
int Micropolis::canDriveOn(int x, int y)
|
||
|
{
|
||
|
int tile;
|
||
|
|
||
|
if (!testBounds(x, y)) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
tile = map[x][y] & LOMASK;
|
||
|
|
||
|
if ((tile >= ROADBASE && tile <= LASTROAD && tile != BRWH && tile != BRWV)
|
||
|
|| tile == HRAILROAD || tile == VRAILROAD) {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
if (tile == DIRT || tally(tile)) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Handle explosion of sprite (mostly due to collision?).
|
||
|
* @param sprite that should explode.
|
||
|
* @todo Add a 'bus crashed' message to #MessageNumber.
|
||
|
*/
|
||
|
void Micropolis::explodeSprite(SimSprite *sprite)
|
||
|
{
|
||
|
int x, y;
|
||
|
|
||
|
sprite->frame = 0;
|
||
|
|
||
|
x = sprite->x + sprite->xHot;
|
||
|
y = sprite->y + sprite->yHot;
|
||
|
makeExplosionAt(x, y);
|
||
|
|
||
|
x = (x >>4);
|
||
|
y = (y >>4);
|
||
|
|
||
|
switch (sprite->type) {
|
||
|
|
||
|
case SPRITE_AIRPLANE:
|
||
|
sendMessage(MESSAGE_PLANE_CRASHED, x, y, true);
|
||
|
break;
|
||
|
|
||
|
case SPRITE_SHIP:
|
||
|
sendMessage(MESSAGE_SHIP_CRASHED, x, y, true);
|
||
|
break;
|
||
|
|
||
|
case SPRITE_TRAIN:
|
||
|
sendMessage(MESSAGE_TRAIN_CRASHED, x, y, true);
|
||
|
break;
|
||
|
|
||
|
case SPRITE_HELICOPTER:
|
||
|
sendMessage(MESSAGE_HELICOPTER_CRASHED, x, y, true);
|
||
|
break;
|
||
|
|
||
|
case SPRITE_BUS:
|
||
|
sendMessage(MESSAGE_TRAIN_CRASHED, x, y, true); /* XXX for now */
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
// Convert sprite coordinates to tile coordinates.
|
||
|
makeSound("city", "ExplosionHigh", x, y); /* explosion */
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool Micropolis::checkWet(int x)
|
||
|
{
|
||
|
if (x == HPOWER || x == VPOWER || x == HRAIL || x == VRAIL
|
||
|
|| x == BRWH || x == BRWV) {
|
||
|
return true;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Destroy a map tile.
|
||
|
* @param ox X coordinate in pixels.
|
||
|
* @param oy Y coordinate in pixels.
|
||
|
*/
|
||
|
void Micropolis::destroyMapTile(int ox, int oy)
|
||
|
{
|
||
|
short t, z, x, y;
|
||
|
|
||
|
x = ox >>4;
|
||
|
y = oy >>4;
|
||
|
|
||
|
if (!testBounds(x, y)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
z = map[x][y];
|
||
|
t = z & LOMASK;
|
||
|
|
||
|
if (t >= TREEBASE) {
|
||
|
if (!(z & BURNBIT)) {
|
||
|
|
||
|
if (t >= ROADBASE && t <= LASTROAD) {
|
||
|
map[x][y] = RIVER;
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (z & ZONEBIT) {
|
||
|
|
||
|
startFireInZone(x, y, z);
|
||
|
|
||
|
if (t > RZB) {
|
||
|
makeExplosionAt(ox, oy);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if (checkWet(t)) {
|
||
|
map[x][y] = RIVER;
|
||
|
} else {
|
||
|
map[x][y] = (doAnimation ? TINYEXP : (LASTTINYEXP - 3))
|
||
|
| BULLBIT | ANIMBIT;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Start a fire in a zone.
|
||
|
* @param Xloc X coordinate in map coordinate.
|
||
|
* @param Yloc Y coordinate in map coordinate.
|
||
|
* @param ch Map character at (\a Xloc, \a Yloc).
|
||
|
*/
|
||
|
void Micropolis::startFireInZone(int Xloc, int Yloc, int ch)
|
||
|
{
|
||
|
short Xtem, Ytem;
|
||
|
short x, y, XYmax;
|
||
|
|
||
|
int value = rateOfGrowthMap.worldGet(Xloc, Yloc);
|
||
|
value = clamp(value - 20, -200, 200);
|
||
|
rateOfGrowthMap.worldSet(Xloc, Yloc, value);
|
||
|
|
||
|
ch &= LOMASK;
|
||
|
|
||
|
if (ch < PORTBASE) {
|
||
|
XYmax = 2;
|
||
|
} else {
|
||
|
if (ch == AIRPORT) {
|
||
|
XYmax = 5;
|
||
|
} else {
|
||
|
XYmax = 4;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (x = -1; x < XYmax; x++) {
|
||
|
for (y = -1; y < XYmax; y++) {
|
||
|
|
||
|
Xtem = Xloc + x;
|
||
|
Ytem = Yloc + y;
|
||
|
|
||
|
if (testBounds(Xtem, Ytem) && (map[Xtem][Ytem] & LOMASK) >= ROADBASE) {
|
||
|
map[Xtem][Ytem] |= BULLBIT;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Start a fire at a single tile.
|
||
|
* @param x X coordinate in map coordinate.
|
||
|
* @param y Y coordinate in map coordinate.
|
||
|
*/
|
||
|
void Micropolis::startFire(int x, int y)
|
||
|
{
|
||
|
int t, z;
|
||
|
|
||
|
x >>= 4;
|
||
|
y >>= 4;
|
||
|
|
||
|
if (!testBounds(x, y)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
z = map[x][y];
|
||
|
t = z & LOMASK;
|
||
|
|
||
|
if (!(z & BURNBIT) && t != DIRT) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (z & ZONEBIT) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
map[x][y] = randomFire();
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Try to start a new train sprite at the given map tile.
|
||
|
* @param x X coordinate in map coordinate.
|
||
|
* @param y Y coordinate in map coordinate.
|
||
|
*/
|
||
|
void Micropolis::generateTrain(int x, int y)
|
||
|
{
|
||
|
if (totalPop > 20 && getSprite(SPRITE_TRAIN) == NULL && getRandom(25) == 0) {
|
||
|
makeSprite(SPRITE_TRAIN, (x <<4) + TRA_GROOVE_X, (y <<4) + TRA_GROOVE_Y);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Try to start a new bus sprite at the given map tile.
|
||
|
* @param x X coordinate in map coordinate.
|
||
|
* @param y Y coordinate in map coordinate.
|
||
|
*/
|
||
|
void Micropolis::generateBus(int x, int y)
|
||
|
{
|
||
|
if (getSprite(SPRITE_BUS) == NULL && getRandom(25) == 0) {
|
||
|
makeSprite(SPRITE_BUS, (x <<4) + BUS_GROOVE_X, (y <<4) + BUS_GROOVE_Y);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/** Try to construct a new ship sprite */
|
||
|
void Micropolis::generateShip()
|
||
|
{
|
||
|
short x, y;
|
||
|
|
||
|
if (!(getRandom16() & 3)) {
|
||
|
for (x = 4; x < WORLD_W - 2; x++) {
|
||
|
if (map[x][0] == CHANNEL) {
|
||
|
makeShipHere(x, 0);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!(getRandom16() & 3)) {
|
||
|
for (y = 1; y < WORLD_H - 2; y++) {
|
||
|
if (map[0][y] == CHANNEL) {
|
||
|
makeShipHere(0, y);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!(getRandom16() & 3)) {
|
||
|
for (x = 4; x < WORLD_W - 2; x++) {
|
||
|
if (map[x][WORLD_H - 1] == CHANNEL) {
|
||
|
makeShipHere(x, WORLD_H - 1);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!(getRandom16() & 3)) {
|
||
|
for (y = 1; y < WORLD_H - 2; y++) {
|
||
|
if (map[WORLD_W - 1][y] == CHANNEL) {
|
||
|
makeShipHere(WORLD_W - 1, y);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Start a new ship sprite at the given map tile.
|
||
|
* @param x X coordinate in map coordinate.
|
||
|
* @param y Y coordinate in map coordinate.
|
||
|
*/
|
||
|
void Micropolis::makeShipHere(int x, int y)
|
||
|
{
|
||
|
makeSprite(SPRITE_SHIP, (x <<4) - (48 - 1), (y <<4));
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Start a new monster sprite.
|
||
|
* @todo Make monster over land, because it disappears if it's made over water.
|
||
|
* Better yet make monster not disappear for a while after it's created,
|
||
|
* over land or water. Should never disappear prematurely.
|
||
|
*/
|
||
|
void Micropolis::makeMonster()
|
||
|
{
|
||
|
int x, y, z, done = 0;
|
||
|
SimSprite *sprite;
|
||
|
|
||
|
sprite = getSprite(SPRITE_MONSTER);
|
||
|
if (sprite != NULL) {
|
||
|
sprite->soundCount = 1;
|
||
|
sprite->count = 1000;
|
||
|
sprite->destX = pollutionMaxX <<4;
|
||
|
sprite->destY = pollutionMaxY <<4;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (z = 0; z < 300; z++) {
|
||
|
|
||
|
x = getRandom(WORLD_W - 20) + 10;
|
||
|
y = getRandom(WORLD_H - 10) + 5;
|
||
|
|
||
|
if (map[x][y] == RIVER || map[x][y] == RIVER + BULLBIT) {
|
||
|
makeMonsterAt(x, y);
|
||
|
done = 1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if (!done) {
|
||
|
makeMonsterAt(60, 50);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Start a new monster sprite at the given map tile.
|
||
|
* @param x X coordinate in map coordinate.
|
||
|
* @param y Y coordinate in map coordinate.
|
||
|
*/
|
||
|
void Micropolis::makeMonsterAt(int x, int y)
|
||
|
{
|
||
|
makeSprite(SPRITE_MONSTER, (x << 4) + 48, (y << 4));
|
||
|
sendMessage(MESSAGE_MONSTER_SIGHTED, x + 5, y, true, true);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Ensure a helicopter sprite exists.
|
||
|
*
|
||
|
* If it does not exist, create one at the given coordinates.
|
||
|
* @param pos Start position in map coordinates.
|
||
|
*/
|
||
|
void Micropolis::generateCopter(const Position &pos)
|
||
|
{
|
||
|
if (getSprite(SPRITE_HELICOPTER) != NULL) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
makeSprite(SPRITE_HELICOPTER, (pos.posX << 4), (pos.posY << 4) + 30);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Ensure an airplane sprite exists.
|
||
|
*
|
||
|
* If it does not exist, create one at the given coordinates.
|
||
|
* @param pos Start position in map coordinates.
|
||
|
*/
|
||
|
void Micropolis::generatePlane(const Position &pos)
|
||
|
{
|
||
|
if (getSprite(SPRITE_AIRPLANE) != NULL) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
makeSprite(SPRITE_AIRPLANE, (pos.posX <<4) + 48, (pos.posY <<4) + 12);
|
||
|
}
|
||
|
|
||
|
|
||
|
/** Ensure a tornado sprite exists. */
|
||
|
void Micropolis::makeTornado()
|
||
|
{
|
||
|
short x, y;
|
||
|
SimSprite *sprite;
|
||
|
|
||
|
sprite = getSprite(SPRITE_TORNADO);
|
||
|
if (sprite != NULL) {
|
||
|
sprite->count = 200;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
x = getRandom((WORLD_W <<4) - 800) + 400;
|
||
|
y = getRandom((WORLD_H <<4) - 200) + 100;
|
||
|
|
||
|
makeSprite(SPRITE_TORNADO, x, y);
|
||
|
sendMessage(MESSAGE_TORNADO_SIGHTED, (x >>4) + 3, (y >>4) + 2, true, true);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Construct an explosion sprite.
|
||
|
* @param x X coordinate of the explosion (in map coordinates).
|
||
|
* @param y Y coordinate of the explosion (in map coordinates).
|
||
|
*/
|
||
|
void Micropolis::makeExplosion(int x, int y)
|
||
|
{
|
||
|
if (testBounds(x, y)) {
|
||
|
makeExplosionAt((x << 4) + 8, (y << 4) + 8);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Construct an explosion sprite.
|
||
|
* @param x X coordinate of the explosion (in pixels).
|
||
|
* @param y Y coordinate of the explosion (in pixels).
|
||
|
*/
|
||
|
void Micropolis::makeExplosionAt( int x, int y)
|
||
|
{
|
||
|
newSprite("", SPRITE_EXPLOSION, x - 40, y - 16);
|
||
|
}
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|