mirror of https://github.com/OpenTTD/OpenTTD
(svn r6532) - Feature: Add support for NewGRF sound effects. Currently sound priority isn't supported.
parent
3ded010d91
commit
653e7fa548
1
Makefile
1
Makefile
|
@ -697,6 +697,7 @@ SRCS += network_udp.c
|
||||||
SRCS += newgrf.c
|
SRCS += newgrf.c
|
||||||
SRCS += newgrf_cargo.c
|
SRCS += newgrf_cargo.c
|
||||||
SRCS += newgrf_engine.c
|
SRCS += newgrf_engine.c
|
||||||
|
SRCS += newgrf_sound.c
|
||||||
SRCS += newgrf_spritegroup.c
|
SRCS += newgrf_spritegroup.c
|
||||||
SRCS += newgrf_station.c
|
SRCS += newgrf_station.c
|
||||||
SRCS += newgrf_text.c
|
SRCS += newgrf_text.c
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "newgrf_engine.h"
|
#include "newgrf_engine.h"
|
||||||
#include "newgrf_callbacks.h"
|
#include "newgrf_callbacks.h"
|
||||||
#include "newgrf_text.h"
|
#include "newgrf_text.h"
|
||||||
|
#include "newgrf_sound.h"
|
||||||
#include "date.h"
|
#include "date.h"
|
||||||
|
|
||||||
static bool AirportMove(Vehicle *v, const AirportFTAClass *Airport);
|
static bool AirportMove(Vehicle *v, const AirportFTAClass *Airport);
|
||||||
|
@ -858,7 +859,9 @@ static void ServiceAircraft(Vehicle *v)
|
||||||
|
|
||||||
static void PlayAircraftSound(const Vehicle* v)
|
static void PlayAircraftSound(const Vehicle* v)
|
||||||
{
|
{
|
||||||
SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
|
if (!PlayVehicleSound(v, VSE_START)) {
|
||||||
|
SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool UpdateAircraftSpeed(Vehicle *v)
|
static bool UpdateAircraftSpeed(Vehicle *v)
|
||||||
|
@ -1426,7 +1429,9 @@ static void AircraftLand(Vehicle *v)
|
||||||
static void AircraftLandAirplane(Vehicle *v)
|
static void AircraftLandAirplane(Vehicle *v)
|
||||||
{
|
{
|
||||||
AircraftLand(v);
|
AircraftLand(v);
|
||||||
SndPlayVehicleFx(SND_17_SKID_PLANE, v);
|
if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
|
||||||
|
SndPlayVehicleFx(SND_17_SKID_PLANE, v);
|
||||||
|
}
|
||||||
MaybeCrashAirplane(v);
|
MaybeCrashAirplane(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
189
newgrf.c
189
newgrf.c
|
@ -25,6 +25,8 @@
|
||||||
#include "table/sprites.h"
|
#include "table/sprites.h"
|
||||||
#include "date.h"
|
#include "date.h"
|
||||||
#include "currency.h"
|
#include "currency.h"
|
||||||
|
#include "sound.h"
|
||||||
|
#include "newgrf_sound.h"
|
||||||
#include "newgrf_spritegroup.h"
|
#include "newgrf_spritegroup.h"
|
||||||
|
|
||||||
/* TTDPatch extended GRF format codec
|
/* TTDPatch extended GRF format codec
|
||||||
|
@ -58,6 +60,14 @@ static uint32 _ttdpatch_flags[8];
|
||||||
static byte *_preload_sprite = NULL;
|
static byte *_preload_sprite = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum GrfDataType {
|
||||||
|
GDT_SOUND,
|
||||||
|
} GrfDataType;
|
||||||
|
|
||||||
|
static byte _grf_data_blocks;
|
||||||
|
static GrfDataType _grf_data_type;
|
||||||
|
|
||||||
|
|
||||||
typedef enum grfspec_feature {
|
typedef enum grfspec_feature {
|
||||||
GSF_TRAIN,
|
GSF_TRAIN,
|
||||||
GSF_ROAD,
|
GSF_ROAD,
|
||||||
|
@ -1182,6 +1192,67 @@ static bool GlobalVarChangeInfo(uint gvid, int numinfo, int prop, byte **bufp, i
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool SoundEffectChangeInfo(uint sid, int numinfo, int prop, byte **bufp, int len)
|
||||||
|
{
|
||||||
|
byte *buf = *bufp;
|
||||||
|
int i;
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
if (_cur_grffile->sound_offset == 0) {
|
||||||
|
grfmsg(GMS_WARN, "SoundEffectChangeInfo: No effects defined, skipping.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (prop) {
|
||||||
|
case 0x08: /* Relative volume */
|
||||||
|
FOR_EACH_OBJECT {
|
||||||
|
uint sound = sid + i + _cur_grffile->sound_offset - GetNumOriginalSounds();
|
||||||
|
|
||||||
|
if (sound >= GetNumSounds()) {
|
||||||
|
grfmsg(GMS_WARN, "SoundEffectChangeInfo: Sound %d not defined (max %d)", sound, GetNumSounds());
|
||||||
|
} else {
|
||||||
|
GetSound(sound)->volume = grf_load_byte(&buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x09: /* Priority */
|
||||||
|
FOR_EACH_OBJECT {
|
||||||
|
uint sound = sid + i + _cur_grffile->sound_offset - GetNumOriginalSounds();
|
||||||
|
|
||||||
|
if (sound >= GetNumSounds()) {
|
||||||
|
grfmsg(GMS_WARN, "SoundEffectChangeInfo: Sound %d not defined (max %d)", sound, GetNumSounds());
|
||||||
|
} else {
|
||||||
|
GetSound(sound)->priority = grf_load_byte(&buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x0A: /* Override old sound */
|
||||||
|
FOR_EACH_OBJECT {
|
||||||
|
uint sound = sid + i + _cur_grffile->sound_offset - GetNumOriginalSounds();
|
||||||
|
uint orig_sound = grf_load_byte(&buf);
|
||||||
|
|
||||||
|
if (sound >= GetNumSounds() || orig_sound >= GetNumSounds()) {
|
||||||
|
grfmsg(GMS_WARN, "SoundEffectChangeInfo: Sound %d or %d not defined (max %d)", sound, orig_sound, GetNumSounds());
|
||||||
|
} else {
|
||||||
|
FileEntry *newfe = GetSound(sound);
|
||||||
|
FileEntry *oldfe = GetSound(orig_sound);
|
||||||
|
|
||||||
|
/* Literally copy the data of the new sound over the original */
|
||||||
|
memcpy(oldfe, newfe, sizeof(*oldfe));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
*bufp = buf;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Action 0x00 */
|
/* Action 0x00 */
|
||||||
static void FeatureChangeInfo(byte *buf, int len)
|
static void FeatureChangeInfo(byte *buf, int len)
|
||||||
{
|
{
|
||||||
|
@ -1214,7 +1285,7 @@ static void FeatureChangeInfo(byte *buf, int len)
|
||||||
/* GSF_INDUSTRYTILES */NULL,
|
/* GSF_INDUSTRYTILES */NULL,
|
||||||
/* GSF_INDUSTRIES */ NULL,
|
/* GSF_INDUSTRIES */ NULL,
|
||||||
/* GSF_CARGOS */ NULL,
|
/* GSF_CARGOS */ NULL,
|
||||||
/* GSF_SOUNDFX */ NULL,
|
/* GSF_SOUNDFX */ SoundEffectChangeInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
uint8 feature;
|
uint8 feature;
|
||||||
|
@ -2708,6 +2779,113 @@ static void DefineGotoLabel(byte *buf, int len)
|
||||||
grfmsg(GMS_NOTICE, "DefineGotoLabel: GOTO target with label 0x%02X", label->label);
|
grfmsg(GMS_NOTICE, "DefineGotoLabel: GOTO target with label 0x%02X", label->label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Action 0x11 */
|
||||||
|
static void GRFSound(byte *buf, int len)
|
||||||
|
{
|
||||||
|
/* <11> <num>
|
||||||
|
*
|
||||||
|
* W num Number of sound files that follow */
|
||||||
|
|
||||||
|
uint16 num;
|
||||||
|
|
||||||
|
check_length(len, 1, "GRFSound");
|
||||||
|
buf++;
|
||||||
|
num = grf_load_word(&buf);
|
||||||
|
|
||||||
|
_grf_data_blocks = num;
|
||||||
|
_grf_data_type = GDT_SOUND;
|
||||||
|
|
||||||
|
if (_cur_grffile->sound_offset == 0) _cur_grffile->sound_offset = GetNumSounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LoadGRFSound(byte *buf, int len)
|
||||||
|
{
|
||||||
|
byte *buf_start = buf;
|
||||||
|
FileEntry *se;
|
||||||
|
|
||||||
|
/* Allocate a sound entry. This is done even if the data is not loaded
|
||||||
|
* so that the indices used elsewhere are still correct. */
|
||||||
|
se = AllocateFileEntry();
|
||||||
|
|
||||||
|
if (grf_load_dword(&buf) != 'FFIR') {
|
||||||
|
grfmsg(GMS_WARN, "LoadGRFSound: Missing RIFF header");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Size of file -- we ignore this */
|
||||||
|
grf_load_dword(&buf);
|
||||||
|
|
||||||
|
if (grf_load_dword(&buf) != 'EVAW') {
|
||||||
|
grfmsg(GMS_WARN, "LoadGRFSound: Invalid RIFF type");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
uint32 tag = grf_load_dword(&buf);
|
||||||
|
uint32 size = grf_load_dword(&buf);
|
||||||
|
|
||||||
|
switch (tag) {
|
||||||
|
case ' tmf': /* 'fmt ' */
|
||||||
|
/* Audio format, must be 1 (PCM) */
|
||||||
|
if (grf_load_word(&buf) != 1) {
|
||||||
|
grfmsg(GMS_WARN, "LoadGRFSound: Invalid audio format");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
se->channels = grf_load_word(&buf);
|
||||||
|
se->rate = grf_load_dword(&buf);
|
||||||
|
grf_load_dword(&buf);
|
||||||
|
grf_load_word(&buf);
|
||||||
|
se->bits_per_sample = grf_load_word(&buf);
|
||||||
|
|
||||||
|
/* Consume any extra bytes */
|
||||||
|
for (; size > 16; size--) grf_load_byte(&buf);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'atad': /* 'data' */
|
||||||
|
se->file_size = size;
|
||||||
|
se->file_offset = FioGetPos() - (len - (buf - buf_start)) + 1;
|
||||||
|
se->file_offset |= _file_index << 24;
|
||||||
|
|
||||||
|
/* Set default volume and priority */
|
||||||
|
se->volume = 0x80;
|
||||||
|
se->priority = 0;
|
||||||
|
|
||||||
|
grfmsg(GMS_NOTICE, "LoadGRFSound: channels %u, sample rate %u, bits per sample %u, length %u", se->channels, se->rate, se->bits_per_sample, size);
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
se->file_size = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 'Action 0xFF' */
|
||||||
|
static void GRFDataBlock(byte *buf, int len)
|
||||||
|
{
|
||||||
|
byte name_len;
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
if (_grf_data_blocks == 0) {
|
||||||
|
grfmsg(GMS_WARN, "GRFDataBlock: unexpected data block, skipping.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf++;
|
||||||
|
name_len = grf_load_byte(&buf);
|
||||||
|
name = (const char *)buf;
|
||||||
|
buf += name_len + 1;
|
||||||
|
|
||||||
|
grfmsg(GMS_NOTICE, "GRFDataBlock: block name '%s'...", name);
|
||||||
|
|
||||||
|
_grf_data_blocks--;
|
||||||
|
|
||||||
|
switch (_grf_data_type) {
|
||||||
|
case GDT_SOUND: LoadGRFSound(buf, len - name_len - 2); break;
|
||||||
|
default: NOT_REACHED(); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void InitializeGRFSpecial(void)
|
static void InitializeGRFSpecial(void)
|
||||||
{
|
{
|
||||||
_ttdpatch_flags[0] = ((_patches.always_small_airport ? 1 : 0) << 0x0C) // keepsmallairport
|
_ttdpatch_flags[0] = ((_patches.always_small_airport ? 1 : 0) << 0x0C) // keepsmallairport
|
||||||
|
@ -2878,6 +3056,7 @@ static void ResetNewGRFData(void)
|
||||||
_traininfo_vehicle_pitch = 0;
|
_traininfo_vehicle_pitch = 0;
|
||||||
_traininfo_vehicle_width = 29;
|
_traininfo_vehicle_width = 29;
|
||||||
|
|
||||||
|
InitializeSoundPool();
|
||||||
InitializeSpriteGroupPool();
|
InitializeSpriteGroupPool();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2986,7 +3165,7 @@ static void DecodeSpecialSprite(uint num, uint stage)
|
||||||
/* We need a pre-stage to set up GOTO labels of Action 0x10 because the grf
|
/* We need a pre-stage to set up GOTO labels of Action 0x10 because the grf
|
||||||
* is not in memory and scanning the file every time would be too expensive.
|
* is not in memory and scanning the file every time would be too expensive.
|
||||||
* In other stages we skip action 0x10 since it's already dealt with. */
|
* In other stages we skip action 0x10 since it's already dealt with. */
|
||||||
static const uint32 action_mask[] = {0x10000, 0x0000FB40, 0x0000FFFF};
|
static const uint32 action_mask[] = {0x10000, 0x0002FB40, 0x0000FFFF};
|
||||||
|
|
||||||
static const SpecialSpriteHandler handlers[] = {
|
static const SpecialSpriteHandler handlers[] = {
|
||||||
/* 0x00 */ FeatureChangeInfo,
|
/* 0x00 */ FeatureChangeInfo,
|
||||||
|
@ -3006,6 +3185,7 @@ static void DecodeSpecialSprite(uint num, uint stage)
|
||||||
/* 0x0E */ GRFInhibit,
|
/* 0x0E */ GRFInhibit,
|
||||||
/* 0x0F */ NULL, // TODO implement
|
/* 0x0F */ NULL, // TODO implement
|
||||||
/* 0x10 */ DefineGotoLabel,
|
/* 0x10 */ DefineGotoLabel,
|
||||||
|
/* 0x11 */ GRFSound,
|
||||||
};
|
};
|
||||||
|
|
||||||
byte* buf;
|
byte* buf;
|
||||||
|
@ -3029,7 +3209,10 @@ static void DecodeSpecialSprite(uint num, uint stage)
|
||||||
|
|
||||||
action = buf[0];
|
action = buf[0];
|
||||||
|
|
||||||
if (action >= lengthof(handlers)) {
|
if (action == 0xFF) {
|
||||||
|
DEBUG(grf, 7) ("Handling data block in stage %d", stage);
|
||||||
|
GRFDataBlock(buf, num);
|
||||||
|
} else if (action >= lengthof(handlers)) {
|
||||||
DEBUG(grf, 7) ("Skipping unknown action 0x%02X", action);
|
DEBUG(grf, 7) ("Skipping unknown action 0x%02X", action);
|
||||||
} else if (!HASBIT(action_mask[stage], action)) {
|
} else if (!HASBIT(action_mask[stage], action)) {
|
||||||
DEBUG(grf, 7) ("Skipping action 0x%02X in stage %d", action, stage);
|
DEBUG(grf, 7) ("Skipping action 0x%02X in stage %d", action, stage);
|
||||||
|
|
2
newgrf.h
2
newgrf.h
|
@ -39,6 +39,8 @@ typedef struct GRFFile {
|
||||||
int spritegroups_count;
|
int spritegroups_count;
|
||||||
struct SpriteGroup **spritegroups;
|
struct SpriteGroup **spritegroups;
|
||||||
|
|
||||||
|
uint sound_offset;
|
||||||
|
|
||||||
StationSpec **stations;
|
StationSpec **stations;
|
||||||
|
|
||||||
uint32 param[0x80];
|
uint32 param[0x80];
|
||||||
|
|
|
@ -45,6 +45,9 @@ enum CallbackID {
|
||||||
/* Called when the player (or AI) tries to start or stop a vehicle. Mainly
|
/* Called when the player (or AI) tries to start or stop a vehicle. Mainly
|
||||||
* used for preventing a vehicle from leaving the depot. */
|
* used for preventing a vehicle from leaving the depot. */
|
||||||
CBID_VEHICLE_START_STOP_CHECK = 0x31,
|
CBID_VEHICLE_START_STOP_CHECK = 0x31,
|
||||||
|
|
||||||
|
/* Called to play a special sound effect */
|
||||||
|
CBID_VEHICLE_SOUND_EFFECT = 0x33,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -651,7 +651,7 @@ static uint32 VehicleGetVariable(const ResolverObject *object, byte variable, by
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x46: /* Motion counter */
|
case 0x46: /* Motion counter */
|
||||||
return 0;
|
return v->motion_counter;
|
||||||
|
|
||||||
case 0x47: { /* Vehicle cargo info */
|
case 0x47: { /* Vehicle cargo info */
|
||||||
/* Format: ccccwwtt
|
/* Format: ccccwwtt
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#ifndef NEWGRF_ENGINE_H
|
#ifndef NEWGRF_ENGINE_H
|
||||||
#define NEWGRF_ENGINE_H
|
#define NEWGRF_ENGINE_H
|
||||||
|
|
||||||
|
#include "newgrf.h"
|
||||||
#include "direction.h"
|
#include "direction.h"
|
||||||
#include "newgrf_cargo.h"
|
#include "newgrf_cargo.h"
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
/* $Id$ */
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "openttd.h"
|
||||||
|
#include "pool.h"
|
||||||
|
#include "sound.h"
|
||||||
|
#include "engine.h"
|
||||||
|
#include "vehicle.h"
|
||||||
|
#include "newgrf_callbacks.h"
|
||||||
|
#include "newgrf_engine.h"
|
||||||
|
#include "newgrf_sound.h"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SOUND_POOL_BLOCK_SIZE_BITS = 3, /* (1 << 3) == 8 items */
|
||||||
|
SOUND_POOL_MAX_BLOCKS = 1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint _sound_count = 0;
|
||||||
|
static MemoryPool _sound_pool = { "Sound", SOUND_POOL_MAX_BLOCKS, SOUND_POOL_BLOCK_SIZE_BITS, sizeof(FileEntry), NULL, NULL, 0, 0, NULL };
|
||||||
|
|
||||||
|
|
||||||
|
/* Allocate a new FileEntry */
|
||||||
|
FileEntry *AllocateFileEntry(void)
|
||||||
|
{
|
||||||
|
if (_sound_count == _sound_pool.total_items) {
|
||||||
|
if (!AddBlockToPool(&_sound_pool)) return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (FileEntry*)GetItemFromPool(&_sound_pool, _sound_count++);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void InitializeSoundPool(void)
|
||||||
|
{
|
||||||
|
CleanPool(&_sound_pool);
|
||||||
|
_sound_count = 0;
|
||||||
|
|
||||||
|
/* Copy original sound data to the pool */
|
||||||
|
SndCopyToPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FileEntry *GetSound(uint index)
|
||||||
|
{
|
||||||
|
if (index >= _sound_count) return NULL;
|
||||||
|
return (FileEntry*)GetItemFromPool(&_sound_pool, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint GetNumSounds(void)
|
||||||
|
{
|
||||||
|
return _sound_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PlayVehicleSound(const Vehicle *v, VehicleSoundEvent event)
|
||||||
|
{
|
||||||
|
const GRFFile *file = GetEngineGRF(v->engine_type);
|
||||||
|
uint16 callback;
|
||||||
|
|
||||||
|
/* If the engine has no GRF ID associated it can't ever play any new sounds */
|
||||||
|
if (file == NULL) return false;
|
||||||
|
|
||||||
|
/* Check that the vehicle type uses the sound effect callback */
|
||||||
|
if (!HASBIT(EngInfo(v->engine_type)->callbackmask, CBM_SOUND_EFFECT)) return false;
|
||||||
|
|
||||||
|
callback = GetVehicleCallback(CBID_VEHICLE_SOUND_EFFECT, event, 0, v->engine_type, v);
|
||||||
|
if (callback == CALLBACK_FAILED) return false;
|
||||||
|
if (callback >= GetNumOriginalSounds()) callback += file->sound_offset - GetNumOriginalSounds();
|
||||||
|
|
||||||
|
SndPlayVehicleFx(callback, v);
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
/* $Id$ */
|
||||||
|
|
||||||
|
#ifndef NEWGRF_SOUND_H
|
||||||
|
#define NEWGRF_SOUND_H
|
||||||
|
|
||||||
|
typedef enum VehicleSoundEvents {
|
||||||
|
VSE_START = 1,
|
||||||
|
VSE_TUNNEL = 2,
|
||||||
|
VSE_BREAKDOWN = 3,
|
||||||
|
VSE_RUNNING = 4,
|
||||||
|
VSE_TOUCHDOWN = 5,
|
||||||
|
VSE_TRAIN_EFFECT = 6,
|
||||||
|
VSE_RUNNING_16 = 7,
|
||||||
|
VSE_STOPPED_16 = 8,
|
||||||
|
VSE_LOAD_UNLOAD = 9,
|
||||||
|
} VehicleSoundEvent;
|
||||||
|
|
||||||
|
|
||||||
|
FileEntry *AllocateFileEntry(void);
|
||||||
|
void InitializeSoundPool(void);
|
||||||
|
FileEntry *GetSound(uint index);
|
||||||
|
uint GetNumSounds(void);
|
||||||
|
bool PlayVehicleSound(const Vehicle *v, VehicleSoundEvent event);
|
||||||
|
|
||||||
|
#endif /* NEWGRF_SOUND_H */
|
|
@ -303,6 +303,9 @@
|
||||||
<File
|
<File
|
||||||
RelativePath=".\newgrf_engine.c">
|
RelativePath=".\newgrf_engine.c">
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\newgrf_sound.c">
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\newgrf_spritegroup.c">
|
RelativePath=".\newgrf_spritegroup.c">
|
||||||
</File>
|
</File>
|
||||||
|
@ -550,6 +553,9 @@
|
||||||
<File
|
<File
|
||||||
RelativePath=".\newgrf_engine.h">
|
RelativePath=".\newgrf_engine.h">
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\newgrf_sound.h">
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\newgrf_spritegroup.h">
|
RelativePath=".\newgrf_spritegroup.h">
|
||||||
</File>
|
</File>
|
||||||
|
|
|
@ -652,6 +652,10 @@
|
||||||
RelativePath=".\newgrf_engine.c"
|
RelativePath=".\newgrf_engine.c"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\newgrf_sound.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\newgrf_spritegroup.c"
|
RelativePath=".\newgrf_spritegroup.c"
|
||||||
>
|
>
|
||||||
|
@ -1027,6 +1031,10 @@
|
||||||
RelativePath=".\newgrf_engine.h"
|
RelativePath=".\newgrf_engine.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\newgrf_sound.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\newgrf_spritegroup.h"
|
RelativePath=".\newgrf_spritegroup.h"
|
||||||
>
|
>
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "newgrf_callbacks.h"
|
#include "newgrf_callbacks.h"
|
||||||
#include "newgrf_engine.h"
|
#include "newgrf_engine.h"
|
||||||
#include "newgrf_text.h"
|
#include "newgrf_text.h"
|
||||||
|
#include "newgrf_sound.h"
|
||||||
#include "yapf/yapf.h"
|
#include "yapf/yapf.h"
|
||||||
#include "date.h"
|
#include "date.h"
|
||||||
|
|
||||||
|
@ -621,8 +622,10 @@ static void HandleBrokenRoadVeh(Vehicle *v)
|
||||||
InvalidateWindow(WC_VEHICLE_VIEW, v->index);
|
InvalidateWindow(WC_VEHICLE_VIEW, v->index);
|
||||||
InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
|
InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
|
||||||
|
|
||||||
SndPlayVehicleFx((_opt.landscape != LT_CANDY) ?
|
if (!PlayVehicleSound(v, VSE_BREAKDOWN)) {
|
||||||
SND_0F_VEHICLE_BREAKDOWN : SND_35_COMEDY_BREAKDOWN, v);
|
SndPlayVehicleFx((_opt.landscape != LT_CANDY) ?
|
||||||
|
SND_0F_VEHICLE_BREAKDOWN : SND_35_COMEDY_BREAKDOWN, v);
|
||||||
|
}
|
||||||
|
|
||||||
if (!(v->vehstatus & VS_HIDDEN)) {
|
if (!(v->vehstatus & VS_HIDDEN)) {
|
||||||
Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
|
Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
|
||||||
|
@ -760,10 +763,12 @@ static void HandleRoadVehLoading(Vehicle *v)
|
||||||
|
|
||||||
static void StartRoadVehSound(const Vehicle* v)
|
static void StartRoadVehSound(const Vehicle* v)
|
||||||
{
|
{
|
||||||
SoundFx s = RoadVehInfo(v->engine_type)->sfx;
|
if (!PlayVehicleSound(v, VSE_START)) {
|
||||||
if (s == SND_19_BUS_START_PULL_AWAY && (v->tick_counter & 3) == 0)
|
SoundFx s = RoadVehInfo(v->engine_type)->sfx;
|
||||||
s = SND_1A_BUS_START_PULL_AWAY_WITH_HORN;
|
if (s == SND_19_BUS_START_PULL_AWAY && (v->tick_counter & 3) == 0)
|
||||||
SndPlayVehicleFx(s, v);
|
s = SND_1A_BUS_START_PULL_AWAY_WITH_HORN;
|
||||||
|
SndPlayVehicleFx(s, v);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct RoadVehFindData {
|
typedef struct RoadVehFindData {
|
||||||
|
|
11
ship_cmd.c
11
ship_cmd.c
|
@ -25,6 +25,7 @@
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "newgrf_callbacks.h"
|
#include "newgrf_callbacks.h"
|
||||||
#include "newgrf_text.h"
|
#include "newgrf_text.h"
|
||||||
|
#include "newgrf_sound.h"
|
||||||
#include "date.h"
|
#include "date.h"
|
||||||
|
|
||||||
static const uint16 _ship_sprites[] = {0x0E5D, 0x0E55, 0x0E65, 0x0E6D};
|
static const uint16 _ship_sprites[] = {0x0E5D, 0x0E55, 0x0E65, 0x0E6D};
|
||||||
|
@ -173,8 +174,10 @@ static void HandleBrokenShip(Vehicle *v)
|
||||||
InvalidateWindow(WC_VEHICLE_VIEW, v->index);
|
InvalidateWindow(WC_VEHICLE_VIEW, v->index);
|
||||||
InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
|
InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
|
||||||
|
|
||||||
SndPlayVehicleFx((_opt.landscape != LT_CANDY) ?
|
if (!PlayVehicleSound(v, VSE_BREAKDOWN)) {
|
||||||
SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2, v);
|
SndPlayVehicleFx((_opt.landscape != LT_CANDY) ?
|
||||||
|
SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2, v);
|
||||||
|
}
|
||||||
|
|
||||||
if (!(v->vehstatus & VS_HIDDEN)) {
|
if (!(v->vehstatus & VS_HIDDEN)) {
|
||||||
Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
|
Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
|
||||||
|
@ -198,7 +201,9 @@ static void MarkShipDirty(Vehicle *v)
|
||||||
|
|
||||||
static void PlayShipSound(Vehicle *v)
|
static void PlayShipSound(Vehicle *v)
|
||||||
{
|
{
|
||||||
SndPlayVehicleFx(ShipVehInfo(v->engine_type)->sfx, v);
|
if (!PlayVehicleSound(v, VSE_START)) {
|
||||||
|
SndPlayVehicleFx(ShipVehInfo(v->engine_type)->sfx, v);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ProcessShipOrder(Vehicle *v)
|
static void ProcessShipOrder(Vehicle *v)
|
||||||
|
|
40
sound.c
40
sound.c
|
@ -10,14 +10,7 @@
|
||||||
#include "window.h"
|
#include "window.h"
|
||||||
#include "viewport.h"
|
#include "viewport.h"
|
||||||
#include "fileio.h"
|
#include "fileio.h"
|
||||||
|
#include "newgrf_sound.h"
|
||||||
typedef struct FileEntry {
|
|
||||||
uint32 file_offset;
|
|
||||||
uint32 file_size;
|
|
||||||
uint16 rate;
|
|
||||||
uint8 bits_per_sample;
|
|
||||||
uint8 channels;
|
|
||||||
} FileEntry;
|
|
||||||
|
|
||||||
static uint _file_count;
|
static uint _file_count;
|
||||||
static FileEntry *_files;
|
static FileEntry *_files;
|
||||||
|
@ -100,14 +93,19 @@ static void OpenBankFile(const char *filename)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint GetNumOriginalSounds(void)
|
||||||
|
{
|
||||||
|
return _file_count;
|
||||||
|
}
|
||||||
|
|
||||||
static bool SetBankSource(MixerChannel *mc, uint bank)
|
static bool SetBankSource(MixerChannel *mc, uint bank)
|
||||||
{
|
{
|
||||||
const FileEntry *fe;
|
const FileEntry *fe;
|
||||||
int8 *mem;
|
int8 *mem;
|
||||||
uint i;
|
uint i;
|
||||||
|
|
||||||
if (bank >= _file_count) return false;
|
if (bank >= GetNumSounds()) return false;
|
||||||
fe = &_files[bank];
|
fe = GetSound(bank);
|
||||||
|
|
||||||
if (fe->file_size == 0) return false;
|
if (fe->file_size == 0) return false;
|
||||||
|
|
||||||
|
@ -180,6 +178,20 @@ static const byte _sound_idx[] = {
|
||||||
72,
|
72,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void SndCopyToPool(void)
|
||||||
|
{
|
||||||
|
uint i;
|
||||||
|
|
||||||
|
for (i = 0; i < _file_count; i++) {
|
||||||
|
FileEntry *orig = &_files[_sound_idx[i]];
|
||||||
|
FileEntry *fe = AllocateFileEntry();
|
||||||
|
|
||||||
|
memcpy(fe, orig, sizeof(*orig));
|
||||||
|
fe->volume = _sound_base_vol[i];
|
||||||
|
fe->priority = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void SndPlayScreenCoordFx(SoundFx sound, int x, int y)
|
static void SndPlayScreenCoordFx(SoundFx sound, int x, int y)
|
||||||
{
|
{
|
||||||
const Window *w;
|
const Window *w;
|
||||||
|
@ -195,9 +207,9 @@ static void SndPlayScreenCoordFx(SoundFx sound, int x, int y)
|
||||||
int left = (x - vp->virtual_left);
|
int left = (x - vp->virtual_left);
|
||||||
|
|
||||||
StartSound(
|
StartSound(
|
||||||
_sound_idx[sound],
|
sound,
|
||||||
left / (vp->virtual_width / ((PANNING_LEVELS << 1) + 1)) - PANNING_LEVELS,
|
left / (vp->virtual_width / ((PANNING_LEVELS << 1) + 1)) - PANNING_LEVELS,
|
||||||
(_sound_base_vol[sound] * msf.effect_vol * _vol_factor_by_zoom[vp->zoom]) >> 15
|
(GetSound(sound)->volume * msf.effect_vol * _vol_factor_by_zoom[vp->zoom]) >> 15
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -225,8 +237,8 @@ void SndPlayVehicleFx(SoundFx sound, const Vehicle *v)
|
||||||
void SndPlayFx(SoundFx sound)
|
void SndPlayFx(SoundFx sound)
|
||||||
{
|
{
|
||||||
StartSound(
|
StartSound(
|
||||||
_sound_idx[sound],
|
sound,
|
||||||
0,
|
0,
|
||||||
(_sound_base_vol[sound] * msf.effect_vol) >> 7
|
(GetSound(sound)->volume * msf.effect_vol) >> 7
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
12
sound.h
12
sound.h
|
@ -16,7 +16,18 @@ typedef struct MusicFileSettings {
|
||||||
|
|
||||||
VARDEF MusicFileSettings msf;
|
VARDEF MusicFileSettings msf;
|
||||||
|
|
||||||
|
typedef struct FileEntry {
|
||||||
|
uint32 file_offset;
|
||||||
|
uint32 file_size;
|
||||||
|
uint16 rate;
|
||||||
|
uint8 bits_per_sample;
|
||||||
|
uint8 channels;
|
||||||
|
uint8 volume;
|
||||||
|
uint8 priority;
|
||||||
|
} FileEntry;
|
||||||
|
|
||||||
bool SoundInitialize(const char *filename);
|
bool SoundInitialize(const char *filename);
|
||||||
|
uint GetNumOriginalSounds(void);
|
||||||
|
|
||||||
typedef enum SoundFx {
|
typedef enum SoundFx {
|
||||||
SND_02_SPLAT, // 0 == 0x00 !
|
SND_02_SPLAT, // 0 == 0x00 !
|
||||||
|
@ -97,5 +108,6 @@ typedef enum SoundFx {
|
||||||
void SndPlayTileFx(SoundFx sound, TileIndex tile);
|
void SndPlayTileFx(SoundFx sound, TileIndex tile);
|
||||||
void SndPlayVehicleFx(SoundFx sound, const Vehicle *v);
|
void SndPlayVehicleFx(SoundFx sound, const Vehicle *v);
|
||||||
void SndPlayFx(SoundFx sound);
|
void SndPlayFx(SoundFx sound);
|
||||||
|
void SndCopyToPool(void);
|
||||||
|
|
||||||
#endif /* SOUND_H */
|
#endif /* SOUND_H */
|
||||||
|
|
15
train_cmd.c
15
train_cmd.c
|
@ -27,6 +27,7 @@
|
||||||
#include "train.h"
|
#include "train.h"
|
||||||
#include "newgrf_callbacks.h"
|
#include "newgrf_callbacks.h"
|
||||||
#include "newgrf_engine.h"
|
#include "newgrf_engine.h"
|
||||||
|
#include "newgrf_sound.h"
|
||||||
#include "newgrf_text.h"
|
#include "newgrf_text.h"
|
||||||
#include "direction.h"
|
#include "direction.h"
|
||||||
#include "yapf/yapf.h"
|
#include "yapf/yapf.h"
|
||||||
|
@ -2021,6 +2022,7 @@ static const int8 _vehicle_smoke_pos[8] = {
|
||||||
static void HandleLocomotiveSmokeCloud(const Vehicle* v)
|
static void HandleLocomotiveSmokeCloud(const Vehicle* v)
|
||||||
{
|
{
|
||||||
const Vehicle* u;
|
const Vehicle* u;
|
||||||
|
bool sound = false;
|
||||||
|
|
||||||
if (v->vehstatus & VS_TRAIN_SLOWING || v->load_unload_time_rem != 0 || v->cur_speed < 2)
|
if (v->vehstatus & VS_TRAIN_SLOWING || v->load_unload_time_rem != 0 || v->cur_speed < 2)
|
||||||
return;
|
return;
|
||||||
|
@ -2068,6 +2070,7 @@ static void HandleLocomotiveSmokeCloud(const Vehicle* v)
|
||||||
// steam smoke.
|
// steam smoke.
|
||||||
if (GB(v->tick_counter, 0, 4) == 0) {
|
if (GB(v->tick_counter, 0, 4) == 0) {
|
||||||
CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
|
CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
|
||||||
|
sound = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -2075,6 +2078,7 @@ static void HandleLocomotiveSmokeCloud(const Vehicle* v)
|
||||||
// diesel smoke
|
// diesel smoke
|
||||||
if (u->cur_speed <= 40 && CHANCE16(15, 128)) {
|
if (u->cur_speed <= 40 && CHANCE16(15, 128)) {
|
||||||
CreateEffectVehicleRel(v, 0, 0, 10, EV_DIESEL_SMOKE);
|
CreateEffectVehicleRel(v, 0, 0, 10, EV_DIESEL_SMOKE);
|
||||||
|
sound = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -2082,10 +2086,13 @@ static void HandleLocomotiveSmokeCloud(const Vehicle* v)
|
||||||
// blue spark
|
// blue spark
|
||||||
if (GB(v->tick_counter, 0, 2) == 0 && CHANCE16(1, 45)) {
|
if (GB(v->tick_counter, 0, 2) == 0 && CHANCE16(1, 45)) {
|
||||||
CreateEffectVehicleRel(v, 0, 0, 10, EV_ELECTRIC_SPARK);
|
CreateEffectVehicleRel(v, 0, 0, 10, EV_ELECTRIC_SPARK);
|
||||||
|
sound = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while ((v = v->next) != NULL);
|
} while ((v = v->next) != NULL);
|
||||||
|
|
||||||
|
if (sound) PlayVehicleSound(u, VSE_TRAIN_EFFECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void TrainPlayLeaveStationSound(const Vehicle* v)
|
static void TrainPlayLeaveStationSound(const Vehicle* v)
|
||||||
|
@ -2098,6 +2105,8 @@ static void TrainPlayLeaveStationSound(const Vehicle* v)
|
||||||
|
|
||||||
EngineID engtype = v->engine_type;
|
EngineID engtype = v->engine_type;
|
||||||
|
|
||||||
|
if (PlayVehicleSound(v, VSE_START)) return;
|
||||||
|
|
||||||
switch (GetEngine(engtype)->railtype) {
|
switch (GetEngine(engtype)->railtype) {
|
||||||
case RAILTYPE_RAIL:
|
case RAILTYPE_RAIL:
|
||||||
case RAILTYPE_ELECTRIC:
|
case RAILTYPE_ELECTRIC:
|
||||||
|
@ -3262,8 +3271,10 @@ static void HandleBrokenTrain(Vehicle *v)
|
||||||
InvalidateWindow(WC_VEHICLE_VIEW, v->index);
|
InvalidateWindow(WC_VEHICLE_VIEW, v->index);
|
||||||
InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
|
InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
|
||||||
|
|
||||||
SndPlayVehicleFx((_opt.landscape != LT_CANDY) ?
|
if (!PlayVehicleSound(v, VSE_BREAKDOWN)) {
|
||||||
SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2, v);
|
SndPlayVehicleFx((_opt.landscape != LT_CANDY) ?
|
||||||
|
SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2, v);
|
||||||
|
}
|
||||||
|
|
||||||
if (!(v->vehstatus & VS_HIDDEN)) {
|
if (!(v->vehstatus & VS_HIDDEN)) {
|
||||||
Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
|
Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "water_map.h"
|
#include "water_map.h"
|
||||||
#include "yapf/yapf.h"
|
#include "yapf/yapf.h"
|
||||||
#include "date.h"
|
#include "date.h"
|
||||||
|
#include "newgrf_sound.h"
|
||||||
|
|
||||||
#include "table/bridge_land.h"
|
#include "table/bridge_land.h"
|
||||||
|
|
||||||
|
@ -1361,8 +1362,9 @@ static uint32 VehicleEnter_TunnelBridge(Vehicle *v, TileIndex tile, int x, int y
|
||||||
|
|
||||||
if (v->u.rail.track != 0x40 && dir == vdir) {
|
if (v->u.rail.track != 0x40 && dir == vdir) {
|
||||||
if (IsFrontEngine(v) && fc == _tunnel_fractcoord_1[dir]) {
|
if (IsFrontEngine(v) && fc == _tunnel_fractcoord_1[dir]) {
|
||||||
if (v->spritenum < 4)
|
if (!PlayVehicleSound(v, VSE_TUNNEL) && v->spritenum < 4) {
|
||||||
SndPlayVehicleFx(SND_05_TRAIN_THROUGH_TUNNEL, v);
|
SndPlayVehicleFx(SND_05_TRAIN_THROUGH_TUNNEL, v);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (fc == _tunnel_fractcoord_2[dir]) {
|
if (fc == _tunnel_fractcoord_2[dir]) {
|
||||||
|
|
17
vehicle.c
17
vehicle.c
|
@ -33,6 +33,7 @@
|
||||||
#include "yapf/yapf.h"
|
#include "yapf/yapf.h"
|
||||||
#include "date.h"
|
#include "date.h"
|
||||||
#include "newgrf_engine.h"
|
#include "newgrf_engine.h"
|
||||||
|
#include "newgrf_sound.h"
|
||||||
|
|
||||||
#define INVALID_COORD (-0x8000)
|
#define INVALID_COORD (-0x8000)
|
||||||
#define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
|
#define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
|
||||||
|
@ -646,6 +647,22 @@ void CallVehicleTicks(void)
|
||||||
|
|
||||||
FOR_ALL_VEHICLES(v) {
|
FOR_ALL_VEHICLES(v) {
|
||||||
_vehicle_tick_procs[v->type - 0x10](v);
|
_vehicle_tick_procs[v->type - 0x10](v);
|
||||||
|
|
||||||
|
switch (v->type) {
|
||||||
|
case VEH_Train:
|
||||||
|
case VEH_Road:
|
||||||
|
case VEH_Aircraft:
|
||||||
|
case VEH_Ship:
|
||||||
|
if (v->type == VEH_Train && IsTrainWagon(v)) continue;
|
||||||
|
if (v->type == VEH_Aircraft && v->subtype > 0) continue;
|
||||||
|
|
||||||
|
v->motion_counter += (v->direction & 1) ? (v->cur_speed * 3) / 4 : v->cur_speed;
|
||||||
|
/* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
|
||||||
|
if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
|
||||||
|
|
||||||
|
/* Play an alterate running sound every 16 ticks */
|
||||||
|
if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// now we handle all the vehicles that entered a depot this tick
|
// now we handle all the vehicles that entered a depot this tick
|
||||||
|
|
|
@ -174,6 +174,7 @@ struct Vehicle {
|
||||||
byte subspeed; // fractional speed
|
byte subspeed; // fractional speed
|
||||||
byte acceleration; // used by train & aircraft
|
byte acceleration; // used by train & aircraft
|
||||||
byte progress;
|
byte progress;
|
||||||
|
uint32 motion_counter;
|
||||||
|
|
||||||
byte vehstatus; // Status
|
byte vehstatus; // Status
|
||||||
StationID last_station_visited;
|
StationID last_station_visited;
|
||||||
|
|
Loading…
Reference in New Issue