/* $Id$ */
/** @file effectvehicle.cpp Implementation of everything generic to vehicles. */
#include "stdafx.h"
#include "openttd.h"
#include "road_map.h"
#include "roadveh.h"
#include "ship.h"
#include "spritecache.h"
#include "tile_cmd.h"
#include "landscape.h"
#include "timetable.h"
#include "viewport_func.h"
#include "gfx_func.h"
#include "news_func.h"
#include "command_func.h"
#include "saveload.h"
#include "player_func.h"
#include "debug.h"
#include "vehicle_gui.h"
#include "rail_type.h"
#include "train.h"
#include "aircraft.h"
#include "industry_map.h"
#include "station_map.h"
#include "water_map.h"
#include "yapf/yapf.h"
#include "newgrf_callbacks.h"
#include "newgrf_engine.h"
#include "newgrf_sound.h"
#include "group.h"
#include "order_func.h"
#include "strings_func.h"
#include "zoom_func.h"
#include "functions.h"
#include "date_func.h"
#include "window_func.h"
#include "vehicle_func.h"
#include "signal_func.h"
#include "sound_func.h"
#include "variables.h"
#include "autoreplace_func.h"
#include "autoreplace_gui.h"
#include "string_func.h"
#include "settings_type.h"
#include "oldpool_func.h"
#include "depot_map.h"
#include "animated_tile_func.h"
#include "effectvehicle_base.h"
#include "effectvehicle_func.h"
#include "table/sprites.h"
#include "table/strings.h"
static void ChimneySmokeInit(Vehicle *v)
{
uint32 r = Random();
v->cur_image = SPR_CHIMNEY_SMOKE_0 + GB(r, 0, 3);
v->progress = GB(r, 16, 3);
}
static void ChimneySmokeTick(Vehicle *v)
{
if (v->progress > 0) {
v->progress--;
} else {
BeginVehicleMove(v);
TileIndex tile = TileVirtXY(v->x_pos, v->y_pos);
if (!IsTileType(tile, MP_INDUSTRY)) {
EndVehicleMove(v);
delete v;
return;
}
if (v->cur_image != SPR_CHIMNEY_SMOKE_7) {
v->cur_image++;
} else {
v->cur_image = SPR_CHIMNEY_SMOKE_0;
}
v->progress = 7;
VehiclePositionChanged(v);
EndVehicleMove(v);
}
}
static void SteamSmokeInit(Vehicle *v)
{
v->cur_image = SPR_STEAM_SMOKE_0;
v->progress = 12;
}
static void SteamSmokeTick(Vehicle *v)
{
bool moved = false;
BeginVehicleMove(v);
v->progress++;
if ((v->progress & 7) == 0) {
v->z_pos++;
moved = true;
}
if ((v->progress & 0xF) == 4) {
if (v->cur_image != SPR_STEAM_SMOKE_4) {
v->cur_image++;
} else {
EndVehicleMove(v);
delete v;
return;
}
moved = true;
}
if (moved) {
VehiclePositionChanged(v);
EndVehicleMove(v);
}
}
static void DieselSmokeInit(Vehicle *v)
{
v->cur_image = SPR_DIESEL_SMOKE_0;
v->progress = 0;
}
static void DieselSmokeTick(Vehicle *v)
{
v->progress++;
if ((v->progress & 3) == 0) {
BeginVehicleMove(v);
v->z_pos++;
VehiclePositionChanged(v);
EndVehicleMove(v);
} else if ((v->progress & 7) == 1) {
BeginVehicleMove(v);
if (v->cur_image != SPR_DIESEL_SMOKE_5) {
v->cur_image++;
VehiclePositionChanged(v);
EndVehicleMove(v);
} else {
EndVehicleMove(v);
delete v;
}
}
}
static void ElectricSparkInit(Vehicle *v)
{
v->cur_image = SPR_ELECTRIC_SPARK_0;
v->progress = 1;
}
static void ElectricSparkTick(Vehicle *v)
{
if (v->progress < 2) {
v->progress++;
} else {
v->progress = 0;
BeginVehicleMove(v);
if (v->cur_image != SPR_ELECTRIC_SPARK_5) {
v->cur_image++;
VehiclePositionChanged(v);
EndVehicleMove(v);
} else {
EndVehicleMove(v);
delete v;
}
}
}
static void SmokeInit(Vehicle *v)
{
v->cur_image = SPR_SMOKE_0;
v->progress = 12;
}
static void SmokeTick(Vehicle *v)
{
bool moved = false;
BeginVehicleMove(v);
v->progress++;
if ((v->progress & 3) == 0) {
v->z_pos++;
moved = true;
}
if ((v->progress & 0xF) == 4) {
if (v->cur_image != SPR_SMOKE_4) {
v->cur_image++;
} else {
EndVehicleMove(v);
delete v;
return;
}
moved = true;
}
if (moved) {
VehiclePositionChanged(v);
EndVehicleMove(v);
}
}
static void ExplosionLargeInit(Vehicle *v)
{
v->cur_image = SPR_EXPLOSION_LARGE_0;
v->progress = 0;
}
static void ExplosionLargeTick(Vehicle *v)
{
v->progress++;
if ((v->progress & 3) == 0) {
BeginVehicleMove(v);
if (v->cur_image != SPR_EXPLOSION_LARGE_F) {
v->cur_image++;
VehiclePositionChanged(v);
EndVehicleMove(v);
} else {
EndVehicleMove(v);
delete v;
}
}
}
static void BreakdownSmokeInit(Vehicle *v)
{
v->cur_image = SPR_BREAKDOWN_SMOKE_0;
v->progress = 0;
}
static void BreakdownSmokeTick(Vehicle *v)
{
v->progress++;
if ((v->progress & 7) == 0) {
BeginVehicleMove(v);
if (v->cur_image != SPR_BREAKDOWN_SMOKE_3) {
v->cur_image++;
} else {
v->cur_image = SPR_BREAKDOWN_SMOKE_0;
}
VehiclePositionChanged(v);
EndVehicleMove(v);
}
v->u.effect.animation_state--;
if (v->u.effect.animation_state == 0) {
BeginVehicleMove(v);
EndVehicleMove(v);
delete v;
}
}
static void ExplosionSmallInit(Vehicle *v)
{
v->cur_image = SPR_EXPLOSION_SMALL_0;
v->progress = 0;
}
static void ExplosionSmallTick(Vehicle *v)
{
v->progress++;
if ((v->progress & 3) == 0) {
BeginVehicleMove(v);
if (v->cur_image != SPR_EXPLOSION_SMALL_B) {
v->cur_image++;
VehiclePositionChanged(v);
EndVehicleMove(v);
} else {
EndVehicleMove(v);
delete v;
}
}
}
static void BulldozerInit(Vehicle *v)
{
v->cur_image = SPR_BULLDOZER_NE;
v->progress = 0;
v->u.effect.animation_state = 0;
v->u.effect.animation_substate = 0;
}
struct BulldozerMovement {
byte direction:2;
byte image:2;
byte duration:3;
};
static const BulldozerMovement _bulldozer_movement[] = {
{ 0, 0, 4 },
{ 3, 3, 4 },
{ 2, 2, 7 },
{ 0, 2, 7 },
{ 1, 1, 3 },
{ 2, 2, 7 },
{ 0, 2, 7 },
{ 1, 1, 3 },
{ 2, 2, 7 },
{ 0, 2, 7 },
{ 3, 3, 6 },
{ 2, 2, 6 },
{ 1, 1, 7 },
{ 3, 1, 7 },
{ 0, 0, 3 },
{ 1, 1, 7 },
{ 3, 1, 7 },
{ 0, 0, 3 },
{ 1, 1, 7 },
{ 3, 1, 7 }
};
static const struct {
int8 x;
int8 y;
} _inc_by_dir[] = {
{ -1, 0 },
{ 0, 1 },
{ 1, 0 },
{ 0, -1 }
};
static void BulldozerTick(Vehicle *v)
{
v->progress++;
if ((v->progress & 7) == 0) {
const BulldozerMovement* b = &_bulldozer_movement[v->u.effect.animation_state];
BeginVehicleMove(v);
v->cur_image = SPR_BULLDOZER_NE + b->image;
v->x_pos += _inc_by_dir[b->direction].x;
v->y_pos += _inc_by_dir[b->direction].y;
v->u.effect.animation_substate++;
if (v->u.effect.animation_substate >= b->duration) {
v->u.effect.animation_substate = 0;
v->u.effect.animation_state++;
if (v->u.effect.animation_state == lengthof(_bulldozer_movement)) {
EndVehicleMove(v);
delete v;
return;
}
}
VehiclePositionChanged(v);
EndVehicleMove(v);
}
}
static void BubbleInit(Vehicle *v)
{
v->cur_image = SPR_BUBBLE_GENERATE_0;
v->spritenum = 0;
v->progress = 0;
}
struct BubbleMovement {
int8 x:4;
int8 y:4;
int8 z:4;
byte image:4;
};
#define MK(x, y, z, i) { x, y, z, i }
#define ME(i) { i, 4, 0, 0 }
static const BubbleMovement _bubble_float_sw[] = {
MK(0, 0, 1, 0),
MK(1, 0, 1, 1),
MK(0, 0, 1, 0),
MK(1, 0, 1, 2),
ME(1)
};
static const BubbleMovement _bubble_float_ne[] = {
MK( 0, 0, 1, 0),
MK(-1, 0, 1, 1),
MK( 0, 0, 1, 0),
MK(-1, 0, 1, 2),
ME(1)
};
static const BubbleMovement _bubble_float_se[] = {
MK(0, 0, 1, 0),
MK(0, 1, 1, 1),
MK(0, 0, 1, 0),
MK(0, 1, 1, 2),
ME(1)
};
static const BubbleMovement _bubble_float_nw[] = {
MK(0, 0, 1, 0),
MK(0, -1, 1, 1),
MK(0, 0, 1, 0),
MK(0, -1, 1, 2),
ME(1)
};
static const BubbleMovement _bubble_burst[] = {
MK(0, 0, 1, 2),
MK(0, 0, 1, 7),
MK(0, 0, 1, 8),
MK(0, 0, 1, 9),
ME(0)
};
static const BubbleMovement _bubble_absorb[] = {
MK(0, 0, 1, 0),
MK(0, 0, 1, 1),
MK(0, 0, 1, 0),
MK(0, 0, 1, 2),
MK(0, 0, 1, 0),
MK(0, 0, 1, 1),
MK(0, 0, 1, 0),
MK(0, 0, 1, 2),
MK(0, 0, 1, 0),
MK(0, 0, 1, 1),
MK(0, 0, 1, 0),
MK(0, 0, 1, 2),
MK(0, 0, 1, 0),
MK(0, 0, 1, 1),
MK(0, 0, 1, 0),
MK(0, 0, 1, 2),
MK(0, 0, 1, 0),
MK(0, 0, 1, 1),
MK(0, 0, 1, 0),
MK(0, 0, 1, 2),
MK(0, 0, 1, 0),
MK(0, 0, 1, 1),
MK(0, 0, 1, 0),
MK(0, 0, 1, 2),
MK(0, 0, 1, 0),
MK(0, 0, 1, 1),
MK(0, 0, 1, 0),
MK(0, 0, 1, 2),
MK(0, 0, 1, 0),
MK(0, 0, 1, 1),
MK(0, 0, 1, 0),
MK(0, 0, 1, 2),
MK(0, 0, 1, 0),
MK(0, 0, 1, 1),
MK(0, 0, 1, 0),
MK(0, 0, 1, 2),
MK(0, 0, 1, 0),
MK(0, 0, 1, 1),
MK(0, 0, 1, 0),
MK(0, 0, 1, 2),
MK(0, 0, 1, 0),
MK(0, 0, 1, 1),
MK(0, 0, 1, 0),
MK(0, 0, 1, 2),
MK(0, 0, 1, 0),
MK(0, 0, 1, 1),
MK(0, 0, 1, 0),
MK(0, 0, 1, 2),
MK(0, 0, 1, 0),
MK(0, 0, 1, 1),
MK(0, 0, 1, 0),
MK(0, 0, 1, 2),
MK(0, 0, 1, 0),
MK(0, 0, 1, 1),
MK(0, 0, 1, 0),
MK(0, 0, 1, 2),
MK(0, 0, 1, 0),
MK(0, 0, 1, 1),
MK(0, 0, 1, 0),
MK(0, 0, 1, 2),
MK(0, 0, 1, 0),
MK(0, 0, 1, 1),
MK(2, 1, 3, 0),
MK(1, 1, 3, 1),
MK(2, 1, 3, 0),
MK(1, 1, 3, 2),
MK(2, 1, 3, 0),
MK(1, 1, 3, 1),
MK(2, 1, 3, 0),
MK(1, 0, 1, 2),
MK(0, 0, 1, 0),
MK(1, 0, 1, 1),
MK(0, 0, 1, 0),
MK(1, 0, 1, 2),
MK(0, 0, 1, 0),
MK(1, 0, 1, 1),
MK(0, 0, 1, 0),
MK(1, 0, 1, 2),
ME(2),
MK(0, 0, 0, 0xA),
MK(0, 0, 0, 0xB),
MK(0, 0, 0, 0xC),
MK(0, 0, 0, 0xD),
MK(0, 0, 0, 0xE),
ME(0)
};
#undef ME
#undef MK
static const BubbleMovement * const _bubble_movement[] = {
_bubble_float_sw,
_bubble_float_ne,
_bubble_float_se,
_bubble_float_nw,
_bubble_burst,
_bubble_absorb,
};
static void BubbleTick(Vehicle *v)
{
/*
* Warning: those effects can NOT use Random(), and have to use
* InteractiveRandom(), because somehow someone forgot to save
* spritenum to the savegame, and so it will cause desyncs in
* multiplayer!! (that is: in ToyLand)
*/
uint et;
v->progress++;
if ((v->progress & 3) != 0) return;
BeginVehicleMove(v);
if (v->spritenum == 0) {
v->cur_image++;
if (v->cur_image < SPR_BUBBLE_GENERATE_3) {
VehiclePositionChanged(v);
EndVehicleMove(v);
return;
}
if (v->u.effect.animation_substate != 0) {
v->spritenum = GB(InteractiveRandom(), 0, 2) + 1;
} else {
v->spritenum = 6;
}
et = 0;
} else {
et = v->engine_type + 1;
}
const BubbleMovement *b = &_bubble_movement[v->spritenum - 1][et];
if (b->y == 4 && b->x == 0) {
EndVehicleMove(v);
delete v;
return;
}
if (b->y == 4 && b->x == 1) {
if (v->z_pos > 180 || Chance16I(1, 96, InteractiveRandom())) {
v->spritenum = 5;
SndPlayVehicleFx(SND_2F_POP, v);
}
et = 0;
}
if (b->y == 4 && b->x == 2) {
TileIndex tile;
et++;
SndPlayVehicleFx(SND_31_EXTRACT, v);
tile = TileVirtXY(v->x_pos, v->y_pos);
if (IsTileType(tile, MP_INDUSTRY) && GetIndustryGfx(tile) == GFX_BUBBLE_CATCHER) AddAnimatedTile(tile);
}
v->engine_type = et;
b = &_bubble_movement[v->spritenum - 1][et];
v->x_pos += b->x;
v->y_pos += b->y;
v->z_pos += b->z;
v->cur_image = SPR_BUBBLE_0 + b->image;
VehiclePositionChanged(v);
EndVehicleMove(v);
}
typedef void EffectInitProc(Vehicle *v);
typedef void EffectTickProc(Vehicle *v);
static EffectInitProc * const _effect_init_procs[] = {
ChimneySmokeInit,
SteamSmokeInit,
DieselSmokeInit,
ElectricSparkInit,
SmokeInit,
ExplosionLargeInit,
BreakdownSmokeInit,
ExplosionSmallInit,
BulldozerInit,
BubbleInit,
};
static EffectTickProc * const _effect_tick_procs[] = {
ChimneySmokeTick,
SteamSmokeTick,
DieselSmokeTick,
ElectricSparkTick,
SmokeTick,
ExplosionLargeTick,
BreakdownSmokeTick,
ExplosionSmallTick,
BulldozerTick,
BubbleTick,
};
Vehicle *CreateEffectVehicle(int x, int y, int z, EffectVehicleType type)
{
Vehicle *v = new EffectVehicle();
if (v != NULL) {
v->subtype = type;
v->x_pos = x;
v->y_pos = y;
v->z_pos = z;
v->tile = 0;
v->UpdateDeltaXY(INVALID_DIR);
v->vehstatus = VS_UNCLICKABLE;
_effect_init_procs[type](v);
VehiclePositionChanged(v);
BeginVehicleMove(v);
EndVehicleMove(v);
}
return v;
}
Vehicle *CreateEffectVehicleAbove(int x, int y, int z, EffectVehicleType type)
{
int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
int safe_y = Clamp(y, 0, MapMaxY() * TILE_SIZE);
return CreateEffectVehicle(x, y, GetSlopeZ(safe_x, safe_y) + z, type);
}
Vehicle *CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVehicleType type)
{
return CreateEffectVehicle(v->x_pos + x, v->y_pos + y, v->z_pos + z, type);
}
void EffectVehicle::Tick()
{
_effect_tick_procs[this->subtype](this);
}
void EffectVehicle::UpdateDeltaXY(Direction direction)
{
this->x_offs = 0;
this->y_offs = 0;
this->x_extent = 1;
this->y_extent = 1;
this->z_extent = 1;
}